feat: add Bunny Storage course cover uploads
This commit is contained in:
@@ -11,6 +11,12 @@ import {
|
||||
uploadBunnyVideo,
|
||||
} from '../providers/bunny.js';
|
||||
import { config } from '../config.js';
|
||||
import {
|
||||
BunnyStorageConfigurationError,
|
||||
BunnyStorageRequestError,
|
||||
isBunnyStorageConfigured,
|
||||
uploadBunnyCover,
|
||||
} from '../providers/bunny-storage.js';
|
||||
|
||||
const createVideoSchema = z.object({ title: z.string().trim().min(1).max(255) });
|
||||
const videoParamsSchema = z.object({ videoId: z.string().uuid() });
|
||||
@@ -21,6 +27,12 @@ function providerError(reply: { code: (status: number) => { send: (payload: obje
|
||||
throw error;
|
||||
}
|
||||
|
||||
function storageError(reply: { code: (status: number) => { send: (payload: object) => unknown } }, error: unknown) {
|
||||
if (error instanceof BunnyStorageConfigurationError) return reply.code(503).send({ error: error.message });
|
||||
if (error instanceof BunnyStorageRequestError) return reply.code(422).send({ error: error.message });
|
||||
throw error;
|
||||
}
|
||||
|
||||
async function persistBunnyStatus(video: { id: string; status: string; durationSeconds: number | null }) {
|
||||
await pool.query(
|
||||
`update lesson_media
|
||||
@@ -40,6 +52,30 @@ export const mediaRoutes: FastifyPluginAsync = async (app) => {
|
||||
},
|
||||
}));
|
||||
|
||||
app.get('/covers/config', manageAccess, async () => ({
|
||||
data: {
|
||||
configured: isBunnyStorageConfigured(),
|
||||
maxUploadBytes: config.BUNNY_COVER_MAX_UPLOAD_MB * 1024 * 1024,
|
||||
},
|
||||
}));
|
||||
|
||||
app.post('/covers', manageAccess, async (request, reply) => {
|
||||
const contentType = request.headers['content-type']?.split(';')[0]?.toLowerCase();
|
||||
const body = request.body;
|
||||
if (!contentType || !Buffer.isBuffer(body)) return reply.code(400).send({ error: 'Send an image file as the request body.' });
|
||||
if (body.length === 0) return reply.code(400).send({ error: 'Choose an image to upload.' });
|
||||
if (body.length > config.BUNNY_COVER_MAX_UPLOAD_MB * 1024 * 1024) {
|
||||
return reply.code(413).send({ error: `Cover image is larger than the ${config.BUNNY_COVER_MAX_UPLOAD_MB} MB upload limit.` });
|
||||
}
|
||||
try {
|
||||
const cover = await uploadBunnyCover({ body, contentType });
|
||||
await recordAudit({ actorId: request.user.id, action: 'media.cover.uploaded', subjectType: 'cover', metadata: { key: cover.key }, ipAddress: request.ip });
|
||||
return reply.code(201).send({ data: cover });
|
||||
} catch (error) {
|
||||
return storageError(reply, error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/bunny/videos', manageAccess, async (request, reply) => {
|
||||
const input = createVideoSchema.parse(request.body);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user