fix: require auth for file downloads, localize atlas search, use flag images

- Block direct access to /uploads/files (401), serve via authenticated
  /api/trips/:tripId/files/:id/download with JWT verification
- Client passes auth token as query parameter for direct links
- Atlas country search now uses Intl.DisplayNames (user language) instead
  of English GeoJSON names
- Atlas search results use flagcdn.com flag images instead of emoji
This commit is contained in:
Maurice
2026-03-31 21:38:16 +02:00
parent f7160e6dec
commit 10107ecf31
5 changed files with 74 additions and 22 deletions
+10 -11
View File
@@ -125,24 +125,23 @@ import { authenticate } from './middleware/auth';
app.use('/uploads/avatars', express.static(path.join(__dirname, '../uploads/avatars')));
app.use('/uploads/covers', express.static(path.join(__dirname, '../uploads/covers')));
// Serve uploaded files (UUIDs are unguessable, path traversal protected)
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');
// Prevent path traversal
const safeName = path.basename(filename);
const filePath = path.join(__dirname, '../uploads', type, safeName);
// Serve uploaded photos (public — needed for shared trips)
app.get('/uploads/photos/:filename', (req: Request, res: Response) => {
const safeName = path.basename(req.params.filename);
const filePath = path.join(__dirname, '../uploads/photos', safeName);
const resolved = path.resolve(filePath);
if (!resolved.startsWith(path.resolve(__dirname, '../uploads', type))) {
if (!resolved.startsWith(path.resolve(__dirname, '../uploads/photos'))) {
return res.status(403).send('Forbidden');
}
if (!fs.existsSync(resolved)) return res.status(404).send('Not found');
res.sendFile(resolved);
});
// Block direct access to /uploads/files — served via authenticated /api/trips/:tripId/files/:id/download
app.use('/uploads/files', (_req: Request, res: Response) => {
res.status(401).send('Authentication required');
});
// Routes
import authRoutes from './routes/auth';
import tripsRoutes from './routes/trips';