feat: auto-subscribe push on login, better push logging

- Auto-subscribe to push on login, register, and session restore
- Guard against nil db in SendChannelNotification
- Log push send count per channel message
- Bump SW cache to v5
This commit is contained in:
2026-07-02 14:34:02 -04:00
parent 6b7dc2addd
commit a593f04d7f
3 changed files with 22 additions and 2 deletions
+8 -1
View File
@@ -185,7 +185,7 @@ func (h *Handler) SendPush(ctx context.Context, userID string, payload map[strin
// SendChannelNotification sends a push notification to all server members (except the author) // SendChannelNotification sends a push notification to all server members (except the author)
// when a new message is posted in a channel. // when a new message is posted in a channel.
func (h *Handler) SendChannelNotification(ctx context.Context, channelID, authorID, channelName, content string) { func (h *Handler) SendChannelNotification(ctx context.Context, channelID, authorID, channelName, content string) {
if h.vapidPub == "" || h.vapidPriv == "" { if h.vapidPub == "" || h.vapidPriv == "" || h.db == nil {
return return
} }
@@ -196,6 +196,8 @@ func (h *Handler) SendChannelNotification(ctx context.Context, channelID, author
return return
} }
h.logger.Info("sending push notifications", "channel", channelName, "author", authorID)
body := content body := content
if len(body) > 200 { if len(body) > 200 {
body = body[:200] + "..." body = body[:200] + "..."
@@ -221,6 +223,7 @@ func (h *Handler) SendChannelNotification(ctx context.Context, channelID, author
} }
defer rows.Close() defer rows.Close()
sent := 0
for rows.Next() { for rows.Next() {
var userID, endpoint, p256dh, auth string var userID, endpoint, p256dh, auth string
if err := rows.Scan(&userID, &endpoint, &p256dh, &auth); err != nil { if err := rows.Scan(&userID, &endpoint, &p256dh, &auth); err != nil {
@@ -250,8 +253,12 @@ func (h *Handler) SendChannelNotification(ctx context.Context, channelID, author
} }
if resp != nil { if resp != nil {
resp.Body.Close() resp.Body.Close()
if resp.StatusCode < 300 {
sent++
} }
} }
} }
h.logger.Info("push notifications sent", "count", sent, "channel", channelName)
}
// GenerateVAPIDKeys is in cmd/keygen. Use github.com/SherClockHolmes/webpush-go directly. // GenerateVAPIDKeys is in cmd/keygen. Use github.com/SherClockHolmes/webpush-go directly.
+1 -1
View File
@@ -1,6 +1,6 @@
// Dumpster PWA Service Worker // Dumpster PWA Service Worker
// Bump CACHE_VERSION on each deploy to force re-cache of hashed assets. // Bump CACHE_VERSION on each deploy to force re-cache of hashed assets.
const CACHE_VERSION = 'dumpster-v4'; const CACHE_VERSION = 'dumpster-v5';
const OFFLINE_URL = '/offline.html'; const OFFLINE_URL = '/offline.html';
// Assets to pre-cache on install (shell files) // Assets to pre-cache on install (shell files)
+13
View File
@@ -71,6 +71,16 @@ interface AuthState {
clearError: () => void; clearError: () => void;
} }
// ponytail: auto-subscribe to push notifications on auth
function autoSubscribePush() {
import("../stores/push.ts").then(({ usePushStore }) => {
const ps = usePushStore.getState();
if (ps.isSupported && Notification.permission !== 'denied' && !ps.isSubscribed) {
ps.subscribe();
}
});
}
export const useAuthStore = create<AuthState>((set) => ({ export const useAuthStore = create<AuthState>((set) => ({
user: null, user: null,
isAuthenticated: false, isAuthenticated: false,
@@ -86,6 +96,7 @@ export const useAuthStore = create<AuthState>((set) => ({
}); });
const user = await api.get<User>("/auth/me"); const user = await api.get<User>("/auth/me");
set({ user, isAuthenticated: true, isLoading: false }); set({ user, isAuthenticated: true, isLoading: false });
autoSubscribePush();
} catch (error) { } catch (error) {
set({ set({
isLoading: false, isLoading: false,
@@ -106,6 +117,7 @@ export const useAuthStore = create<AuthState>((set) => ({
}); });
const user = await api.get<User>("/auth/me"); const user = await api.get<User>("/auth/me");
set({ user, isAuthenticated: true, isLoading: false }); set({ user, isAuthenticated: true, isLoading: false });
autoSubscribePush();
} catch (error) { } catch (error) {
set({ set({
isLoading: false, isLoading: false,
@@ -135,6 +147,7 @@ export const useAuthStore = create<AuthState>((set) => ({
try { try {
const user = await api.get<User>(`/auth/me?t=${Date.now()}`); const user = await api.get<User>(`/auth/me?t=${Date.now()}`);
set({ user, isAuthenticated: true, isLoading: false }); set({ user, isAuthenticated: true, isLoading: false });
autoSubscribePush();
} catch (error) { } catch (error) {
set({ set({
user: null, user: null,