python - 通过 Python 注册 Service Worker 时 serverApplicationKey 无效
问题描述
订阅服务工作者时,我不断收到此错误“未处理的拒绝(InvalidAccessError):无法在'PushManager'上执行'订阅':提供的applicationServerKey无效”。我不知道是什么导致了这个问题。我正在尝试使用 Flask 和 React 从服务器向客户端发出推送通知。我不知道密钥是否错误,没有在服务器中注册 VAPID 密钥,或者其他什么。有人能帮我吗?
路线.py
from database.models import User, Reminder
from database import app, request, bcrypt, db
import json
from flask_login import login_user, logout_user, current_user, login_required
from flask import jsonify
import sys
import jwt
from functools import wraps
import datetime
import datetime
import time
from database import modules
import base64
VAPID_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0GpiI5nprbMjvyXkWgCP43XZDBmuECPl4E/w3f7yJL+kuPEFmbYT75q/mbrmPRimlEfSjPlKGCShPVWjF6Pt9A=="
VAPID_PRIVATE_KEY = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgswSiYtHbjUBg4CHBpBE9vTx6CwCRFlvgQRR5hpjYnZ6hRANCAATQamIjmemtsyO/JeRaAI/jddkMGa4QI+XgT/Dd/vIkv6S48QWZthPvmr+ZuuY9GKaUR9KM+UoYJKE9VaMXo+30"
@app.route("/register", methods= ['POST', 'GET'])
def registerForm():
if request.method == 'POST':
name = request.form["personName"]
number = request.form["phoneNumber"]
email = request.form["personEmail"]
password = request.form["password"]
confirmPassword = request.form["confirmPassword"]
duplicateEmail = User.query.filter_by(email=email).first()
if password != confirmPassword:
return json.dumps({"passwordError": "Passwords Do Not Match! "}), 403
if duplicateEmail is not None:
return json.dumps({"duplicateEmailError": "E-mail Already Exists! Please sign in."}), 409
pw_hash = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(name = name, email = email, password = pw_hash, number = number)
db.session.add(user)
db.session.commit()
return json.dumps({"message": "Successfully Registered"}),200
@app.route("/login", methods = ['POST', 'GET'])
def logInForm():
if request.method == 'POST':
email = request.form['personEmail']
password = request.form['password']
getPerson = User.query.filter_by(email=email).first()
if getPerson != None:
hash_pass = getPerson.password
if getPerson and bcrypt.check_password_hash(hash_pass, password):
login_user(getPerson)
token = jwt.encode({'public_id': getPerson.id, 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=365)}, app.config['SECRET_KEY'] )
return jsonify({'token':token.decode('UTF-8')})
return json.dumps({"errorMessage":"E-mail Does Not Exist or Password Is Incorrect"}), 404
@app.route("/logout", methods = ['POST', 'GET'])
def logout():
return json.dumps({"logOut": True})
def token_required(f):
@wraps(f)
def decorator(*args, **kwargs):
req_data = {}
req_data['headers'] = dict(request.headers)
token = req_data['headers']['Authorization']
if token == "undefined":
return jsonify({'message': 'Token is missing.'}), 404
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = User.query.filter_by(id=data['public_id']).first()
except:
return jsonify({'message': 'token is invalid'}), 401
return f(current_user, *args, **kwargs)
return decorator
@app.route("/reminders", methods =['POST', 'GET'])
@token_required
def createReminders(current_user):
if request.method == 'POST':
form = json.loads(request.data.decode('UTF-8'))
date = form['reminderDate'].split("-")
time = form['reminderTime'].split(":")
dateAndTime = datetime.datetime(int(date[0]), int(date[1]), int(date[2]), int(time[0]), int(time[1]))
text = form['reminderText']
enableMobileNotification = True if form['enableMobileNotification'] is True else False
enableDesktopNotification = True if form['enableDesktopNotification'] is True else False
appointmentDate = datetime.datetime(int(date[0]), int(date[1]), int(date[2]))
getTodayDate = datetime.datetime.today().strftime('%Y-%m-%d').split("-")
todayDate = datetime.datetime(int(getTodayDate[0]), int(getTodayDate[1]), int(getTodayDate[2]))
createReminder = Reminder(
text=text,
date = dateAndTime,
notifyMobile = enableMobileNotification,
notifyDesktop = enableDesktopNotification,
numberOfDaysUntilAppointment = modules.cal_days_diff(appointmentDate, todayDate),
person_id = current_user.id
)
db.session.add(createReminder)
db.session.commit()
return jsonify({"message": "Successfully Updated"}), 200
@app.route("/notification", methods = ['POST'])
@token_required
def setNotifications(current_user):
form = json.loads(request.data.decode('UTF-8'))
subscription = form["subscription"]
createNotif = Reminder(subscription = subscription)
db.session.add(createNotif)
db.session.commit()
message = "Hello From Server"
VAPID_CLAIMS = {
"sub": "my email"
}
try:
modules.send_web_push(subscription, message, VAPID_PRIVATE_KEY, VAPID_CLAIMS)
return jsonify({'success':1})
except Exception as e:
print("error",e)
return jsonify({'failed':str(e)})
提醒列表.jsx
import React, {useState, useRef} from "react";
import {Form, Button,Popover, OverlayTrigger} from "react-bootstrap";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons'
function ReminderLists(props) {
const [isChecked, setChecked] = useState(false);
const [changeInput, setChangeInput] = useState(props.text_value);
const [storeFormData, setFormData] = useState({
reminderText: "",
reminderDate: "",
reminderTime: "",
enableMobileNotification: false,
enableDesktopNotification:false,
})
const publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0GpiI5nprbMjvyXkWgCP43XZDBmuECPl4E/w3f7yJL+kuPEFmbYT75q/mbrmPRimlEfSjPlKGCShPVWjF6Pt9A=="
const header = {
'Content-Type': 'application/json',
'Authorization': props.token,
}
function check(e) {
const id_to_be_deleted = e.target.attributes.getNamedItem("unique_Key").value;
if(!isChecked) {
setChecked(true);
props.addIds(isChecked, id_to_be_deleted);
}
else {
setChecked(false);
props.addIds(isChecked, id_to_be_deleted)
}
}
function setChange(e) {
setChangeInput(e.target.value);
props.local_storage(e);
storeFormData.reminderText=e.target.value
}
function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function handleSubmit(e) {
console.log("Token in reminder list " + props.token)
e.preventDefault();
fetch('http://127.0.0.1:5000/reminders', {
method: 'POST',
headers:header,
body: JSON.stringify(storeFormData)
}).then(res=>res.json()).then(res=> {
// console.log("Local storage is set")
console.log(res.message);
notify()
})
async function notify() {
if ('serviceWorker' in navigator && 'PushManager' in window) {
navigator.serviceWorker.register("/sw.js").then(serviceWorkerRegistration => {
console.log('Service worker was registered.');
console.log({serviceWorkerRegistration});
}).catch(error => {
console.log('An error occurred while registering the service worker.');
console.log(error);
});
} else {
console.log('Browser does not support service workers or push messages.');
return;
}
const result = await Notification.requestPermission();
console.log("Permission " + result)
if (result === 'denied') {
console.log('The user explicitly denied the permission request.');
return;
}
if (result === 'granted') {
console.log('The user accepted the permission request.');
const registration = await navigator.serviceWorker.getRegistration();
const subscribed = await registration.pushManager.getSubscription();
if (subscribed) {
console.log('User is already subscribed.');
return;
}
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(publicKey)
});
fetch("http://127.0.0.1:5000/notification", {
method: "POST",
headers:header,
body: JSON.stringify({"subscription":subscription})
})
}
}
}
return (
<Form>
<fieldset>
<div className = "input-group mb-3">
<div className="input-group-prepend">
<div className="input-group-text">
<input unique_Key = {props.id} onInput = {e=> e.target.checked = isChecked} onClick={e=> check(e) } checked = {props.checked} id = "check-item" type="checkbox" aria-label="Checkbox for following text input"/>
</div>
</div>
<textarea id = {props.id} onChange = {e=>setChange(e)} value = {changeInput} class="form-control" rows="1" name = "reminderText" ></textarea>
<input type="date" id="start" name="reminderDate" onChange = {(e)=> storeFormData.reminderDate=e.target.value}></input>
<input type="time" id="appt" name="reminderTime" min="09:00" max="18:00" required onChange = {(e)=> storeFormData.reminderTime=e.target.value}></input>
<OverlayTrigger
trigger="click"
key={'bottom'}
placement={'bottom'}
overlay={
<Popover id={`popover-positioned-${'bottom'}`}>
<Popover.Title as="h3">{`Notification Settings`}</Popover.Title>
<Popover.Content>
<div>
<input type="checkbox" id= "mobile" name="enableMobileNotification" value= "mobile"
onChange = {(e)=> storeFormData.enableMobileNotification= !storeFormData.enableMobileNotification}>
</input>
<label className = "padding-left" for="mobile">Enable Mobile</label>
</div>
<div>
<input type="checkbox" id="desktop" name="enableDesktopNotification" value="desktop"
onChange = {(e)=> storeFormData.enableDesktopNotification= !storeFormData.enableDesktopNotification}
>
</input>
<label className = "padding-left" for="desktop">Enable Desktop</label>
</div>
<Button variant="primary" size="sm" type="submit" onClick = {handleSubmit}>Submit</Button>
</Popover.Content>
</Popover>
}>
<Button className = "black app-btns " id="Popover1" type="button">
<FontAwesomeIcon icon = {faInfoCircle} size = "2x" />
</Button>
</OverlayTrigger>
</div>
</fieldset>
</Form>
)
}
export {ReminderLists};
解决方案
推荐阅读
- r - 如何根据分类变量在我的数据框中创建数据组(因子变量)#R
- c# - 将多个元素添加到列表的单行
- php - 关于 Woocommerce 产品插入挂钩
- node.js - OctoKit NodeJS 获取用户/组织中所有 repos 的所有提交
- php - Laravel - 防止多次登录相同的凭据
- sql - sql update INNER JOIN中关键字'INNER'附近的语法不正确
- ruby - RSpec:在示例中从块中访问“let”定义
- sql - 如何在 Postgres 中使用动态键从 JSON 中获取价值
- 3d - 点在三角形内
- excel - VBA:单元格颜色的 elseIf 条件不起作用