Split the server API module into client, guards, form fields and action helpers
This commit is contained in:
@@ -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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import { authedAction } from '$lib/server/api';
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
|
|
||||||
const setFollowing = (follow: boolean) =>
|
const setFollowing = (follow: boolean) =>
|
||||||
authedAction(async ({ api, params }) => {
|
authedAction(async ({ api, params }) => {
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import { authedAction, formString } from '$lib/server/api';
|
import { formString } from '$lib/server/forms/fields';
|
||||||
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
|
|
||||||
export const addTrackAction = authedAction(async ({ api, form, params }) => {
|
export const addTrackAction = authedAction(async ({ api, form, params }) => {
|
||||||
const playlistId = formString(form, 'playlistId') || (params.id ?? '');
|
const playlistId = formString(form, 'playlistId') || (params.id ?? '');
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import createClient from 'openapi-fetch';
|
import createClient from 'openapi-fetch';
|
||||||
import { error, fail, redirect, type ActionFailure, type RequestEvent } from '@sveltejs/kit';
|
import { error, fail, type ActionFailure, type RequestEvent } from '@sveltejs/kit';
|
||||||
import type { paths } from '$lib/api/schema';
|
import type { paths } from '$lib/api/schema';
|
||||||
import type { SessionUser } from '$lib/types';
|
|
||||||
import { apiConfig } from '$lib/server/config';
|
import { apiConfig } from '$lib/server/config';
|
||||||
|
|
||||||
interface ApiClientOptions {
|
interface ApiClientOptions {
|
||||||
@@ -23,59 +22,6 @@ export function apiFor({ fetch, locals }: Pick<RequestEvent, 'fetch' | 'locals'>
|
|||||||
return createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
|
return createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginRedirect(url: URL): never {
|
|
||||||
redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requireUser(locals: App.Locals, url: URL): SessionUser {
|
|
||||||
if (!locals.user) loginRedirect(url);
|
|
||||||
return locals.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function optionalUser(
|
|
||||||
locals: App.Locals,
|
|
||||||
url: URL,
|
|
||||||
allowAnonymousListening: boolean
|
|
||||||
): SessionUser | null {
|
|
||||||
if (locals.user) return locals.user;
|
|
||||||
if (!allowAnonymousListening) loginRedirect(url);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formString(form: FormData, key: string): string {
|
|
||||||
return String(form.get(key) ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formFile(form: FormData, key: string): File | null {
|
|
||||||
const value = form.get(key);
|
|
||||||
return value instanceof File && value.size > 0 ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unwrapOrError<T>(
|
export function unwrapOrError<T>(
|
||||||
result: { data?: T; error?: unknown; response?: Response },
|
result: { data?: T; error?: unknown; response?: Response },
|
||||||
message: string,
|
message: string,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { formString } from '$lib/server/api';
|
import { formString } from '$lib/server/forms/fields';
|
||||||
import { ALBUM_EARLIEST_YEAR, LIMITS } from '$lib/validation';
|
import { ALBUM_EARLIEST_YEAR, LIMITS } from '$lib/validation';
|
||||||
|
|
||||||
interface AlbumFormBody {
|
interface AlbumFormBody {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function formString(form: FormData, key: string): string {
|
||||||
|
return String(form.get(key) ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formFile(form: FormData, key: string): File | null {
|
||||||
|
const value = form.get(key);
|
||||||
|
return value instanceof File && value.size > 0 ? value : null;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { formFile, formString } from '$lib/server/api';
|
import { formFile, formString } from '$lib/server/forms/fields';
|
||||||
import { LIMITS } from '$lib/validation';
|
import { LIMITS } from '$lib/validation';
|
||||||
|
|
||||||
interface PlaylistFormBody {
|
interface PlaylistFormBody {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { formFile, formString } from '$lib/server/api';
|
import { formFile, formString } from '$lib/server/forms/fields';
|
||||||
import { LIMITS } from '$lib/validation';
|
import { LIMITS } from '$lib/validation';
|
||||||
|
|
||||||
interface TrackUploadBody {
|
interface TrackUploadBody {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
import type { SessionUser } from '$lib/types';
|
||||||
|
|
||||||
|
export function loginRedirect(url: URL): never {
|
||||||
|
redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requireUser(locals: App.Locals, url: URL): SessionUser {
|
||||||
|
if (!locals.user) loginRedirect(url);
|
||||||
|
return locals.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optionalUser(
|
||||||
|
locals: App.Locals,
|
||||||
|
url: URL,
|
||||||
|
allowAnonymousListening: boolean
|
||||||
|
): SessionUser | null {
|
||||||
|
if (locals.user) return locals.user;
|
||||||
|
if (!allowAnonymousListening) loginRedirect(url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
import { getAllowAnonymousListening } from '$lib/server/playbackConfig';
|
import { getAllowAnonymousListening } from '$lib/server/playbackConfig';
|
||||||
import { apiFor, loginRedirect } from '$lib/server/api';
|
import { apiFor } from '$lib/server/api';
|
||||||
|
import { loginRedirect } from '$lib/server/guards';
|
||||||
import { toLikedTrack, toPlaylist, toTrack } from '$lib/server/mappers';
|
import { toLikedTrack, toPlaylist, toTrack } from '$lib/server/mappers';
|
||||||
import { authConfig } from '$lib/server/config';
|
import { authConfig } from '$lib/server/config';
|
||||||
import { LIKED_TRACKS_PAGE_SIZE, PLAYLIST_PICKER_PAGE_SIZE } from '$lib/config';
|
import { LIKED_TRACKS_PAGE_SIZE, PLAYLIST_PICKER_PAGE_SIZE } from '$lib/config';
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { apiFor, requireUser } from '$lib/server/api';
|
import { apiFor } from '$lib/server/api';
|
||||||
|
import { requireUser } from '$lib/server/guards';
|
||||||
import { toMix, toPlaylist, toTrack } from '$lib/server/mappers';
|
import { toMix, toPlaylist, toTrack } from '$lib/server/mappers';
|
||||||
import { addTrackAction } from '$lib/server/playlistActions';
|
import { addTrackAction } from '$lib/server/actions/playlist';
|
||||||
import { HOME_MIXES_LIMIT, HOME_SHELF_LIMIT, HOME_SPOTLIGHT_TRACKS_LIMIT } from '$lib/config';
|
import { HOME_MIXES_LIMIT, HOME_SHELF_LIMIT, HOME_SPOTLIGHT_TRACKS_LIMIT } from '$lib/config';
|
||||||
import { pickGreeting } from '$lib/server/greeting';
|
import { pickGreeting } from '$lib/server/greeting';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import { apiFor, authedAction, formFile, requireUser, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
|
import { requireUser } from '$lib/server/guards';
|
||||||
|
import { formFile } from '$lib/server/forms/fields';
|
||||||
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
import { toAlbum, toPage } from '$lib/server/mappers';
|
import { toAlbum, toPage } from '$lib/server/mappers';
|
||||||
import { ALBUMS_PAGE_SIZE } from '$lib/config';
|
import { ALBUMS_PAGE_SIZE } from '$lib/config';
|
||||||
import { parseAlbumForm } from '$lib/server/forms/albumForm';
|
import { parseAlbumForm } from '$lib/server/forms/albumForm';
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import {
|
import { apiFor, failOnError, unwrapOrError } from '$lib/server/api';
|
||||||
apiFor,
|
import { optionalUser } from '$lib/server/guards';
|
||||||
authedAction,
|
import { formFile, formString } from '$lib/server/forms/fields';
|
||||||
failOnError,
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
formFile,
|
|
||||||
formString,
|
|
||||||
optionalUser,
|
|
||||||
unwrapOrError
|
|
||||||
} from '$lib/server/api';
|
|
||||||
import { toAlbum, toTrack } from '$lib/server/mappers';
|
import { toAlbum, toTrack } from '$lib/server/mappers';
|
||||||
import { ALBUM_TRACKS_PAGE_SIZE, LIBRARY_PICKER_PAGE_SIZE } from '$lib/config';
|
import { ALBUM_TRACKS_PAGE_SIZE, LIBRARY_PICKER_PAGE_SIZE } from '$lib/config';
|
||||||
import { parseAlbumForm } from '$lib/server/forms/albumForm';
|
import { parseAlbumForm } from '$lib/server/forms/albumForm';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { PageServerLoad, Actions } from './$types';
|
|||||||
import { apiFor, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
import { getGenres } from '$lib/server/genres';
|
import { getGenres } from '$lib/server/genres';
|
||||||
import { toAlbum, toPage, toTrack } from '$lib/server/mappers';
|
import { toAlbum, toPage, toTrack } from '$lib/server/mappers';
|
||||||
import { addAlbumToPlaylistAction, addTrackAction } from '$lib/server/playlistActions';
|
import { addAlbumToPlaylistAction, addTrackAction } from '$lib/server/actions/playlist';
|
||||||
import { EXPLORE_ALBUMS_PAGE_SIZE, EXPLORE_PAGE_SIZE, EXPLORE_USERS_LIMIT } from '$lib/config';
|
import { EXPLORE_ALBUMS_PAGE_SIZE, EXPLORE_PAGE_SIZE, EXPLORE_USERS_LIMIT } from '$lib/config';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url, locals, fetch }) => {
|
export const load: PageServerLoad = async ({ url, locals, fetch }) => {
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import { toDatedTrack, toPage } from '$lib/server/mappers';
|
import { toDatedTrack, toPage } from '$lib/server/mappers';
|
||||||
import {
|
import { apiFor, failOnError, unwrapOrError } from '$lib/server/api';
|
||||||
apiFor,
|
import { requireUser } from '$lib/server/guards';
|
||||||
authedAction,
|
import { formString } from '$lib/server/forms/fields';
|
||||||
failOnError,
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
formString,
|
|
||||||
requireUser,
|
|
||||||
unwrapOrError
|
|
||||||
} from '$lib/server/api';
|
|
||||||
import { LIBRARY_PAGE_SIZE } from '$lib/config';
|
import { LIBRARY_PAGE_SIZE } from '$lib/config';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals, url, fetch }) => {
|
export const load: PageServerLoad = async ({ locals, url, fetch }) => {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { apiFor, requireUser, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
|
import { requireUser } from '$lib/server/guards';
|
||||||
import { toMix } from '$lib/server/mappers';
|
import { toMix } from '$lib/server/mappers';
|
||||||
import { addTrackAction } from '$lib/server/playlistActions';
|
import { addTrackAction } from '$lib/server/actions/playlist';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params, locals, url, fetch }) => {
|
export const load: PageServerLoad = async ({ params, locals, url, fetch }) => {
|
||||||
requireUser(locals, url);
|
requireUser(locals, url);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import { apiFor, authedAction, requireUser, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
|
import { requireUser } from '$lib/server/guards';
|
||||||
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
import { PLAYLISTS_PAGE_SIZE } from '$lib/config';
|
import { PLAYLISTS_PAGE_SIZE } from '$lib/config';
|
||||||
import { toPlaylist } from '$lib/server/mappers';
|
import { toPlaylist } from '$lib/server/mappers';
|
||||||
import { parsePlaylistForm } from '$lib/server/forms/playlistForm';
|
import { parsePlaylistForm } from '$lib/server/forms/playlistForm';
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import {
|
import { apiFor, failOnError, unwrapOrError } from '$lib/server/api';
|
||||||
apiFor,
|
import { optionalUser } from '$lib/server/guards';
|
||||||
authedAction,
|
import { formString } from '$lib/server/forms/fields';
|
||||||
failOnError,
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
formString,
|
|
||||||
optionalUser,
|
|
||||||
unwrapOrError
|
|
||||||
} from '$lib/server/api';
|
|
||||||
import { PLAYLIST_TRACKS_PAGE_SIZE } from '$lib/config';
|
import { PLAYLIST_TRACKS_PAGE_SIZE } from '$lib/config';
|
||||||
import { toDatedTrack, toPlaylist } from '$lib/server/mappers';
|
import { toDatedTrack, toPlaylist } from '$lib/server/mappers';
|
||||||
import { parsePlaylistForm } from '$lib/server/forms/playlistForm';
|
import { parsePlaylistForm } from '$lib/server/forms/playlistForm';
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import { apiFor, authedAction, requireData, requireUser } from '$lib/server/api';
|
import { apiFor, requireData } from '$lib/server/api';
|
||||||
|
import { requireUser } from '$lib/server/guards';
|
||||||
|
import { authedAction } from '$lib/server/actions/authedAction';
|
||||||
import { parseTrackUploadForm } from '$lib/server/forms/trackUploadForm';
|
import { parseTrackUploadForm } from '$lib/server/forms/trackUploadForm';
|
||||||
import { putPresigned, extOf, contentTypeOf, AUDIO_TYPES, IMAGE_TYPES } from '$lib/server/upload';
|
import { putPresigned, extOf, contentTypeOf, AUDIO_TYPES, IMAGE_TYPES } from '$lib/server/upload';
|
||||||
import { findConflict, genreInfo } from '$lib/data/genres';
|
import { findConflict, genreInfo } from '$lib/data/genres';
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import type { PageServerLoad, Actions } from './$types';
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
import { apiFor, optionalUser, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
|
import { optionalUser } from '$lib/server/guards';
|
||||||
import { toPlaylist } from '$lib/server/mappers';
|
import { toPlaylist } from '$lib/server/mappers';
|
||||||
import { PROFILE_PLAYLISTS_PAGE_SIZE } from '$lib/config';
|
import { PROFILE_PLAYLISTS_PAGE_SIZE } from '$lib/config';
|
||||||
import { followUserAction, unfollowUserAction } from '$lib/server/followActions';
|
import { followUserAction, unfollowUserAction } from '$lib/server/actions/follow';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params, locals, url, fetch, parent }) => {
|
export const load: PageServerLoad = async ({ params, locals, url, fetch, parent }) => {
|
||||||
const { allowAnonymousListening } = await parent();
|
const { allowAnonymousListening } = await parent();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { apiFor, optionalUser, unwrapOrError } from '$lib/server/api';
|
import { apiFor, unwrapOrError } from '$lib/server/api';
|
||||||
|
import { optionalUser } from '$lib/server/guards';
|
||||||
import { toPage } from '$lib/server/mappers';
|
import { toPage } from '$lib/server/mappers';
|
||||||
import { fetchFollowList } from '$lib/server/follows';
|
import { fetchFollowList } from '$lib/server/follows';
|
||||||
import { FOLLOW_LIST_PAGE_SIZE } from '$lib/config';
|
import { FOLLOW_LIST_PAGE_SIZE } from '$lib/config';
|
||||||
|
|||||||
Reference in New Issue
Block a user