augmenttoolkit / server /src /nest /files /files-download.controller.ts
Leon4gr45's picture
Upload folder using huggingface_hub (part 5)
57a889c verified
Raw
History Blame Contribute Delete
2.51 kB
import { Controller, Get, HttpException, Param, Req, Res } from '@nestjs/common';
import type { Request, Response } from 'express';
import path from 'path';
import fs from 'fs';
import { FilesService } from './files.service';
/**
* GET /api/trips/:tripId/files/:id/download — authenticated file download.
*
* Deliberately NOT behind the JwtAuthGuard: it accepts a cookie, a Bearer header
* OR a one-shot `?token=` query param (so links can be opened directly), all via
* the legacy authenticateDownload helper. Byte-identical to the legacy route:
* 401 token, 404 trip/file, 403 path traversal, .pkpass served inline for Wallet.
*/
@Controller('api/trips/:tripId/files')
export class FilesDownloadController {
constructor(private readonly files: FilesService) {}
@Get(':id/download')
download(@Req() req: Request, @Res() res: Response, @Param('tripId') tripId: string, @Param('id') id: string): void {
const auth = this.files.authenticateDownload(req);
if ('error' in auth) {
throw new HttpException({ error: auth.error }, auth.status);
}
const trip = this.files.verifyTripAccess(tripId, auth.userId);
if (!trip) {
throw new HttpException({ error: 'Trip not found' }, 404);
}
const file = this.files.getFileById(id, tripId);
if (!file) {
throw new HttpException({ error: 'File not found' }, 404);
}
const { resolved, safe } = this.files.resolveFilePath(file.filename);
if (!safe) {
throw new HttpException({ error: 'Forbidden' }, 403);
}
if (!fs.existsSync(resolved)) {
throw new HttpException({ error: 'File not found' }, 404);
}
// Serve Apple Wallet passes inline with the canonical MIME type so Safari
// (iOS/macOS) hands them to Wallet instead of downloading as a blob.
if (path.extname(resolved).toLowerCase() === '.pkpass') {
res.setHeader('Content-Type', 'application/vnd.apple.pkpass');
res.setHeader('Content-Disposition', `inline; filename="${path.basename(file.original_name || resolved)}"`);
}
// Serve with an explicit { root } + basename rather than an absolute path:
// under the Nest ExpressAdapter, res.sendFile(absolutePath) resolves the
// file relative to the (rewritten) req.url and fails with a spurious
// "Not Found", whereas the root-relative form streams correctly. The
// resolveFilePath guard above already pins this to the uploads dir.
res.sendFile(path.basename(resolved), { root: path.dirname(resolved) });
}
}