fix: fall back to Bunny Storage for cover images
This commit is contained in:
@@ -52,3 +52,18 @@ export async function uploadBunnyCover(image: CoverImage) {
|
||||
}
|
||||
return { key, coverImageUrl: `${cdnHost}/${key}` };
|
||||
}
|
||||
|
||||
export async function downloadBunnyCover(filename: string) {
|
||||
const { zone, password, endpoint } = bunnyStorageConfiguration();
|
||||
const response = await fetch(`${endpoint}/${encodeURIComponent(zone)}/covers/${encodeURIComponent(filename)}`, {
|
||||
headers: { AccessKey: password },
|
||||
});
|
||||
if (!response.ok) {
|
||||
const detail = await response.text().catch(() => '');
|
||||
throw new BunnyStorageRequestError(`Bunny Storage download failed (${response.status})${detail ? `: ${detail.slice(0, 250)}` : ''}`);
|
||||
}
|
||||
return {
|
||||
body: Buffer.from(await response.arrayBuffer()),
|
||||
contentType: response.headers.get('content-type') || 'application/octet-stream',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { FastifyPluginAsync } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import { pool } from '../db/pool.js';
|
||||
import { signedBunnyEmbedUrl, BunnyConfigurationError } from '../providers/bunny.js';
|
||||
import { BunnyStorageConfigurationError, BunnyStorageRequestError, downloadBunnyCover } from '../providers/bunny-storage.js';
|
||||
|
||||
const paramsSchema = z.object({
|
||||
courseId: z.string().uuid(),
|
||||
@@ -10,6 +11,9 @@ const playbackParamsSchema = z.object({
|
||||
courseId: z.string().uuid(),
|
||||
lessonId: z.string().uuid(),
|
||||
});
|
||||
const coverParamsSchema = z.object({
|
||||
filename: z.string().regex(/^[0-9a-f]{8}-[0-9a-f-]{27}\.(?:jpg|png|webp)$/i),
|
||||
});
|
||||
|
||||
export const courseSelect = (publicOnly = false, includeUnreadyMedia = false) => `
|
||||
select
|
||||
@@ -110,6 +114,21 @@ export const courseRoutes: FastifyPluginAsync = async (app) => {
|
||||
return { data: result.rows.map((row) => row.name) };
|
||||
});
|
||||
|
||||
app.get('/covers/:filename', async (request, reply) => {
|
||||
const { filename } = coverParamsSchema.parse(request.params);
|
||||
try {
|
||||
const cover = await downloadBunnyCover(filename);
|
||||
reply.header('Content-Type', cover.contentType);
|
||||
reply.header('Cache-Control', 'public, max-age=86400, stale-while-revalidate=604800');
|
||||
return reply.send(cover.body);
|
||||
} catch (error) {
|
||||
if (error instanceof BunnyStorageConfigurationError || error instanceof BunnyStorageRequestError) {
|
||||
return reply.code(404).send({ error: 'Cover image was not found' });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/', async (request) => {
|
||||
const authenticated = await hasSession(request);
|
||||
const result = await pool.query(
|
||||
|
||||
Reference in New Issue
Block a user