first commit
Some checks failed
CI / Validate frontend and API (push) Failing after 7m27s

This commit is contained in:
Cauê Faleiros
2026-08-31 12:38:34 -03:00
parent 684f929f28
commit 701788ea08
40 changed files with 5396 additions and 380 deletions

View File

@@ -0,0 +1,115 @@
import type { FastifyPluginAsync } from 'fastify';
import { z } from 'zod';
import { pool } from '../db/pool.js';
const paramsSchema = z.object({
courseId: z.string().uuid(),
});
export const courseSelect = (publicOnly = false) => `
select
c.id,
c.slug,
c.title,
c.description,
c.category,
c.cover_image_url as "coverImageUrl",
c.status,
c.published_at as "publishedAt",
jsonb_build_object(
'id', u.id,
'name', u.display_name
) as instructor,
coalesce((
select jsonb_agg(
jsonb_build_object(
'id', l.id,
'title', l.title,
'description', l.description,
'position', l.position,
'durationSeconds', l.duration_seconds,
'accessLevel', l.access_level,
'assets', coalesce((
select jsonb_agg(jsonb_build_object(
'id', a.id,
'name', a.name,
'kind', a.kind,
'url', a.url,
'description', a.description,
'accessLevel', a.access_level
) order by a.created_at)
from assets a
where a.lesson_id = l.id ${publicOnly ? "and a.access_level = 'public'" : ''}
), '[]'::jsonb),
'media', coalesce((
select jsonb_agg(
jsonb_build_object(
'id', lm.id,
'provider', lm.provider,
'status', lm.status,
'embedUrl', lm.embed_url,
'playbackUrl', lm.playback_url,
'durationSeconds', lm.duration_seconds
) order by lm.created_at
)
from lesson_media lm
where lm.lesson_id = l.id and lm.status = 'ready'
), '[]'::jsonb)
) order by l.position
)
from lessons l
where l.course_id = c.id ${publicOnly ? "and l.access_level = 'public'" : ''}
), '[]'::jsonb) as lessons
, coalesce((
select jsonb_agg(jsonb_build_object(
'id', a.id,
'name', a.name,
'kind', a.kind,
'url', a.url,
'description', a.description,
'accessLevel', a.access_level
) order by a.created_at)
from assets a
where a.course_id = c.id ${publicOnly ? "and a.access_level = 'public'" : ''}
), '[]'::jsonb) as assets
from courses c
join users u on u.id = c.instructor_id
`;
export const courseRoutes: FastifyPluginAsync = async (app) => {
const withoutProtectedMedia = (course: Record<string, unknown>) => ({
...course,
assets: [],
lessons: Array.isArray(course.lessons)
? course.lessons.map((lesson) => ({ ...lesson, media: [], assets: [] }))
: [],
});
const hasSession = async (request: { jwtVerify: () => Promise<unknown> }) => {
try {
await request.jwtVerify();
return true;
} catch {
return false;
}
};
app.get('/', async (request) => {
const authenticated = await hasSession(request);
const result = await pool.query(`${courseSelect()} where c.status = 'published' order by c.published_at desc`);
return { data: authenticated ? result.rows : result.rows.map(withoutProtectedMedia) };
});
app.get('/:courseId', async (request, reply) => {
const authenticated = await hasSession(request);
const { courseId } = paramsSchema.parse(request.params);
const result = await pool.query(`${courseSelect()} where c.id = $1 and c.status = 'published'`, [courseId]);
const course = result.rows[0];
if (!course) {
return reply.code(404).send({ error: 'Course not found' });
}
return { data: authenticated ? course : withoutProtectedMedia(course) };
});
};