Add direct file download links to completed requests

Embeds a signed JWT download token (30-day expiry) in the requests API
response so users can download completed audiobook/ebook files directly
from the UI or by sharing the URL to apps like BookPlayer — no session
cookie required.

- jwt.ts: add generateDownloadToken / verifyDownloadToken helpers
- api/requests: append downloadUrl to completed requests with a filePath
- api/requests/[id]/download: new token-authenticated streaming endpoint;
  serves single files directly or zips multi-file audiobooks with adm-zip
- RequestCard: add Download link in the actions area for completed requests
This commit is contained in:
razzamatazm
2026-02-26 11:21:06 -08:00
parent 547af71de8
commit 1006a04337
4 changed files with 201 additions and 2 deletions
+12 -2
View File
@@ -9,6 +9,7 @@ import { prisma } from '@/lib/db';
import { z } from 'zod';
import { RMABLogger } from '@/lib/utils/logger';
import { createRequestForUser } from '@/lib/services/request-creator.service';
import { generateDownloadToken } from '@/lib/utils/jwt';
const logger = RMABLogger.create('API.Requests');
@@ -146,10 +147,19 @@ export async function GET(request: NextRequest) {
take: limit,
});
const COMPLETED_STATUSES = ['available', 'downloaded'];
const enriched = requests.map(r => {
const isCompleted = COMPLETED_STATUSES.includes(r.status);
const hasFile = isCompleted && r.audiobook?.filePath;
if (!hasFile) return r;
const token = generateDownloadToken(req.user!.id, r.id);
return { ...r, downloadUrl: `/api/requests/${r.id}/download?token=${token}` };
});
return NextResponse.json({
success: true,
requests,
count: requests.length,
requests: enriched,
count: enriched.length,
});
} catch (error) {
logger.error('Failed to get requests', { error: error instanceof Error ? error.message : String(error) });