Bug fixes for video serving and updats to documentation for mobile use screenshots
This commit is contained in:
@@ -3,8 +3,8 @@ import { prisma } from '../../../config/database';
|
||||
import { requireAdminRole } from '../middleware/auth';
|
||||
import { videoAnalyticsService } from '../services/video-analytics.service';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { sign } from 'jsonwebtoken';
|
||||
import { env } from '../../../config/env';
|
||||
import { signMediaPath } from '../../../utils/signed-url';
|
||||
import { copyFile } from 'fs/promises';
|
||||
import { join, dirname, basename, extname, normalize, resolve } from 'path';
|
||||
import { z } from 'zod';
|
||||
@@ -299,7 +299,13 @@ export async function videoActionsRoutes(fastify: FastifyInstance) {
|
||||
|
||||
/**
|
||||
* GET /videos/:id/preview-link
|
||||
* Generate a temporary preview link with expiring JWT token
|
||||
* Generate a shareable, time-limited preview link.
|
||||
*
|
||||
* Uses path-bound HMAC signatures (sig/exp/uid) — same scheme as
|
||||
* POST /api/media/sign — instead of the legacy `?token=<JWT>` form,
|
||||
* which leaked full session tokens via access logs/referer headers.
|
||||
* The signature carries only the admin's user-id and is bound to the
|
||||
* stream URL, so it can be safely shared with stakeholders.
|
||||
*/
|
||||
fastify.get<{ Params: { id: string } }>(
|
||||
'/:id/preview-link',
|
||||
@@ -318,24 +324,20 @@ export async function videoActionsRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(404).send({ message: 'Video not found' });
|
||||
}
|
||||
|
||||
// Generate JWT token that expires in 24 hours
|
||||
const expiryHours = parseInt(process.env.VIDEO_PREVIEW_LINK_EXPIRY_HOURS || '24');
|
||||
const token = sign(
|
||||
{
|
||||
videoId,
|
||||
purpose: 'preview',
|
||||
},
|
||||
env.JWT_ACCESS_SECRET,
|
||||
{ expiresIn: `${expiryHours}h` }
|
||||
);
|
||||
const ttlSeconds = expiryHours * 60 * 60;
|
||||
const userId = request.user!.id;
|
||||
|
||||
const previewUrl = `${env.MEDIA_API_PUBLIC_URL}/api/videos/${videoId}/preview?token=${token}`;
|
||||
const streamPath = `/api/videos/${videoId}/stream`;
|
||||
const signed = signMediaPath(streamPath, userId, ttlSeconds);
|
||||
const query = `sig=${signed.sig}&exp=${signed.exp}&uid=${signed.uid}`;
|
||||
const previewUrl = `${env.MEDIA_API_PUBLIC_URL}${streamPath}?${query}`;
|
||||
|
||||
logger.info(`Generated preview link for video ${videoId}`, { expiresInHours: expiryHours });
|
||||
|
||||
return {
|
||||
previewUrl,
|
||||
expiresAt: new Date(Date.now() + expiryHours * 60 * 60 * 1000).toISOString(),
|
||||
expiresAt: new Date(Number(signed.exp) * 1000).toISOString(),
|
||||
expiryHours,
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user