Web Player Refactor #2
@@ -46,7 +46,7 @@ src/routes/
|
||||
library/ /library Tu biblioteca ⬜
|
||||
playlists/ /playlists Tus playlists ⬜
|
||||
[id]/ /playlists/:id Detalle playlist ⬜
|
||||
login logout auth/callback Auth (endpoints) ✅
|
||||
auth auth/login auth/logout auth/callback Auth (página + endpoints) ✅
|
||||
api/tracks/[id]/stream Proxy de stream ✅
|
||||
```
|
||||
|
||||
@@ -66,10 +66,11 @@ Página de entrada para no autenticados. Vender el producto y llevar a registro/
|
||||
|
||||
## 2. Login / Auth ✅ (endpoints)
|
||||
|
||||
No es una página visual propia: `/login` inicia el flujo OIDC de Zitadel y redirige.
|
||||
`?mode=register` arranca en registro; `?returnTo=` vuelve a la ruta pedida tras entrar.
|
||||
`/logout` cierra sesión. `/auth/callback` intercambia el código y crea la cookie
|
||||
cifrada `mf_session`. La UI de credenciales la sirve Zitadel, no Musify.
|
||||
`/auth` es la pantalla de login de Musify; sus botones llevan a `/auth/login`, que inicia
|
||||
el flujo OIDC de Zitadel y redirige. `?mode=register` arranca en registro; `?returnTo=`
|
||||
vuelve a la ruta pedida tras entrar. `/auth/logout` cierra sesión. `/auth/callback`
|
||||
intercambia el código y crea la cookie cifrada `mf_session`. La UI de credenciales la
|
||||
sirve Zitadel, no Musify.
|
||||
|
||||
## 3. Explorar / Buscar `/explore` ✅ (app)
|
||||
|
||||
@@ -101,7 +102,7 @@ Contenido de la página:
|
||||
- **Pipeline visible**: pasos Subir → Procesar → Publicar con su estado.
|
||||
- **Barra de progreso** de subida y estado de procesado.
|
||||
- **Estados**: vacío, archivo seleccionado, subiendo, procesando, publicado, error.
|
||||
- **Requiere sesión** (al cablear: redirigir a `/login?returnTo=/upload`).
|
||||
- **Requiere sesión** (al cablear: redirigir a `/auth?returnTo=/upload`).
|
||||
|
||||
## 5. Tu biblioteca `/library` ⬜ (app)
|
||||
|
||||
@@ -133,7 +134,7 @@ Listado de las playlists del usuario.
|
||||
- **Acciones del dueño**: editar (`PUT`), borrar (`DELETE`) la playlist, reordenar (futuro).
|
||||
- **Añadir tracks**: desde explorar/biblioteca (`POST /playlists/{id}/tracks/{trackId}`).
|
||||
|
||||
## 8. Perfil de usuario `/users/[id]` ⬜ (futuro)
|
||||
## 8. Perfil de usuario `/user/[id]` ⬜ (futuro)
|
||||
|
||||
Vista pública de un usuario: avatar, nombre, sus canciones y playlists públicas
|
||||
(`GET /users/{id}`, `GET /tracks/users/{id}`, `GET /playlists/users/{id}`).
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{#if name}
|
||||
{#if ownerUserId}
|
||||
<a
|
||||
href="/u/{ownerUserId}"
|
||||
href="/user/{ownerUserId}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="block max-w-full truncate hover:text-fg-2 hover:underline {klass}"
|
||||
>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
{#if open}
|
||||
<GlassMenu class="absolute top-full right-0 z-(--z-menu) mt-2 overflow-hidden {WIDTH[width]}">
|
||||
<MenuItem icon={User} label="Ver perfil" href="/u/{user.sub}" onclick={close} />
|
||||
<MenuItem icon={User} label="Ver perfil" href="/user/{user.sub}" onclick={close} />
|
||||
{@render extraItems?.({ close })}
|
||||
{#if accountUrl}
|
||||
<MenuItem
|
||||
@@ -61,7 +61,7 @@
|
||||
label={themeMode.current === 'dark' ? 'Modo blanco' : 'Modo oscuro'}
|
||||
onclick={() => themeMode.toggle()}
|
||||
/>
|
||||
<form method="POST" action="/logout" data-sveltekit-reload>
|
||||
<form method="POST" action="/auth/logout" data-sveltekit-reload>
|
||||
<MenuItem icon={LogOut} label="Cerrar sesión" type="submit" />
|
||||
</form>
|
||||
</GlassMenu>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function createApiClient({ fetch, accessToken }: ApiClientOptions) {
|
||||
|
||||
export function requireUser(locals: App.Locals, url: URL): SessionUser {
|
||||
if (!locals.user)
|
||||
redirect(302, `/login?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
return locals.user;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function optionalUser(
|
||||
): SessionUser | null {
|
||||
if (locals.user) return locals.user;
|
||||
if (!allowAnonymousListening)
|
||||
redirect(302, `/login?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
if (!topResult || topResult.kind === 'track') return undefined;
|
||||
if (topResult.kind === 'album') return `/albums/${topResult.album.id}`;
|
||||
if (topResult.kind === 'playlist') return `/playlists/${topResult.playlist.id}`;
|
||||
return `/u/${topResult.user.id}`;
|
||||
return `/user/${topResult.user.id}`;
|
||||
});
|
||||
|
||||
function togglePlay(index: number) {
|
||||
@@ -400,7 +400,7 @@
|
||||
<ul class="flex flex-col gap-1">
|
||||
{#each capped(sfilter, users) as u (u.id)}
|
||||
<li>
|
||||
<ListRow title={u.name} href="/u/{u.id}" size="lg">
|
||||
<ListRow title={u.name} href="/user/{u.id}" size="lg">
|
||||
{#snippet art()}
|
||||
<Avatar name={u.name} src={u.profilePictureUrl} size="md" />
|
||||
{/snippet}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createApiClient } from '$lib/server/api';
|
||||
import { authConfig } from '$lib/server/config';
|
||||
import { LIKED_TRACKS_PAGE_SIZE } from '$lib/config';
|
||||
|
||||
const PUBLIC_PATHS = new Set(['/login']);
|
||||
const PUBLIC_PATHS = new Set(['/auth']);
|
||||
const SIDEBAR_PLAYLISTS_LIMIT = 8;
|
||||
|
||||
async function fetchSidebarPlaylists(
|
||||
@@ -43,7 +43,7 @@ export const load: LayoutServerLoad = async ({ locals, url, fetch }) => {
|
||||
const allowAnonymousListening = await getAllowAnonymousListening(fetch);
|
||||
|
||||
if (!locals.user && !allowAnonymousListening && !PUBLIC_PATHS.has(url.pathname)) {
|
||||
redirect(302, `/login?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||
}
|
||||
|
||||
const accountUrl = authConfig.issuer
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
if (data.likedTracks?.length) liked.hydrate(data.likedTracks);
|
||||
});
|
||||
|
||||
const isAuthPage = $derived(page.url.pathname === '/login');
|
||||
const isAuthPage = $derived(page.url.pathname === '/auth');
|
||||
const hasTrack = $derived(player.currentId !== null);
|
||||
|
||||
const initialThemeCss = `:root{${tokensToCss(buildThemeTokens(ACCENT_HUE, 'dark'))}}:root[data-theme='light']{${tokensToCss(buildThemeTokens(ACCENT_HUE, 'light'))}}`;
|
||||
|
||||
Reference in New Issue
Block a user