Design Prot Merge #1

Merged
NotS1mp merged 217 commits from design-prot into master 2026-09-21 15:48:16 +00:00
6 changed files with 57 additions and 1 deletions
Showing only changes of commit 72a7d13f6d - Show all commits
+12
View File
@@ -30,3 +30,15 @@ npm run check # svelte-check — debe salir 0 errores antes de cerrar un camb
`hooks.server.ts` valida la cookie de sesión cifrada (`mf_session`), refresca el `access_token` y puebla `locals.user` / `locals.accessToken`. Para proteger una ruta, comprueba `locals.user` en su `load` y redirige a `/login?returnTo=…`. Para el backend, envía `locals.accessToken` como Bearer hacia `API_BASE_URL`. `hooks.server.ts` valida la cookie de sesión cifrada (`mf_session`), refresca el `access_token` y puebla `locals.user` / `locals.accessToken`. Para proteger una ruta, comprueba `locals.user` en su `load` y redirige a `/login?returnTo=…`. Para el backend, envía `locals.accessToken` como Bearer hacia `API_BASE_URL`.
Variables de entorno en `.env` (ver `.env.example`): `ZITADEL_*`, `AUTH_REDIRECT_URI`, `AUTH_POST_LOGOUT_URI`, `SESSION_SECRET`, `API_BASE_URL`. Variables de entorno en `.env` (ver `.env.example`): `ZITADEL_*`, `AUTH_REDIRECT_URI`, `AUTH_POST_LOGOUT_URI`, `SESSION_SECRET`, `API_BASE_URL`.
## Backend API
Cliente tipado con `openapi-fetch` sobre el OpenAPI de `Musify.Api`. En un `load`/action server:
```ts
import { createApiClient } from '$lib/server/api';
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
const { data, error: err } = await api.GET('/tracks');
```
Devuelve `{ data, error }` (estilo ErrorOr), no lanza. Los tipos viven en `src/lib/api/schema.d.ts` y se regeneran con `npm run gen:api` (requiere la API dev levantada en `API_BASE_URL`, que expone `/openapi/v1.json` solo en Development).
+16
View File
@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@sveltejs/adapter-node": "^5.5.7", "@sveltejs/adapter-node": "^5.5.7",
"jose": "^6.2.3", "jose": "^6.2.3",
"openapi-fetch": "^0.17.0",
"openid-client": "^6.8.4" "openid-client": "^6.8.4"
}, },
"devDependencies": { "devDependencies": {
@@ -1834,6 +1835,21 @@
"node": ">=12.20.0" "node": ">=12.20.0"
} }
}, },
"node_modules/openapi-fetch": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.17.0.tgz",
"integrity": "sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==",
"license": "MIT",
"dependencies": {
"openapi-typescript-helpers": "^0.1.0"
}
},
"node_modules/openapi-typescript-helpers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.1.0.tgz",
"integrity": "sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==",
"license": "MIT"
},
"node_modules/openid-client": { "node_modules/openid-client": {
"version": "6.8.4", "version": "6.8.4",
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz",
+3 -1
View File
@@ -9,7 +9,8 @@
"preview": "vite preview", "preview": "vite preview",
"prepare": "svelte-kit sync || echo ''", "prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"gen:api": "npx -y openapi-typescript@7 http://localhost:5111/openapi/v1.json -o src/lib/api/schema.d.ts"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/adapter-auto": "^7.0.1",
@@ -25,6 +26,7 @@
"dependencies": { "dependencies": {
"@sveltejs/adapter-node": "^5.5.7", "@sveltejs/adapter-node": "^5.5.7",
"jose": "^6.2.3", "jose": "^6.2.3",
"openapi-fetch": "^0.17.0",
"openid-client": "^6.8.4" "openid-client": "^6.8.4"
} }
} }
+4
View File
@@ -0,0 +1,4 @@
export type paths = Record<string, never>;
export type webhooks = Record<string, never>;
export type components = Record<string, never>;
export type operations = Record<string, never>;
+16
View File
@@ -0,0 +1,16 @@
import createClient from 'openapi-fetch';
import type { paths } from '$lib/api/schema';
import { apiConfig } from '$lib/server/config';
interface ApiClientOptions {
fetch: typeof fetch;
accessToken?: string;
}
export function createApiClient({ fetch, accessToken }: ApiClientOptions) {
return createClient<paths>({
baseUrl: apiConfig.baseUrl,
fetch,
headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : undefined
});
}
+6
View File
@@ -45,3 +45,9 @@ export const authConfig = {
return env.AUTH_ALLOW_INSECURE_HTTP === 'true'; return env.AUTH_ALLOW_INSECURE_HTTP === 'true';
} }
}; };
export const apiConfig = {
get baseUrl() {
return (env.API_BASE_URL ?? '').replace(/\/+$/, '');
}
};