Split the server API module into client, guards, form fields and action helpers

This commit is contained in:
2026-09-24 13:59:21 +02:00
parent 155f0e2b6a
commit 351b333529
21 changed files with 99 additions and 97 deletions
@@ -0,0 +1,27 @@
import { fail, type RequestEvent } from '@sveltejs/kit';
import { apiFor, type ApiClient } from '$lib/server/api';
export interface ActionContext {
api: ApiClient;
form: FormData;
params: Record<string, string>;
locals: App.Locals;
url: URL;
}
export function authedAction<R>(
handler: (context: ActionContext) => R | Promise<R>,
unauthorizedMessage = 'Inicia sesión.'
) {
return async (event: RequestEvent) => {
if (!event.locals.accessToken) return fail(401, { message: unauthorizedMessage });
const form = await event.request.formData();
return handler({
api: apiFor(event),
form,
params: event.params as Record<string, string>,
locals: event.locals,
url: event.url
});
};
}
@@ -0,0 +1,24 @@
import { fail } from '@sveltejs/kit';
import { authedAction } from '$lib/server/actions/authedAction';
const setFollowing = (follow: boolean) =>
authedAction(async ({ api, params }) => {
const id = params.id ?? '';
if (!id) return fail(400, { message: 'Falta el usuario.' });
const options = { params: { path: { id } } };
const { error } = follow
? await api.POST('/users/{id}/follow', options)
: await api.DELETE('/users/{id}/follow', options);
if (error) {
return fail(502, {
message: follow ? 'No se pudo seguir al usuario.' : 'No se pudo dejar de seguir al usuario.'
});
}
return { following: follow, userId: id };
});
export const followUserAction = setFollowing(true);
export const unfollowUserAction = setFollowing(false);
@@ -0,0 +1,46 @@
import { fail } from '@sveltejs/kit';
import { formString } from '$lib/server/forms/fields';
import { authedAction } from '$lib/server/actions/authedAction';
export const addTrackAction = authedAction(async ({ api, form, params }) => {
const playlistId = formString(form, 'playlistId') || (params.id ?? '');
const trackId = formString(form, 'trackId');
if (!playlistId || !trackId) {
return fail(400, { message: 'Faltan datos de la canción.' });
}
const { error, response } = await api.POST('/playlists/{playlistId}/tracks', {
params: { path: { playlistId } },
body: { trackId }
});
if (error) {
if (response?.status === 409) {
return fail(409, { message: 'La canción ya está en esa playlist.', trackId });
}
return fail(502, { message: 'No se pudo añadir la canción.', trackId });
}
return { added: true, trackId, playlistId };
});
export const addAlbumToPlaylistAction = authedAction(async ({ api, form, params }) => {
const playlistId = formString(form, 'playlistId') || (params.id ?? '');
const albumId = formString(form, 'albumId');
if (!playlistId || !albumId) {
return fail(400, { message: 'Faltan datos del álbum.' });
}
const { data, error } = await api.POST('/playlists/{playlistId}/albums/{albumId}', {
params: { path: { playlistId, albumId } }
});
if (error || !data) {
return fail(502, { message: 'No se pudo añadir el álbum.', albumId });
}
const added = Number(data.addedCount);
return { addedAlbum: true, albumId, playlistId, added };
});