fix: align @types/express to v4 to match express runtime
The project uses express@^4.18.3 at runtime but had @types/express@^5.0.6 as type definitions. The v5 types widened ParamsDictionary from string to string | string[], causing 115 type errors across all route handlers. Fix: downgrade @types/express to ^4.17.25 (latest v4), which correctly types req.params as string — matching Express 4 runtime behaviour. Removes the StringParams = Record<string, string> workaround from types.ts and the Request<StringParams> annotations from all 15 route files that were introduced as a workaround for the type mismatch.
This commit is contained in:
44
server/package-lock.json
generated
44
server/package-lock.json
generated
@@ -30,7 +30,7 @@
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/express": "^4.17.25",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/multer": "^2.1.0",
|
||||
"@types/node": "^25.5.0",
|
||||
@@ -516,21 +516,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"version": "4.17.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
|
||||
"integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "^2"
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "^1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
|
||||
"integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
|
||||
"version": "4.19.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz",
|
||||
"integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -558,6 +559,13 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||
@@ -627,13 +635,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
|
||||
"version": "1.15.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
|
||||
"integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "<1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static/node_modules/@types/send": {
|
||||
"version": "0.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
|
||||
"integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/express": "^4.17.25",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/multer": "^2.1.0",
|
||||
"@types/node": "^25.5.0",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dotenv/config';
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import { StringParams } from './types';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import path from 'path';
|
||||
@@ -79,7 +78,7 @@ app.use(express.urlencoded({ extended: true }));
|
||||
app.use('/uploads/avatars', express.static(path.join(__dirname, '../uploads/avatars')));
|
||||
|
||||
// All other uploads require authentication
|
||||
app.get('/uploads/:type/:filename', (req: Request<StringParams>, res: Response) => {
|
||||
app.get('/uploads/:type/:filename', (req: Request, res: Response) => {
|
||||
const { type, filename } = req.params;
|
||||
const allowedTypes = ['covers', 'files', 'photos'];
|
||||
if (!allowedTypes.includes(type)) return res.status(404).send('Not found');
|
||||
|
||||
@@ -4,7 +4,7 @@ import { authenticate } from '../middleware/auth';
|
||||
import { requireTripAccess } from '../middleware/tripAccess';
|
||||
import { broadcast } from '../websocket';
|
||||
import { loadTagsByPlaceIds, loadParticipantsByAssignmentIds, formatAssignmentWithPlace } from '../services/queryHelpers';
|
||||
import { StringParams, AuthRequest, AssignmentRow, DayAssignment, Tag, Participant } from '../types';
|
||||
import { AuthRequest, AssignmentRow, DayAssignment, Tag, Participant } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -75,7 +75,7 @@ function getAssignmentWithPlace(assignmentId: number | bigint) {
|
||||
};
|
||||
}
|
||||
|
||||
router.get('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, dayId } = req.params;
|
||||
|
||||
const day = db.prepare('SELECT id FROM days WHERE id = ? AND trip_id = ?').get(dayId, tripId);
|
||||
@@ -109,7 +109,7 @@ router.get('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAc
|
||||
res.json({ assignments: result });
|
||||
});
|
||||
|
||||
router.post('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, dayId } = req.params;
|
||||
const { place_id, notes } = req.body;
|
||||
|
||||
@@ -131,7 +131,7 @@ router.post('/trips/:tripId/days/:dayId/assignments', authenticate, requireTripA
|
||||
broadcast(tripId, 'assignment:created', { assignment }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/trips/:tripId/days/:dayId/assignments/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/trips/:tripId/days/:dayId/assignments/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, dayId, id } = req.params;
|
||||
|
||||
const assignment = db.prepare(
|
||||
@@ -145,7 +145,7 @@ router.delete('/trips/:tripId/days/:dayId/assignments/:id', authenticate, requir
|
||||
broadcast(tripId, 'assignment:deleted', { assignmentId: Number(id), dayId: Number(dayId) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/trips/:tripId/days/:dayId/assignments/reorder', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/trips/:tripId/days/:dayId/assignments/reorder', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, dayId } = req.params;
|
||||
const { orderedIds } = req.body;
|
||||
|
||||
@@ -167,7 +167,7 @@ router.put('/trips/:tripId/days/:dayId/assignments/reorder', authenticate, requi
|
||||
broadcast(tripId, 'assignment:reordered', { dayId: Number(dayId), orderedIds }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/trips/:tripId/assignments/:id/move', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/trips/:tripId/assignments/:id/move', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
const { new_day_id, order_index } = req.body;
|
||||
|
||||
@@ -190,7 +190,7 @@ router.put('/trips/:tripId/assignments/:id/move', authenticate, requireTripAcces
|
||||
broadcast(tripId, 'assignment:moved', { assignment: updated, oldDayId: Number(oldDayId), newDayId: Number(new_day_id) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.get('/trips/:tripId/assignments/:id/participants', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/trips/:tripId/assignments/:id/participants', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const participants = db.prepare(`
|
||||
@@ -203,7 +203,7 @@ router.get('/trips/:tripId/assignments/:id/participants', authenticate, requireT
|
||||
res.json({ participants });
|
||||
});
|
||||
|
||||
router.put('/trips/:tripId/assignments/:id/time', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/trips/:tripId/assignments/:id/time', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const assignment = db.prepare(`
|
||||
@@ -222,7 +222,7 @@ router.put('/trips/:tripId/assignments/:id/time', authenticate, requireTripAcces
|
||||
broadcast(Number(tripId), 'assignment:updated', { assignment: updated }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/trips/:tripId/assignments/:id/participants', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/trips/:tripId/assignments/:id/participants', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const { user_ids } = req.body;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { StringParams, AuthRequest, Trip, Place } from '../types';
|
||||
import { AuthRequest, Trip, Place } from '../types';
|
||||
|
||||
const router = express.Router();
|
||||
router.use(authenticate);
|
||||
@@ -83,7 +83,7 @@ const CONTINENT_MAP: Record<string, string> = {
|
||||
SE:'Europe',CH:'Europe',TH:'Asia',TR:'Europe',UA:'Europe',AE:'Asia',GB:'Europe',US:'North America',VN:'Asia',NG:'Africa',
|
||||
};
|
||||
|
||||
router.get('/stats', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/stats', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const userId = authReq.user.id;
|
||||
|
||||
@@ -208,7 +208,7 @@ router.get('/stats', (req: Request<StringParams>, res: Response) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/country/:code', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/country/:code', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const userId = authReq.user.id;
|
||||
const code = req.params.code.toUpperCase();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import { StringParams } from '../types';
|
||||
import archiver from 'archiver';
|
||||
import unzipper from 'unzipper';
|
||||
import multer from 'multer';
|
||||
@@ -18,7 +17,7 @@ const MAX_BACKUP_UPLOAD_SIZE = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
const backupAttempts = new Map<string, { count: number; first: number }>();
|
||||
function backupRateLimiter(maxAttempts: number, windowMs: number) {
|
||||
return (req: Request<StringParams>, res: Response, next: NextFunction) => {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
const key = req.ip || 'unknown';
|
||||
const now = Date.now();
|
||||
const record = backupAttempts.get(key);
|
||||
@@ -120,7 +119,7 @@ router.post('/create', backupRateLimiter(3, BACKUP_RATE_WINDOW), async (_req: Re
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/download/:filename', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/download/:filename', (req: Request, res: Response) => {
|
||||
const { filename } = req.params;
|
||||
|
||||
if (!/^backup-[\w\-]+\.zip$/.test(filename)) {
|
||||
@@ -183,7 +182,7 @@ async function restoreFromZip(zipPath: string, res: Response) {
|
||||
}
|
||||
}
|
||||
|
||||
router.post('/restore/:filename', async (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/restore/:filename', async (req: Request, res: Response) => {
|
||||
const { filename } = req.params;
|
||||
if (!/^backup-[\w\-]+\.zip$/.test(filename)) {
|
||||
return res.status(400).json({ error: 'Invalid filename' });
|
||||
@@ -204,7 +203,7 @@ const uploadTmp = multer({
|
||||
limits: { fileSize: MAX_BACKUP_UPLOAD_SIZE },
|
||||
});
|
||||
|
||||
router.post('/upload-restore', uploadTmp.single('backup'), async (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/upload-restore', uploadTmp.single('backup'), async (req: Request, res: Response) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
|
||||
const zipPath = req.file.path;
|
||||
await restoreFromZip(zipPath, res);
|
||||
@@ -244,7 +243,7 @@ function parseAutoBackupBody(body: Record<string, unknown>): {
|
||||
return { enabled, interval, keep_days };
|
||||
}
|
||||
|
||||
router.put('/auto-settings', (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/auto-settings', (req: Request, res: Response) => {
|
||||
try {
|
||||
const settings = parseAutoBackupBody((req.body || {}) as Record<string, unknown>);
|
||||
scheduler.saveSettings(settings);
|
||||
@@ -260,7 +259,7 @@ router.put('/auto-settings', (req: Request<StringParams>, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/:filename', (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:filename', (req: Request, res: Response) => {
|
||||
const { filename } = req.params;
|
||||
|
||||
if (!/^backup-[\w\-]+\.zip$/.test(filename)) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { StringParams, AuthRequest, BudgetItem, BudgetItemMember } from '../types';
|
||||
import { AuthRequest, BudgetItem, BudgetItemMember } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -23,7 +23,7 @@ function avatarUrl(user: { avatar?: string | null }): string | null {
|
||||
return user.avatar ? `/uploads/avatars/${user.avatar}` : null;
|
||||
}
|
||||
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
|
||||
@@ -55,7 +55,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ items });
|
||||
});
|
||||
|
||||
router.get('/summary/per-person', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/summary/per-person', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
if (!canAccessTrip(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -75,7 +75,7 @@ router.get('/summary/per-person', authenticate, (req: Request<StringParams>, res
|
||||
res.json({ summary: summary.map(s => ({ ...s, avatar_url: avatarUrl(s) })) });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { category, name, total_price, persons, days, note } = req.body;
|
||||
@@ -107,7 +107,7 @@ router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
broadcast(tripId, 'budget:created', { item }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { category, name, total_price, persons, days, note, sort_order } = req.body;
|
||||
@@ -145,7 +145,7 @@ router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
broadcast(tripId, 'budget:updated', { item: updated }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id/members', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!canAccessTrip(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -175,7 +175,7 @@ router.put('/:id/members', authenticate, (req: Request<StringParams>, res: Respo
|
||||
broadcast(Number(tripId), 'budget:members-updated', { itemId: Number(id), members, persons: (updated as BudgetItem).persons }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id/members/:userId/paid', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id/members/:userId/paid', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id, userId } = req.params;
|
||||
if (!canAccessTrip(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -195,7 +195,7 @@ router.put('/:id/members/:userId/paid', authenticate, (req: Request<StringParams
|
||||
broadcast(Number(tripId), 'budget:member-paid-updated', { itemId: Number(id), userId: Number(userId), paid: paid ? 1 : 0 }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { validateStringLengths } from '../middleware/validate';
|
||||
import { StringParams, AuthRequest, CollabNote, CollabPoll, CollabMessage, TripFile } from '../types';
|
||||
import { AuthRequest, CollabNote, CollabPoll, CollabMessage, TripFile } from '../types';
|
||||
|
||||
interface ReactionRow {
|
||||
emoji: string;
|
||||
@@ -90,7 +90,7 @@ function formatMessage(msg: CollabMessage, reactions?: { emoji: string; users: {
|
||||
return { ...msg, user_avatar: avatarUrl(msg), avatar_url: avatarUrl(msg), reactions: reactions || [] };
|
||||
}
|
||||
|
||||
router.get('/notes', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/notes', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -106,7 +106,7 @@ router.get('/notes', authenticate, (req: Request<StringParams>, res: Response) =
|
||||
res.json({ notes: notes.map(formatNote) });
|
||||
});
|
||||
|
||||
router.post('/notes', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/notes', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { title, content, category, color, website } = req.body;
|
||||
@@ -127,7 +127,7 @@ router.post('/notes', authenticate, (req: Request<StringParams>, res: Response)
|
||||
broadcast(tripId, 'collab:note:created', { note: formatted }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/notes/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/notes/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { title, content, category, color, pinned, website } = req.body;
|
||||
@@ -165,7 +165,7 @@ router.put('/notes/:id', authenticate, (req: Request<StringParams>, res: Respons
|
||||
broadcast(tripId, 'collab:note:updated', { note: formatted }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/notes/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/notes/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -185,7 +185,7 @@ router.delete('/notes/:id', authenticate, (req: Request<StringParams>, res: Resp
|
||||
broadcast(tripId, 'collab:note:deleted', { noteId: Number(id) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.post('/notes/:id/files', authenticate, noteUpload.single('file'), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/notes/:id/files', authenticate, noteUpload.single('file'), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!verifyTripAccess(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -203,7 +203,7 @@ router.post('/notes/:id/files', authenticate, noteUpload.single('file'), (req: R
|
||||
broadcast(Number(tripId), 'collab:note:updated', { note: formatNote(db.prepare('SELECT n.*, u.username, u.avatar FROM collab_notes n JOIN users u ON n.user_id = u.id WHERE n.id = ?').get(id) as CollabNote) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/notes/:id/files/:fileId', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/notes/:id/files/:fileId', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id, fileId } = req.params;
|
||||
if (!verifyTripAccess(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -254,7 +254,7 @@ function getPollWithVotes(pollId: number | bigint | string) {
|
||||
};
|
||||
}
|
||||
|
||||
router.get('/polls', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/polls', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -267,7 +267,7 @@ router.get('/polls', authenticate, (req: Request<StringParams>, res: Response) =
|
||||
res.json({ polls });
|
||||
});
|
||||
|
||||
router.post('/polls', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/polls', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { question, options, multiple, multiple_choice, deadline } = req.body;
|
||||
@@ -289,7 +289,7 @@ router.post('/polls', authenticate, (req: Request<StringParams>, res: Response)
|
||||
broadcast(tripId, 'collab:poll:created', { poll }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.post('/polls/:id/vote', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/polls/:id/vote', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { option_index } = req.body;
|
||||
@@ -322,7 +322,7 @@ router.post('/polls/:id/vote', authenticate, (req: Request<StringParams>, res: R
|
||||
broadcast(tripId, 'collab:poll:voted', { poll: updatedPoll }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/polls/:id/close', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/polls/:id/close', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -337,7 +337,7 @@ router.put('/polls/:id/close', authenticate, (req: Request<StringParams>, res: R
|
||||
broadcast(tripId, 'collab:poll:closed', { poll: updatedPoll }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/polls/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/polls/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -350,7 +350,7 @@ router.delete('/polls/:id', authenticate, (req: Request<StringParams>, res: Resp
|
||||
broadcast(tripId, 'collab:poll:deleted', { pollId: Number(id) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.get('/messages', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/messages', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { before } = req.query;
|
||||
@@ -390,7 +390,7 @@ router.get('/messages', authenticate, (req: Request<StringParams>, res: Response
|
||||
res.json({ messages: messages.map(m => formatMessage(m, groupReactions(reactionsByMsg[m.id] || []))) });
|
||||
});
|
||||
|
||||
router.post('/messages', authenticate, validateStringLengths({ text: 5000 }), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/messages', authenticate, validateStringLengths({ text: 5000 }), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { text, reply_to } = req.body;
|
||||
@@ -421,7 +421,7 @@ router.post('/messages', authenticate, validateStringLengths({ text: 5000 }), (r
|
||||
broadcast(tripId, 'collab:message:created', { message: formatted }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.post('/messages/:id/react', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/messages/:id/react', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { emoji } = req.body;
|
||||
@@ -443,7 +443,7 @@ router.post('/messages/:id/react', authenticate, (req: Request<StringParams>, re
|
||||
broadcast(Number(tripId), 'collab:message:reacted', { messageId: Number(id), reactions }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/messages/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/messages/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -457,7 +457,7 @@ router.delete('/messages/:id', authenticate, (req: Request<StringParams>, res: R
|
||||
broadcast(tripId, 'collab:message:deleted', { messageId: Number(id), username: message.username || authReq.user.username }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.get('/link-preview', authenticate, async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/link-preview', authenticate, async (req: Request, res: Response) => {
|
||||
const { url } = req.query as { url?: string };
|
||||
if (!url) return res.status(400).json({ error: 'URL is required' });
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { validateStringLengths } from '../middleware/validate';
|
||||
import { StringParams, AuthRequest, DayNote } from '../types';
|
||||
import { AuthRequest, DayNote } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -11,7 +11,7 @@ function verifyAccess(tripId: string | number, userId: number) {
|
||||
return canAccessTrip(tripId, userId);
|
||||
}
|
||||
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, dayId } = req.params;
|
||||
if (!verifyAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -23,7 +23,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ notes });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, dayId } = req.params;
|
||||
if (!verifyAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -43,7 +43,7 @@ router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }),
|
||||
broadcast(tripId, 'dayNote:created', { dayId: Number(dayId), note }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, dayId, id } = req.params;
|
||||
if (!verifyAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -67,7 +67,7 @@ router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 })
|
||||
broadcast(tripId, 'dayNote:updated', { dayId: Number(dayId), note: updated }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, dayId, id } = req.params;
|
||||
if (!verifyAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
@@ -4,7 +4,7 @@ import { authenticate } from '../middleware/auth';
|
||||
import { requireTripAccess } from '../middleware/tripAccess';
|
||||
import { broadcast } from '../websocket';
|
||||
import { loadTagsByPlaceIds, loadParticipantsByAssignmentIds, formatAssignmentWithPlace } from '../services/queryHelpers';
|
||||
import { StringParams, AuthRequest, AssignmentRow, Day, DayNote } from '../types';
|
||||
import { AuthRequest, AssignmentRow, Day, DayNote } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -68,7 +68,7 @@ function getAssignmentsForDay(dayId: number | string) {
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId } = req.params;
|
||||
|
||||
const days = db.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number ASC').all(tripId) as Day[];
|
||||
@@ -125,7 +125,7 @@ router.get('/', authenticate, requireTripAccess, (req: Request<StringParams>, re
|
||||
res.json({ days: daysWithAssignments });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId } = req.params;
|
||||
const { date, notes } = req.body;
|
||||
|
||||
@@ -143,7 +143,7 @@ router.post('/', authenticate, requireTripAccess, (req: Request<StringParams>, r
|
||||
broadcast(tripId, 'day:created', { day: dayResult }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const day = db.prepare('SELECT * FROM days WHERE id = ? AND trip_id = ?').get(id, tripId) as Day | undefined;
|
||||
@@ -160,7 +160,7 @@ router.put('/:id', authenticate, requireTripAccess, (req: Request<StringParams>,
|
||||
broadcast(tripId, 'day:updated', { day: dayWithAssignments }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const day = db.prepare('SELECT * FROM days WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -184,7 +184,7 @@ function getAccommodationWithPlace(id: number | bigint) {
|
||||
`).get(id);
|
||||
}
|
||||
|
||||
accommodationsRouter.get('/', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
accommodationsRouter.get('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId } = req.params;
|
||||
|
||||
const accommodations = db.prepare(`
|
||||
@@ -198,7 +198,7 @@ accommodationsRouter.get('/', authenticate, requireTripAccess, (req: Request<Str
|
||||
res.json({ accommodations });
|
||||
});
|
||||
|
||||
accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId } = req.params;
|
||||
const { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes } = req.body;
|
||||
|
||||
@@ -242,7 +242,7 @@ accommodationsRouter.post('/', authenticate, requireTripAccess, (req: Request<St
|
||||
broadcast(tripId, 'reservation:created', {}, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
accommodationsRouter.put('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
accommodationsRouter.put('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
interface DayAccommodation { id: number; trip_id: number; place_id: number; start_day_id: number; end_day_id: number; check_in: string | null; check_out: string | null; confirmation: string | null; notes: string | null; }
|
||||
@@ -293,7 +293,7 @@ accommodationsRouter.put('/:id', authenticate, requireTripAccess, (req: Request<
|
||||
broadcast(tripId, 'accommodation:updated', { accommodation }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
accommodationsRouter.delete('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
accommodationsRouter.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
const existing = db.prepare('SELECT * FROM day_accommodations WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate, demoUploadBlock } from '../middleware/auth';
|
||||
import { requireTripAccess } from '../middleware/tripAccess';
|
||||
import { broadcast } from '../websocket';
|
||||
import { StringParams, AuthRequest, TripFile } from '../types';
|
||||
import { AuthRequest, TripFile } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -72,7 +72,7 @@ function formatFile(file: TripFile) {
|
||||
}
|
||||
|
||||
// List files (excludes soft-deleted by default)
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const showTrash = req.query.trash === 'true';
|
||||
@@ -86,7 +86,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
});
|
||||
|
||||
// Upload file
|
||||
router.post('/', authenticate, requireTripAccess, demoUploadBlock, upload.single('file'), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, requireTripAccess, demoUploadBlock, upload.single('file'), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { place_id, description, reservation_id } = req.body;
|
||||
@@ -116,7 +116,7 @@ router.post('/', authenticate, requireTripAccess, demoUploadBlock, upload.single
|
||||
});
|
||||
|
||||
// Update file metadata
|
||||
router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { description, place_id, reservation_id } = req.body;
|
||||
@@ -146,7 +146,7 @@ router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
});
|
||||
|
||||
// Toggle starred
|
||||
router.patch('/:id/star', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.patch('/:id/star', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
@@ -165,7 +165,7 @@ router.patch('/:id/star', authenticate, (req: Request<StringParams>, res: Respon
|
||||
});
|
||||
|
||||
// Soft-delete (move to trash)
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
@@ -181,7 +181,7 @@ router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response)
|
||||
});
|
||||
|
||||
// Restore from trash
|
||||
router.post('/:id/restore', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/:id/restore', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
@@ -199,7 +199,7 @@ router.post('/:id/restore', authenticate, (req: Request<StringParams>, res: Resp
|
||||
});
|
||||
|
||||
// Permanently delete from trash
|
||||
router.delete('/:id/permanent', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id/permanent', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
@@ -220,7 +220,7 @@ router.delete('/:id/permanent', authenticate, (req: Request<StringParams>, res:
|
||||
});
|
||||
|
||||
// Empty entire trash
|
||||
router.delete('/trash/empty', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/trash/empty', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import express, { Request, Response } from 'express';
|
||||
import fetch from 'node-fetch';
|
||||
import { db } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { StringParams, AuthRequest } from '../types';
|
||||
import { AuthRequest } from '../types';
|
||||
|
||||
interface NominatimResult {
|
||||
osm_type: string;
|
||||
@@ -247,7 +247,7 @@ async function searchNominatim(query: string, lang?: string) {
|
||||
}));
|
||||
}
|
||||
|
||||
router.post('/search', authenticate, async (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/search', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { query } = req.body;
|
||||
|
||||
@@ -301,7 +301,7 @@ router.post('/search', authenticate, async (req: Request<StringParams>, res: Res
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/details/:placeId', authenticate, async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/details/:placeId', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { placeId } = req.params;
|
||||
|
||||
@@ -372,7 +372,7 @@ router.get('/details/:placeId', authenticate, async (req: Request<StringParams>,
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/place-photo/:placeId', authenticate, async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/place-photo/:placeId', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { placeId } = req.params;
|
||||
|
||||
@@ -453,7 +453,7 @@ router.get('/place-photo/:placeId', authenticate, async (req: Request<StringPara
|
||||
});
|
||||
|
||||
// Reverse geocoding via Nominatim
|
||||
router.get('/reverse', authenticate, async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/reverse', authenticate, async (req: Request, res: Response) => {
|
||||
const { lat, lng, lang } = req.query as { lat: string; lng: string; lang?: string };
|
||||
if (!lat || !lng) return res.status(400).json({ error: 'lat and lng required' });
|
||||
try {
|
||||
|
||||
@@ -2,7 +2,7 @@ import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { StringParams, AuthRequest } from '../types';
|
||||
import { AuthRequest } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -10,7 +10,7 @@ function verifyTripOwnership(tripId: string | number, userId: number) {
|
||||
return canAccessTrip(tripId, userId);
|
||||
}
|
||||
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
|
||||
@@ -24,7 +24,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ items });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { name, category, checked } = req.body;
|
||||
@@ -46,7 +46,7 @@ router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
broadcast(tripId, 'packing:created', { item }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { name, checked, category } = req.body;
|
||||
@@ -76,7 +76,7 @@ router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
broadcast(tripId, 'packing:updated', { item: updated }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
@@ -91,7 +91,7 @@ router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response)
|
||||
broadcast(tripId, 'packing:deleted', { itemId: Number(id) }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/reorder', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/reorder', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { orderedIds } = req.body;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { requireTripAccess } from '../middleware/tripAccess';
|
||||
import { broadcast } from '../websocket';
|
||||
import { loadTagsByPlaceIds } from '../services/queryHelpers';
|
||||
import { validateStringLengths } from '../middleware/validate';
|
||||
import { StringParams, AuthRequest, Place } from '../types';
|
||||
import { AuthRequest, Place } from '../types';
|
||||
|
||||
interface PlaceWithCategory extends Place {
|
||||
category_name: string | null;
|
||||
@@ -21,7 +21,7 @@ interface UnsplashSearchResponse {
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId } = req.params
|
||||
const { search, category, tag } = req.query;
|
||||
|
||||
@@ -72,7 +72,7 @@ router.get('/', authenticate, requireTripAccess, (req: Request<StringParams>, re
|
||||
res.json({ places: placesWithTags });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, requireTripAccess, validateStringLengths({ name: 200, description: 2000, address: 500, notes: 2000 }), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, requireTripAccess, validateStringLengths({ name: 200, description: 2000, address: 500, notes: 2000 }), (req: Request, res: Response) => {
|
||||
const { tripId } = req.params
|
||||
|
||||
const {
|
||||
@@ -112,7 +112,7 @@ router.post('/', authenticate, requireTripAccess, validateStringLengths({ name:
|
||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.get('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params
|
||||
|
||||
const placeCheck = db.prepare('SELECT id FROM places WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -124,7 +124,7 @@ router.get('/:id', authenticate, requireTripAccess, (req: Request<StringParams>,
|
||||
res.json({ place });
|
||||
});
|
||||
|
||||
router.get('/:id/image', authenticate, requireTripAccess, async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/:id/image', authenticate, requireTripAccess, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params
|
||||
|
||||
@@ -165,7 +165,7 @@ router.get('/:id/image', authenticate, requireTripAccess, async (req: Request<St
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, requireTripAccess, validateStringLengths({ name: 200, description: 2000, address: 500, notes: 2000 }), (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, requireTripAccess, validateStringLengths({ name: 200, description: 2000, address: 500, notes: 2000 }), (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params
|
||||
|
||||
const existingPlace = db.prepare('SELECT * FROM places WHERE id = ? AND trip_id = ?').get(id, tripId) as Place | undefined;
|
||||
@@ -237,7 +237,7 @@ router.put('/:id', authenticate, requireTripAccess, validateStringLengths({ name
|
||||
broadcast(tripId, 'place:updated', { place }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, requireTripAccess, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => {
|
||||
const { tripId, id } = req.params
|
||||
|
||||
const place = db.prepare('SELECT id FROM places WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
|
||||
@@ -2,7 +2,7 @@ import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { StringParams, AuthRequest, Reservation } from '../types';
|
||||
import { AuthRequest, Reservation } from '../types';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
@@ -10,7 +10,7 @@ function verifyTripOwnership(tripId: string | number, userId: number) {
|
||||
return canAccessTrip(tripId, userId);
|
||||
}
|
||||
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
|
||||
@@ -32,7 +32,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ reservations });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { title, reservation_time, reservation_end_time, location, confirmation_number, notes, day_id, place_id, assignment_id, status, type, accommodation_id, metadata, create_accommodation } = req.body;
|
||||
@@ -103,7 +103,7 @@ router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
broadcast(tripId, 'reservation:created', { reservation }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { title, reservation_time, reservation_end_time, location, confirmation_number, notes, day_id, place_id, assignment_id, status, type, accommodation_id, metadata, create_accommodation } = req.body;
|
||||
@@ -195,7 +195,7 @@ router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
broadcast(tripId, 'reservation:updated', { reservation: updated }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { db, canAccessTrip, isOwner } from '../db/database';
|
||||
import { authenticate, demoUploadBlock } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { StringParams, AuthRequest, Trip, User } from '../types';
|
||||
import { AuthRequest, Trip, User } from '../types';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -120,7 +120,7 @@ function generateDays(tripId: number | bigint | string, startDate: string | null
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const archived = req.query.archived === '1' ? 1 : 0;
|
||||
const userId = authReq.user.id;
|
||||
@@ -133,7 +133,7 @@ router.get('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ trips });
|
||||
});
|
||||
|
||||
router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { title, description, start_date, end_date, currency } = req.body;
|
||||
if (!title) return res.status(400).json({ error: 'Title is required' });
|
||||
@@ -151,7 +151,7 @@ router.post('/', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
res.status(201).json({ trip });
|
||||
});
|
||||
|
||||
router.get('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const userId = authReq.user.id;
|
||||
const trip = db.prepare(`
|
||||
@@ -163,7 +163,7 @@ router.get('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
res.json({ trip });
|
||||
});
|
||||
|
||||
router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const access = canAccessTrip(req.params.id, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -201,7 +201,7 @@ router.put('/:id', authenticate, (req: Request<StringParams>, res: Response) =>
|
||||
broadcast(req.params.id, 'trip:updated', { trip: updatedTrip }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.post('/:id/cover', authenticate, demoUploadBlock, uploadCover.single('cover'), (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/:id/cover', authenticate, demoUploadBlock, uploadCover.single('cover'), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!isOwner(req.params.id, authReq.user.id))
|
||||
return res.status(403).json({ error: 'Only the owner can change the cover image' });
|
||||
@@ -224,7 +224,7 @@ router.post('/:id/cover', authenticate, demoUploadBlock, uploadCover.single('cov
|
||||
res.json({ cover_image: coverUrl });
|
||||
});
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!isOwner(req.params.id, authReq.user.id))
|
||||
return res.status(403).json({ error: 'Only the owner can delete the trip' });
|
||||
@@ -234,7 +234,7 @@ router.delete('/:id', authenticate, (req: Request<StringParams>, res: Response)
|
||||
broadcast(deletedTripId, 'trip:deleted', { id: deletedTripId }, req.headers['x-socket-id'] as string);
|
||||
});
|
||||
|
||||
router.get('/:id/members', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!canAccessTrip(req.params.id, authReq.user.id))
|
||||
return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -261,7 +261,7 @@ router.get('/:id/members', authenticate, (req: Request<StringParams>, res: Respo
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/:id/members', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!canAccessTrip(req.params.id, authReq.user.id))
|
||||
return res.status(404).json({ error: 'Trip not found' });
|
||||
@@ -287,7 +287,7 @@ router.post('/:id/members', authenticate, (req: Request<StringParams>, res: Resp
|
||||
res.status(201).json({ member: { ...target, role: 'member', avatar_url: target.avatar ? `/uploads/avatars/${target.avatar}` : null } });
|
||||
});
|
||||
|
||||
router.delete('/:id/members/:userId', authenticate, (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/:id/members/:userId', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!canAccessTrip(req.params.id, authReq.user.id))
|
||||
return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { StringParams, AuthRequest } from '../types';
|
||||
import { AuthRequest } from '../types';
|
||||
|
||||
interface VacayPlan {
|
||||
id: number;
|
||||
@@ -100,7 +100,7 @@ function getPlanUsers(planId: number) {
|
||||
return [owner, ...members];
|
||||
}
|
||||
|
||||
router.get('/plan', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/plan', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const plan = getActivePlan(authReq.user.id);
|
||||
const activePlanId = plan.id;
|
||||
@@ -140,7 +140,7 @@ router.get('/plan', (req: Request<StringParams>, res: Response) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.put('/plan', async (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/plan', async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
const { block_weekends, holidays_enabled, holidays_region, company_holidays_enabled, carry_over_enabled } = req.body;
|
||||
@@ -222,7 +222,7 @@ router.put('/plan', async (req: Request<StringParams>, res: Response) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.put('/color', (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/color', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { color, target_user_id } = req.body;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
@@ -239,7 +239,7 @@ router.put('/color', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.post('/invite', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/invite', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { user_id } = req.body;
|
||||
if (!user_id) return res.status(400).json({ error: 'user_id required' });
|
||||
@@ -273,7 +273,7 @@ router.post('/invite', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.post('/invite/accept', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/invite/accept', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { plan_id } = req.body;
|
||||
const invite = db.prepare("SELECT * FROM vacay_plan_members WHERE plan_id = ? AND user_id = ? AND status = 'pending'").get(plan_id, authReq.user.id) as VacayPlanMember | undefined;
|
||||
@@ -318,7 +318,7 @@ router.post('/invite/accept', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.post('/invite/decline', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/invite/decline', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { plan_id } = req.body;
|
||||
db.prepare("DELETE FROM vacay_plan_members WHERE plan_id = ? AND user_id = ? AND status = 'pending'").run(plan_id, authReq.user.id);
|
||||
@@ -326,7 +326,7 @@ router.post('/invite/decline', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.post('/invite/cancel', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/invite/cancel', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { user_id } = req.body;
|
||||
const plan = getActivePlan(authReq.user.id);
|
||||
@@ -340,7 +340,7 @@ router.post('/invite/cancel', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.post('/dissolve', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/dissolve', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const plan = getActivePlan(authReq.user.id);
|
||||
const isOwnerFlag = plan.owner_id === authReq.user.id;
|
||||
@@ -375,7 +375,7 @@ router.post('/dissolve', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.get('/available-users', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/available-users', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
const users = db.prepare(`
|
||||
@@ -391,14 +391,14 @@ router.get('/available-users', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ users });
|
||||
});
|
||||
|
||||
router.get('/years', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/years', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
const years = db.prepare('SELECT year FROM vacay_years WHERE plan_id = ? ORDER BY year').all(planId) as { year: number }[];
|
||||
res.json({ years: years.map(y => y.year) });
|
||||
});
|
||||
|
||||
router.post('/years', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/years', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { year } = req.body;
|
||||
if (!year) return res.status(400).json({ error: 'Year required' });
|
||||
@@ -426,7 +426,7 @@ router.post('/years', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ years: years.map(y => y.year) });
|
||||
});
|
||||
|
||||
router.delete('/years/:year', (req: Request<StringParams>, res: Response) => {
|
||||
router.delete('/years/:year', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const year = parseInt(req.params.year);
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
@@ -438,7 +438,7 @@ router.delete('/years/:year', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ years: years.map(y => y.year) });
|
||||
});
|
||||
|
||||
router.get('/entries/:year', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/entries/:year', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const year = req.params.year;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
@@ -453,7 +453,7 @@ router.get('/entries/:year', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ entries, companyHolidays });
|
||||
});
|
||||
|
||||
router.post('/entries/toggle', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/entries/toggle', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { date, target_user_id } = req.body;
|
||||
if (!date) return res.status(400).json({ error: 'date required' });
|
||||
@@ -479,7 +479,7 @@ router.post('/entries/toggle', (req: Request<StringParams>, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/entries/company-holiday', (req: Request<StringParams>, res: Response) => {
|
||||
router.post('/entries/company-holiday', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { date, note } = req.body;
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
@@ -496,7 +496,7 @@ router.post('/entries/company-holiday', (req: Request<StringParams>, res: Respon
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/stats/:year', (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/stats/:year', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const year = parseInt(req.params.year);
|
||||
const planId = getActivePlanId(authReq.user.id);
|
||||
@@ -532,7 +532,7 @@ router.get('/stats/:year', (req: Request<StringParams>, res: Response) => {
|
||||
res.json({ stats });
|
||||
});
|
||||
|
||||
router.put('/stats/:year', (req: Request<StringParams>, res: Response) => {
|
||||
router.put('/stats/:year', (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const year = parseInt(req.params.year);
|
||||
const { vacation_days, target_user_id } = req.body;
|
||||
@@ -564,7 +564,7 @@ router.get('/holidays/countries', async (_req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/holidays/:year/:country', async (req: Request<StringParams>, res: Response) => {
|
||||
router.get('/holidays/:year/:country', async (req: Request, res: Response) => {
|
||||
const { year, country } = req.params;
|
||||
const cacheKey = `${year}-${country}`;
|
||||
const cached = holidayCache.get(cacheKey);
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { Request } from 'express';
|
||||
|
||||
// Route params from Express 4 are always strings at runtime.
|
||||
// Use this with Request<StringParams> to get string-typed req.params.
|
||||
export type StringParams = Record<string, string>;
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
|
||||
Reference in New Issue
Block a user