fixes lol
All checks were successful
Build and Push / build-all (push) Successful in 9m12s

This commit is contained in:
doomtube 2026-01-07 02:14:34 -05:00
parent 99151c6692
commit 3676dc46ed
16 changed files with 894 additions and 89 deletions

View file

@ -2,6 +2,71 @@ import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
// Token refresh interval (2 hours - before the 2.5-hour access token expires)
const REFRESH_INTERVAL_MS = 2 * 60 * 60 * 1000;
let refreshInterval = null;
let isRefreshing = false;
// Silent token refresh function
async function refreshAccessToken() {
if (isRefreshing) return false;
isRefreshing = true;
try {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
// Update user data if returned
if (data.user) {
auth.updateUser(data.user);
}
isRefreshing = false;
return true;
} else {
// Refresh failed - session expired
console.log('Token refresh failed - session expired');
isRefreshing = false;
return false;
}
} catch (error) {
console.error('Token refresh error:', error);
isRefreshing = false;
return false;
}
}
// Start automatic token refresh
function startTokenRefresh() {
if (!browser) return;
// Clear any existing interval
if (refreshInterval) {
clearInterval(refreshInterval);
}
// Refresh every 12 minutes
refreshInterval = setInterval(async () => {
const success = await refreshAccessToken();
if (!success) {
// Stop refresh interval and logout
stopTokenRefresh();
auth.logout();
}
}, REFRESH_INTERVAL_MS);
}
// Stop automatic token refresh
function stopTokenRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
function createAuthStore() {
const { subscribe, set, update } = writable({
user: null,
@ -10,25 +75,29 @@ function createAuthStore() {
return {
subscribe,
async init() {
if (!browser) return;
// Use cookie-based auth - no localStorage tokens
try {
const response = await fetch('/api/user/me', {
credentials: 'include' // Send cookies
});
if (response.ok) {
const data = await response.json();
set({ user: data.user, loading: false });
// Start token refresh when authenticated
startTokenRefresh();
} else {
set({ user: null, loading: false });
stopTokenRefresh();
}
} catch (error) {
console.error('Auth init error:', error);
set({ user: null, loading: false });
stopTokenRefresh();
}
},
@ -46,6 +115,8 @@ function createAuthStore() {
// Server sets httpOnly cookie for HTTP requests
// Token is NOT stored in localStorage to prevent XSS attacks
set({ user: data.user, loading: false });
// Start token refresh after successful login
startTokenRefresh();
goto('/');
return { success: true };
}
@ -67,6 +138,8 @@ function createAuthStore() {
// Server sets httpOnly cookie for HTTP requests
// Token is NOT stored in localStorage to prevent XSS attacks
set({ user: data.user, loading: false });
// Start token refresh after successful login
startTokenRefresh();
goto('/');
return { success: true };
}
@ -132,20 +205,26 @@ function createAuthStore() {
},
async logout() {
// Call logout endpoint to clear httpOnly cookie
// Stop token refresh interval
stopTokenRefresh();
// Call logout endpoint to clear httpOnly cookies
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
// Clear token from localStorage
// Clear token from localStorage (legacy cleanup)
if (browser) {
localStorage.removeItem('token');
}
set({ user: null, loading: false });
goto('/login');
}
},
// Export refresh function for use by other modules (e.g., WebSocket)
refreshToken: refreshAccessToken
};
}

View file

@ -184,6 +184,15 @@ function createWatchSyncStore() {
error: data.error || 'Unknown error'
}));
break;
case 'lock_changed':
// Notify components about lock state change
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('playlist-lock-changed', {
detail: { playlistItemId: data.playlistItemId, locked: data.locked }
}));
}
break;
}
}
@ -333,6 +342,15 @@ function createWatchSyncStore() {
});
}
// Send lock update to server (for immediate in-memory state update)
function sendLockUpdate(playlistItemId, locked) {
send({
type: 'lock_update',
playlistItemId: playlistItemId,
locked: locked
});
}
// Check if local player needs to sync
function checkSync(localTime) {
let state;
@ -358,6 +376,7 @@ function createWatchSyncStore() {
requestSync,
checkSync,
reportDuration,
sendLockUpdate,
getExpectedTime: () => {
let state;
subscribe(s => { state = s; })();