Finish Phase 6: Push frontend, WebAuthn frontend, OpenAPI/Swagger
Frontend: - stores/push.ts: fixed VAPID key endpoint, added checkSubscription, proper subscription format - UserSettings.tsx: added [ENABLE PUSH] toggle and [REGISTER PASSKEY] button - LoginForm.tsx: added [SIGN IN WITH PASSKEY] button Backend: - cmd/server/main.go: added Swagger annotations, /docs/* route - docs/: generated swagger.json, swagger.yaml, docs.go - Makefile: added docs target for swag init
This commit is contained in:
+27
-3
@@ -8,6 +8,7 @@ interface PushState {
|
||||
subscribe: () => Promise<void>;
|
||||
unsubscribe: () => Promise<void>;
|
||||
requestPermission: () => Promise<NotificationPermission>;
|
||||
checkSubscription: () => Promise<void>;
|
||||
}
|
||||
|
||||
function urlBase64ToUint8Array(base64String: string): BufferSource {
|
||||
@@ -24,7 +25,7 @@ function urlBase64ToUint8Array(base64String: string): BufferSource {
|
||||
export const usePushStore = create<PushState>((set, get) => ({
|
||||
isSupported: 'serviceWorker' in navigator && 'PushManager' in window,
|
||||
isSubscribed: false,
|
||||
permission: 'default',
|
||||
permission: typeof Notification !== 'undefined' ? Notification.permission : 'default',
|
||||
|
||||
requestPermission: async () => {
|
||||
if (!get().isSupported) return 'denied';
|
||||
@@ -33,16 +34,31 @@ export const usePushStore = create<PushState>((set, get) => ({
|
||||
return permission;
|
||||
},
|
||||
|
||||
checkSubscription: async () => {
|
||||
if (!get().isSupported) return;
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const subscription = await registration.pushManager.getSubscription();
|
||||
set({ isSubscribed: !!subscription });
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
|
||||
subscribe: async () => {
|
||||
if (!get().isSupported) return;
|
||||
|
||||
try {
|
||||
// Request notification permission first
|
||||
const permission = await get().requestPermission();
|
||||
if (permission !== 'granted') return;
|
||||
|
||||
// Register service worker
|
||||
const registration = await navigator.serviceWorker.register('/sw.js');
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
// Get VAPID public key from server
|
||||
const { publicKey } = await api.get<{ publicKey: string }>('/push/vapid-key');
|
||||
const { publicKey } = await api.get<{ publicKey: string }>('/push/vapid-public-key');
|
||||
if (!publicKey) return;
|
||||
|
||||
// Subscribe to push
|
||||
@@ -51,8 +67,16 @@ export const usePushStore = create<PushState>((set, get) => ({
|
||||
applicationServerKey: urlBase64ToUint8Array(publicKey),
|
||||
});
|
||||
|
||||
const sub = subscription.toJSON();
|
||||
const keys = sub.keys as { p256dh: string; auth: string };
|
||||
|
||||
// Send subscription to server
|
||||
await api.post('/push/subscribe', subscription.toJSON());
|
||||
await api.post('/push/subscribe', {
|
||||
endpoint: sub.endpoint!,
|
||||
p256dh: keys.p256dh,
|
||||
auth: keys.auth,
|
||||
});
|
||||
|
||||
set({ isSubscribed: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to subscribe to push:', error);
|
||||
|
||||
Reference in New Issue
Block a user