diff --git a/backend/package.json b/backend/package.json
index caf76a6..b674ec4 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-backend",
- "version": "0.12.24",
+ "version": "0.12.25",
"description": "RosterChirp backend server",
"main": "src/index.js",
"scripts": {
diff --git a/backend/src/routes/push.js b/backend/src/routes/push.js
index f918718..abaa4b2 100644
--- a/backend/src/routes/push.js
+++ b/backend/src/routes/push.js
@@ -97,7 +97,7 @@ router.get('/firebase-config', (req, res) => {
const appId = process.env.FIREBASE_APP_ID;
const vapidKey = process.env.FIREBASE_VAPID_KEY;
- if (!apiKey || !projectId || !messagingSenderId || !appId) {
+ if (!apiKey || !projectId || !messagingSenderId || !appId || !vapidKey) {
return res.status(503).json({ error: 'FCM not configured' });
}
res.json({ apiKey, projectId, messagingSenderId, appId, vapidKey });
@@ -220,7 +220,7 @@ router.get('/debug', authMiddleware, async (req, res) => {
WHERE ps.fcm_token IS NOT NULL
ORDER BY u.name, ps.device
`);
- const fcmConfigured = !!(process.env.FIREBASE_API_KEY && process.env.FIREBASE_SERVICE_ACCOUNT);
+ const fcmConfigured = !!(process.env.FIREBASE_API_KEY && process.env.FIREBASE_SERVICE_ACCOUNT && process.env.FIREBASE_VAPID_KEY);
const firebaseAdminReady = !!getMessaging();
res.json({ subscriptions: subs, fcmConfigured, firebaseAdminReady });
} catch (e) { res.status(500).json({ error: e.message }); }
diff --git a/build.sh b/build.sh
index 46b9e9a..4f14809 100644
--- a/build.sh
+++ b/build.sh
@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
-VERSION="${1:-0.12.24}"
+VERSION="${1:-0.12.25}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="rosterchirp"
diff --git a/frontend/package.json b/frontend/package.json
index c583eec..edd9fa5 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-frontend",
- "version": "0.12.24",
+ "version": "0.12.25",
"private": true,
"scripts": {
"dev": "vite",
diff --git a/frontend/src/components/SettingsModal.jsx b/frontend/src/components/SettingsModal.jsx
index 218fef8..4970387 100644
--- a/frontend/src/components/SettingsModal.jsx
+++ b/frontend/src/components/SettingsModal.jsx
@@ -211,7 +211,8 @@ function PushDebugTab() {
const [testing, setTesting] = useState(false);
const permission = (typeof Notification !== 'undefined') ? Notification.permission : 'unsupported';
- const cachedToken = localStorage.getItem('rc_fcm_token');
+ const [cachedToken, setCachedToken] = useState(localStorage.getItem('rc_fcm_token'));
+ const [lastError, setLastError] = useState(localStorage.getItem('rc_fcm_error'));
const load = async () => {
setLoading(true);
@@ -245,9 +246,21 @@ function PushDebugTab() {
const clearToken = () => {
localStorage.removeItem('rc_fcm_token');
+ localStorage.removeItem('rc_fcm_error');
+ setCachedToken(null);
+ setLastError(null);
toast('Cached token cleared — reload to re-register with server', 'info');
};
+ const reregister = () => {
+ localStorage.removeItem('rc_fcm_token');
+ localStorage.removeItem('rc_fcm_error');
+ setCachedToken(null);
+ setLastError(null);
+ window.dispatchEvent(new CustomEvent('rosterchirp:push-init'));
+ toast('Re-registering push subscription…', 'info');
+ };
+
const box = { background: 'var(--surface-variant)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', padding: '12px 14px', marginBottom: 14 };
const sectionLabel = { fontSize: 11, fontWeight: 600, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 8 };
@@ -263,8 +276,12 @@ function PushDebugTab() {