refactor: dedupe database requests
This commit is contained in:
@@ -128,9 +128,4 @@ function isOwner(tripId: number | string, userId: number): boolean {
|
||||
return !!_db!.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId);
|
||||
}
|
||||
|
||||
function getTripOwnerId(tripId: number | string): number | undefined {
|
||||
const row = _db!.prepare('SELECT user_id FROM trips WHERE id = ?').get(tripId) as { user_id: number } | undefined;
|
||||
return row?.user_id;
|
||||
}
|
||||
|
||||
export { db, closeDb, reinitialize, getPlaceWithTags, canAccessTrip, isOwner, getTripOwnerId };
|
||||
export { db, closeDb, reinitialize, getPlaceWithTags, canAccessTrip, isOwner };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { checkPermission } from '../services/permissions';
|
||||
@@ -84,9 +84,7 @@ router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('budget_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('budget_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
if (!name) return res.status(400).json({ error: 'Name is required' });
|
||||
@@ -121,9 +119,7 @@ router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('budget_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('budget_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const item = db.prepare('SELECT * FROM budget_items WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -159,11 +155,10 @@ router.put('/:id', authenticate, (req: Request, 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' });
|
||||
const access = canAccessTrip(Number(tripId), authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('budget_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('budget_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const item = db.prepare('SELECT * FROM budget_items WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -194,11 +189,10 @@ router.put('/:id/members', authenticate, (req: Request, 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' });
|
||||
const access = canAccessTrip(Number(tripId), authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('budget_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('budget_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const { paid } = req.body;
|
||||
@@ -294,9 +288,7 @@ router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('budget_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('budget_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const item = db.prepare('SELECT id FROM budget_items WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
|
||||
@@ -3,7 +3,7 @@ import multer from 'multer';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { validateStringLengths } from '../middleware/validate';
|
||||
@@ -112,10 +112,9 @@ 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;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!title) return res.status(400).json({ error: 'Title is required' });
|
||||
|
||||
@@ -142,10 +141,9 @@ 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;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const existing = db.prepare('SELECT * FROM collab_notes WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -183,10 +181,9 @@ router.put('/notes/:id', authenticate, (req: Request, 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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const existing = db.prepare('SELECT id FROM collab_notes WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -207,10 +204,9 @@ router.delete('/notes/:id', authenticate, (req: Request, 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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(Number(tripId), authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
|
||||
|
||||
@@ -229,10 +225,9 @@ router.post('/notes/:id/files', authenticate, noteUpload.single('file'), (req: R
|
||||
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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(Number(tripId), authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const file = db.prepare('SELECT * FROM trip_files WHERE id = ? AND note_id = ?').get(fileId, id) as TripFile | undefined;
|
||||
@@ -298,10 +293,9 @@ 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;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!question) return res.status(400).json({ error: 'Question is required' });
|
||||
if (!Array.isArray(options) || options.length < 2) {
|
||||
@@ -324,10 +318,9 @@ 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;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const poll = db.prepare('SELECT * FROM collab_polls WHERE id = ? AND trip_id = ?').get(id, tripId) as CollabPoll | undefined;
|
||||
@@ -360,10 +353,9 @@ router.post('/polls/:id/vote', authenticate, (req: Request, 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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const poll = db.prepare('SELECT * FROM collab_polls WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -379,10 +371,9 @@ router.put('/polls/:id/close', authenticate, (req: Request, 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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const poll = db.prepare('SELECT id FROM collab_polls WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -437,10 +428,9 @@ router.post('/messages', authenticate, validateStringLengths({ text: 5000 }), (r
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
const { text, reply_to } = req.body;
|
||||
if (!verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!text || !text.trim()) return res.status(400).json({ error: 'Message text is required' });
|
||||
|
||||
@@ -479,10 +469,9 @@ router.post('/messages/:id/react', authenticate, (req: Request, res: Response) =
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId, id } = req.params;
|
||||
const { emoji } = req.body;
|
||||
if (!verifyTripAccess(Number(tripId), authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(Number(tripId), authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!emoji) return res.status(400).json({ error: 'Emoji is required' });
|
||||
|
||||
@@ -504,10 +493,9 @@ router.post('/messages/:id/react', authenticate, (req: Request, 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' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyTripAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('collab_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const message = db.prepare('SELECT * FROM collab_messages WHERE id = ? AND trip_id = ?').get(id, tripId) as CollabMessage | undefined;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { validateStringLengths } from '../middleware/validate';
|
||||
@@ -27,11 +27,9 @@ router.get('/', authenticate, (req: Request, 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' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const day = db.prepare('SELECT id FROM days WHERE id = ? AND trip_id = ?').get(dayId, tripId);
|
||||
@@ -52,11 +50,9 @@ router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }),
|
||||
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' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const note = db.prepare('SELECT * FROM day_notes WHERE id = ? AND day_id = ? AND trip_id = ?').get(id, dayId, tripId) as DayNote | undefined;
|
||||
@@ -81,11 +77,9 @@ router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 })
|
||||
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' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = verifyAccess(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const note = db.prepare('SELECT id FROM day_notes WHERE id = ? AND day_id = ? AND trip_id = ?').get(id, dayId, tripId);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { checkPermission } from '../services/permissions';
|
||||
@@ -34,9 +34,7 @@ router.post('/import', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
if (!Array.isArray(items) || items.length === 0) return res.status(400).json({ error: 'items must be a non-empty array' });
|
||||
@@ -85,9 +83,7 @@ router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
if (!name) return res.status(400).json({ error: 'Item name is required' });
|
||||
@@ -112,9 +108,7 @@ router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const item = db.prepare('SELECT * FROM packing_items WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -152,9 +146,7 @@ router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const item = db.prepare('SELECT id FROM packing_items WHERE id = ? AND trip_id = ?').get(id, tripId);
|
||||
@@ -182,9 +174,7 @@ router.post('/bags', authenticate, (req: Request, res: Response) => {
|
||||
const { name, color } = req.body;
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
if (!name?.trim()) return res.status(400).json({ error: 'Name is required' });
|
||||
const maxOrder = db.prepare('SELECT MAX(sort_order) as max FROM packing_bags WHERE trip_id = ?').get(tripId) as { max: number | null };
|
||||
@@ -200,9 +190,7 @@ router.put('/bags/:bagId', authenticate, (req: Request, res: Response) => {
|
||||
const { name, color, weight_limit_grams } = req.body;
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
const bag = db.prepare('SELECT * FROM packing_bags WHERE id = ? AND trip_id = ?').get(bagId, tripId);
|
||||
if (!bag) return res.status(404).json({ error: 'Bag not found' });
|
||||
@@ -217,9 +205,7 @@ router.delete('/bags/:bagId', authenticate, (req: Request, res: Response) => {
|
||||
const { tripId, bagId } = req.params;
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
const bag = db.prepare('SELECT * FROM packing_bags WHERE id = ? AND trip_id = ?').get(bagId, tripId);
|
||||
if (!bag) return res.status(404).json({ error: 'Bag not found' });
|
||||
@@ -237,9 +223,7 @@ router.post('/apply-template/:templateId', authenticate, (req: Request, res: Res
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const templateItems = db.prepare(`
|
||||
@@ -299,9 +283,7 @@ router.put('/category-assignees/:categoryName', authenticate, (req: Request, res
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const cat = decodeURIComponent(categoryName);
|
||||
@@ -343,9 +325,7 @@ router.put('/reorder', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('packing_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('packing_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const update = db.prepare('UPDATE packing_items SET sort_order = ? WHERE id = ? AND trip_id = ?');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { checkPermission } from '../services/permissions';
|
||||
@@ -41,9 +41,7 @@ router.post('/', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
if (!title) return res.status(400).json({ error: 'Title is required' });
|
||||
@@ -124,9 +122,7 @@ router.put('/positions', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
if (!Array.isArray(positions)) return res.status(400).json({ error: 'positions must be an array' });
|
||||
@@ -151,9 +147,7 @@ router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const reservation = db.prepare('SELECT * FROM reservations WHERE id = ? AND trip_id = ?').get(id, tripId) as Reservation | undefined;
|
||||
@@ -252,9 +246,7 @@ router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const trip = verifyTripOwnership(tripId, authReq.user.id);
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
if (!checkPermission('reservation_edit', authReq.user.role, trip.user_id, authReq.user.id, trip.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const reservation = db.prepare('SELECT id, title, type, accommodation_id FROM reservations WHERE id = ? AND trip_id = ?').get(id, tripId) as { id: number; title: string; type: string; accommodation_id: number | null } | undefined;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { checkPermission } from '../services/permissions';
|
||||
import { AuthRequest } from '../types';
|
||||
@@ -12,10 +12,9 @@ const router = express.Router();
|
||||
router.post('/trips/:tripId/share-link', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
if (!canAccessTrip(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('share_manage', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = canAccessTrip(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('share_manage', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
const { share_map = true, share_bookings = true, share_packing = false, share_budget = false, share_collab = false } = req.body || {};
|
||||
@@ -49,10 +48,9 @@ router.get('/trips/:tripId/share-link', authenticate, (req: Request, res: Respon
|
||||
router.delete('/trips/:tripId/share-link', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { tripId } = req.params;
|
||||
if (!canAccessTrip(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = getTripOwnerId(tripId);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('share_manage', authReq.user.role, tripOwnerId, authReq.user.id, tripOwnerId !== authReq.user.id))
|
||||
const access = canAccessTrip(tripId, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
if (!checkPermission('share_manage', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
|
||||
return res.status(403).json({ error: 'No permission' });
|
||||
|
||||
db.prepare('DELETE FROM share_tokens WHERE trip_id = ?').run(tripId);
|
||||
|
||||
@@ -3,7 +3,7 @@ import multer from 'multer';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { db, canAccessTrip, getTripOwnerId } from '../db/database';
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { authenticate, demoUploadBlock } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { AuthRequest, Trip, User } from '../types';
|
||||
@@ -264,9 +264,10 @@ router.put('/:id', authenticate, (req: Request, res: Response) => {
|
||||
|
||||
router.post('/:id/cover', authenticate, demoUploadBlock, uploadCover.single('cover'), (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const tripOwnerId = getTripOwnerId(req.params.id);
|
||||
const access = canAccessTrip(req.params.id, authReq.user.id);
|
||||
const tripOwnerId = access?.user_id;
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
const isMember = tripOwnerId !== authReq.user.id && !!canAccessTrip(req.params.id, authReq.user.id);
|
||||
const isMember = tripOwnerId !== authReq.user.id;
|
||||
if (!checkPermission('trip_cover_upload', authReq.user.role, tripOwnerId, authReq.user.id, isMember))
|
||||
return res.status(403).json({ error: 'No permission to change the cover image' });
|
||||
|
||||
@@ -290,8 +291,9 @@ router.post('/:id/cover', authenticate, demoUploadBlock, uploadCover.single('cov
|
||||
|
||||
router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const tripOwnerId = getTripOwnerId(req.params.id);
|
||||
if (!tripOwnerId) return res.status(404).json({ error: 'Trip not found' });
|
||||
const trip = db.prepare('SELECT user_id FROM trips WHERE id = ?').get(req.params.id) as { user_id: number } | undefined;
|
||||
if (!trip) return res.status(404).json({ error: 'Trip not found' });
|
||||
const tripOwnerId = trip.user_id;
|
||||
if (!checkPermission('trip_delete', authReq.user.role, tripOwnerId, authReq.user.id, false))
|
||||
return res.status(403).json({ error: 'No permission to delete this trip' });
|
||||
const deletedTripId = Number(req.params.id);
|
||||
@@ -309,10 +311,11 @@ router.delete('/:id', authenticate, (req: Request, res: Response) => {
|
||||
|
||||
router.get('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!canAccessTrip(req.params.id, authReq.user.id))
|
||||
const access = canAccessTrip(req.params.id, authReq.user.id);
|
||||
if (!access)
|
||||
return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(req.params.id)!;
|
||||
const tripOwnerId = access.user_id;
|
||||
const members = db.prepare(`
|
||||
SELECT u.id, u.username, u.email, u.avatar,
|
||||
CASE WHEN u.id = ? THEN 'owner' ELSE 'member' END as role,
|
||||
@@ -336,10 +339,11 @@ router.get('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
|
||||
router.post('/:id/members', authenticate, (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
if (!canAccessTrip(req.params.id, authReq.user.id))
|
||||
const access = canAccessTrip(req.params.id, authReq.user.id);
|
||||
if (!access)
|
||||
return res.status(404).json({ error: 'Trip not found' });
|
||||
|
||||
const tripOwnerId = getTripOwnerId(req.params.id)!;
|
||||
const tripOwnerId = access.user_id;
|
||||
const isMember = tripOwnerId !== authReq.user.id;
|
||||
if (!checkPermission('member_manage', authReq.user.role, tripOwnerId, authReq.user.id, isMember))
|
||||
return res.status(403).json({ error: 'No permission to manage members' });
|
||||
@@ -378,9 +382,10 @@ router.delete('/:id/members/:userId', authenticate, (req: Request, res: Response
|
||||
const targetId = parseInt(req.params.userId);
|
||||
const isSelf = targetId === authReq.user.id;
|
||||
if (!isSelf) {
|
||||
const tripOwnerId = getTripOwnerId(req.params.id)!;
|
||||
const memberCheck = tripOwnerId !== authReq.user.id;
|
||||
if (!checkPermission('member_manage', authReq.user.role, tripOwnerId, authReq.user.id, memberCheck))
|
||||
const access = canAccessTrip(req.params.id, authReq.user.id);
|
||||
if (!access) return res.status(404).json({ error: 'Trip not found' });
|
||||
const memberCheck = access.user_id !== authReq.user.id;
|
||||
if (!checkPermission('member_manage', authReq.user.role, access.user_id, authReq.user.id, memberCheck))
|
||||
return res.status(403).json({ error: 'No permission to remove members' });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user