@@ -18,10 +18,13 @@ async function req<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|||||||
const res = await fetch(`${BASE}${path}`, { ...options, headers });
|
const res = await fetch(`${BASE}${path}`, { ...options, headers });
|
||||||
|
|
||||||
if (res.status === 401) {
|
if (res.status === 401) {
|
||||||
// Token expired — clear storage and reload to login
|
const hadToken = Boolean(localStorage.getItem(TOKEN_KEY));
|
||||||
localStorage.removeItem(TOKEN_KEY);
|
localStorage.removeItem(TOKEN_KEY);
|
||||||
localStorage.removeItem('codedump_user');
|
localStorage.removeItem('codedump_user');
|
||||||
window.location.href = '/login';
|
// Only force-redirect if the user had an active session that expired.
|
||||||
|
// Without this guard, unauthenticated requests (e.g. settings fetch on
|
||||||
|
// the login page) trigger a redirect loop: 401 → /login → fetch → 401 → ...
|
||||||
|
if (hadToken) window.location.href = '/login';
|
||||||
throw new Error('Session expired');
|
throw new Error('Session expired');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ app.use('/api/uploads', express.static(UPLOAD_PATH));
|
|||||||
app.use('/api/projects', requireAuth, projectsRouter);
|
app.use('/api/projects', requireAuth, projectsRouter);
|
||||||
app.use('/api/tools', requireAuth, toolsRouter);
|
app.use('/api/tools', requireAuth, toolsRouter);
|
||||||
app.use('/api/uploads', requireAuth, uploadsRouter); // handles POST + DELETE only
|
app.use('/api/uploads', requireAuth, uploadsRouter); // handles POST + DELETE only
|
||||||
app.use('/api/settings', requireAuth, settingsRouter);
|
app.use('/api/settings', settingsRouter); // GET is public (branding on login page); PUT/POST require admin (per-method in router)
|
||||||
app.use('/api/users', usersRouter); // requireAdmin applied inside router
|
app.use('/api/users', usersRouter); // requireAdmin applied inside router
|
||||||
|
|
||||||
// Serve built React client in production
|
// Serve built React client in production
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import multer from 'multer';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import db, { UPLOAD_PATH } from '../db/schema';
|
import db, { UPLOAD_PATH } from '../db/schema';
|
||||||
|
import { requireAdmin } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ router.get('/', (_req: Request, res: Response) => {
|
|||||||
res.json(settings);
|
res.json(settings);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/', (req: Request, res: Response) => {
|
router.put('/', requireAdmin, (req: Request, res: Response) => {
|
||||||
const allowed = ['app_title', 'logo_url', 'accent_color', 'company_name'];
|
const allowed = ['app_title', 'logo_url', 'accent_color', 'company_name'];
|
||||||
const upsert = db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)');
|
const upsert = db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)');
|
||||||
const update = db.transaction((body: Record<string, any>) => {
|
const update = db.transaction((body: Record<string, any>) => {
|
||||||
@@ -45,7 +46,7 @@ router.put('/', (req: Request, res: Response) => {
|
|||||||
res.json(settings);
|
res.json(settings);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/logo', logoUpload.single('logo'), (req: Request, res: Response) => {
|
router.post('/logo', requireAdmin, logoUpload.single('logo'), (req: Request, res: Response) => {
|
||||||
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
|
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
|
||||||
const url = `/api/uploads/${req.file.filename}`;
|
const url = `/api/uploads/${req.file.filename}`;
|
||||||
db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run('logo_url', JSON.stringify(url));
|
db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run('logo_url', JSON.stringify(url));
|
||||||
|
|||||||
Reference in New Issue
Block a user