- Svelte 62.6%
- TypeScript 36.6%
- JavaScript 0.5%
- CSS 0.1%
- HTML 0.1%
- Other 0.1%
|
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
As 18 verificações do `html-validity.spec.ts` falhavam em
`UnsupportedClassVersionError: class file version 61.0 ... only recognizes
up to 55.0` — 61 é Java 17, 55 é Java 11. O passo instalava
`default-jre-headless`, que no Ubuntu jammy é o 11, e o `vnu.jar` do
projeto foi compilado para o 17.
Passa a instalar `openjdk-17-jre-headless`. Verificado dentro da própria
imagem do Playwright: com o default, o vnu estoura; com o 17, devolve
`{"version":"26.8.6","messages":[]}`.
Vale registrar por que isso apareceu como falha e não como silêncio: o
teste recusa relatório vazio em vez de ler "nenhuma mensagem" como
"página válida". Sem essa guarda, as 18 verificações teriam ficado
verdes sem validar uma linha de HTML.
|
||
|---|---|---|
| cypress | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| .woodpecker.yml | ||
| app.json | ||
| Dockerfile | ||
| eslint.config.js | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| postcss.config.js | ||
| Procfile | ||
| README.md | ||
| svelte.config.js | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
playfullms-front
Frontend SvelteKit 5 (runes) + Tailwind do PlayfulLMS.
Development
npm install
npm run dev # http://localhost:5173
Requer o backend rodando em http://localhost:8000 (ver backend/README.md).
| Script | O que faz |
|---|---|
npm run dev |
Dev server (Vite) |
npm run build |
Build de produção (adapter-node) |
npm run preview |
Serve o build |
npm run check |
svelte-check + sync de tipos |
npm run lint |
ESLint |
npm test |
Unit tests (Vitest) |
npm run test:e2e |
E2E (Playwright) |
npm run test:cypress |
E2E (Cypress) |
Autenticação
O login (src/routes/(auth)/login/+page.server.ts) posta em
/api/v1/auth/login no backend e guarda o access_token em cookie httpOnly.
Auth e papéis são do Keyrunes — o namespace usado é public, e o procedimento
para criar o primeiro admin está em
../backend/README.md.
src/hooks.server.ts resolve o usuário a cada request via GET /auth/me e
coloca o resultado em event.locals.user (SessionUser | null, tipado em
src/app.d.ts). Visitante anônimo, token expirado e backend fora do ar são todos
null — quem consome decide o que fazer.
| Área | Gate |
|---|---|
src/routes/(admin)/ |
+layout.server.ts: exige ADMIN ou TEACHER; sem sessão redireciona para /login?redirect=… |
src/routes/(app)/ |
Sem gate de layout — cada +page.server.ts checa o cookie |
src/routes/(public)/ |
Aberto |
Papéis vêm em maiúsculas (ADMIN / TEACHER / STUDENT), derivados no backend
a partir do claim groups do Keyrunes.
Em dev com KEYRUNES_MOCK=true no backend, logar como admin@example.com dá
ADMIN e prof@example.com dá TEACHER (qualquer senha com 6+ caracteres).
Carrinho
$lib/stores/cart.svelte.ts guarda o estado do carrinho. Visitante anônimo fica em
localStorage; ao logar, cartStore.merge() envia os itens para
POST /cart/merge antes de navegar — sem isso, entrar no meio da compra perderia
o carrinho.
cartStore.count é o número de linhas (não a soma das quantidades).
O badge do header não lê o store no servidor: o store é um singleton de
módulo, e mutá-lo durante SSR vazaria o carrinho de um usuário na resposta de
outro. hydrate() roda só no cliente (via $effect), e o header recebe
cartCount como prop do layout para acertar o primeiro paint. A flag hydrated
decide qual dos dois valores mostrar.
As chamadas passam pelos proxies em src/routes/api/v1/cart/*, que anexam o token
do cookie — o código do navegador nunca vê o token.
Shell do site
SiteHeader e SiteFooter (em $lib/components/layout/) são montados pelos
layouts de (public) e (app). O (admin) tem shell próprio.
| Coisa | Onde |
|---|---|
| Container 1170px | max-w-site (definido em tailwind.config.ts) |
| Fontes | Inter + Plus Jakarta Sans, carregadas em app.html com preconnect e display=swap |
| Ícones | $lib/components/ui/Icon.svelte — 15 glifos SVG inline, sem webfont |
| 404 | +error.svelte na raiz e por grupo; o corpo vem de ErrorBody.svelte |
Há quatro +error.svelte de propósito: uma URL que não casa com rota nenhuma não
passa por layout de grupo, então a página raiz precisa trazer header e footer por
conta própria. As variantes de grupo só renderizam o corpo.
(app)/+layout.server.ts faz o gate da área logada. Antes disso cada
+page.server.ts checava o cookie sozinho — e quem esquecesse renderizava para
visitante anônimo.
Fórum
Três telas em (app)/courses/[slug]/forum/: lista de tópicos, tópico com
respostas ([topic_id]) e novo tópico (new). Tipos e o loader do curso ficam em
$lib/server/forum.ts — SvelteKit só permite um conjunto fixo de exports em
+page.server.ts.
O 403 da API vira uma página de erro que explica que falta matrícula, em vez de uma lista vazia.
Para decidir o que renderizar, compare
locals.user.id(ousers.idlocal), nuncalocals.user.user_id(o subject do Keyrunes). Ids de autor vêm do banco; comparar com o subject não casa nunca — foi exatamente o bug que fazia o autor do tópico não ver o botão de aceitar solução.