mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 13:00:13 +02:00
Move server directory outside of frontend src
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import { idValidation, paginationValidation } from './shared.validation';
|
||||
|
||||
export const list = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
serverFolderIds: z.string().min(1),
|
||||
}),
|
||||
};
|
||||
|
||||
export const detail = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('id') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
export const albumArtistsValidation = {
|
||||
detail,
|
||||
list,
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { z } from 'zod';
|
||||
import { AlbumSort } from '@helpers/albums.helpers';
|
||||
import {
|
||||
idValidation,
|
||||
orderByValidation,
|
||||
paginationValidation,
|
||||
serverFolderIdValidation,
|
||||
serverUrlIdValidation,
|
||||
} from './shared.validation';
|
||||
|
||||
const list = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
...serverFolderIdValidation,
|
||||
...orderByValidation,
|
||||
...serverUrlIdValidation,
|
||||
sortBy: z.nativeEnum(AlbumSort),
|
||||
}),
|
||||
};
|
||||
|
||||
const detail = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('albumId'),
|
||||
...idValidation('serverId'),
|
||||
}),
|
||||
query: z.object({
|
||||
...serverUrlIdValidation,
|
||||
}),
|
||||
};
|
||||
|
||||
const detailSongList = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('albumId'),
|
||||
...idValidation('serverId'),
|
||||
}),
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
...serverFolderIdValidation,
|
||||
...orderByValidation,
|
||||
...serverUrlIdValidation,
|
||||
sortBy: z.nativeEnum(AlbumSort),
|
||||
}),
|
||||
};
|
||||
|
||||
export const albumsValidation = {
|
||||
detail,
|
||||
detailSongList,
|
||||
list,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { z } from 'zod';
|
||||
import { AlbumSort } from '@helpers/albums.helpers';
|
||||
import {
|
||||
idValidation,
|
||||
orderByValidation,
|
||||
paginationValidation,
|
||||
serverFolderIdValidation,
|
||||
} from './shared.validation';
|
||||
|
||||
const list = {
|
||||
body: z.object({}),
|
||||
params: z.object({}),
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
...serverFolderIdValidation,
|
||||
...orderByValidation,
|
||||
sortBy: z.nativeEnum(AlbumSort),
|
||||
}),
|
||||
};
|
||||
|
||||
const detail = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('id') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
export const artistsValidation = {
|
||||
detail,
|
||||
list,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const login = {
|
||||
body: z.object({
|
||||
password: z.string(),
|
||||
username: z.string(),
|
||||
}),
|
||||
params: z.object({}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const register = {
|
||||
body: z.object({
|
||||
password: z.string().min(6).max(255),
|
||||
username: z.string().min(4).max(26),
|
||||
}),
|
||||
params: z.object({}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const refresh = {
|
||||
body: z.object({
|
||||
refreshToken: z.string(),
|
||||
}),
|
||||
params: z.object({}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
export const authValidation = {
|
||||
login,
|
||||
refresh,
|
||||
register,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { albumArtistsValidation } from './album-artists.validation';
|
||||
import { albumsValidation } from './albums.validation';
|
||||
import { artistsValidation } from './artists.validation';
|
||||
import { authValidation } from './auth.validation';
|
||||
import { serversValidation } from './servers.validation';
|
||||
import { songsValidation } from './songs.validation';
|
||||
import { usersValidation } from './users.validation';
|
||||
|
||||
export { validateRequest, TypedRequest } from './shared.validation';
|
||||
|
||||
export const validation = {
|
||||
albumArtists: albumArtistsValidation,
|
||||
albums: albumsValidation,
|
||||
artists: artistsValidation,
|
||||
auth: authValidation,
|
||||
servers: serversValidation,
|
||||
songs: songsValidation,
|
||||
users: usersValidation,
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
import { ServerType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
import { idValidation } from './shared.validation';
|
||||
|
||||
const detail = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const list = {
|
||||
body: z.object({}),
|
||||
params: z.object({}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const deleteServer = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const update = {
|
||||
body: z.object({
|
||||
legacy: z.boolean().optional(),
|
||||
name: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
type: z.nativeEnum(ServerType),
|
||||
url: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const create = {
|
||||
body: z.object({
|
||||
legacy: z.boolean().optional(),
|
||||
name: z.string(),
|
||||
password: z.string(),
|
||||
type: z.enum([
|
||||
ServerType.JELLYFIN,
|
||||
ServerType.SUBSONIC,
|
||||
ServerType.NAVIDROME,
|
||||
]),
|
||||
url: z.string(),
|
||||
username: z.string(),
|
||||
}),
|
||||
params: z.object({}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const scan = {
|
||||
body: z.object({ serverFolderId: z.string().array().optional() }),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const refresh = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const createCredential = {
|
||||
body: z.object({ credential: z.string(), username: z.string() }),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const getCredentialDetail = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const deleteCredential = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('credentialId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const enableCredential = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('credentialId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const disableCredential = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('credentialId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const createUrl = {
|
||||
body: z.object({ url: z.string() }),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const deleteUrl = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('urlId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const enableUrl = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('urlId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
const disableUrl = {
|
||||
body: z.object({}),
|
||||
params: z.object({
|
||||
...idValidation('serverId'),
|
||||
...idValidation('urlId'),
|
||||
}),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
export const serversValidation = {
|
||||
create,
|
||||
createCredential,
|
||||
createUrl,
|
||||
deleteCredential,
|
||||
deleteServer,
|
||||
deleteUrl,
|
||||
detail,
|
||||
disableCredential,
|
||||
disableUrl,
|
||||
enableCredential,
|
||||
enableUrl,
|
||||
getCredentialDetail,
|
||||
list,
|
||||
refresh,
|
||||
scan,
|
||||
update,
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
import { Request, RequestHandler } from 'express';
|
||||
import { z, ZodError, ZodSchema } from 'zod';
|
||||
import { SortOrder } from '@/types/types';
|
||||
import { ApiError } from '@/utils';
|
||||
// Modified from zod-express-middleware: https://github.com/Aquila169/zod-express-middleware
|
||||
|
||||
export type TypedRequest<
|
||||
S extends {
|
||||
body: z.AnyZodObject;
|
||||
params: z.AnyZodObject;
|
||||
query: z.AnyZodObject;
|
||||
}
|
||||
> = Request<z.infer<S['params']>, any, z.infer<S['body']>, z.infer<S['query']>>;
|
||||
|
||||
export enum ValidationType {
|
||||
BODY = 'Body',
|
||||
PARAMS = 'Params',
|
||||
QUERY = 'Query',
|
||||
}
|
||||
|
||||
type RequestValidation<TParams, TQuery, TBody> = {
|
||||
body?: ZodSchema<TBody>;
|
||||
params?: ZodSchema<TParams>;
|
||||
query?: ZodSchema<TQuery>;
|
||||
};
|
||||
|
||||
type ErrorListItem = {
|
||||
errors: ZodError<any>;
|
||||
type: ValidationType;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const getErrorValue = (object: any, error: ZodError<any>) => {
|
||||
const e = error.errors[0];
|
||||
if (e.code === z.ZodIssueCode.invalid_type) {
|
||||
return e.expected;
|
||||
}
|
||||
|
||||
return object[e.path[0]][e.path[1]];
|
||||
};
|
||||
|
||||
export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
|
||||
schemas: RequestValidation<TParams, TQuery, TBody>
|
||||
) => RequestHandler<TParams, any, TBody, TQuery> =
|
||||
({ params, query, body }) =>
|
||||
(req, _res, next) => {
|
||||
const errors: Array<ErrorListItem> = [];
|
||||
if (params) {
|
||||
const parsed = params.safeParse(req.params);
|
||||
if (!parsed.success) {
|
||||
errors.push({
|
||||
errors: parsed.error,
|
||||
type: ValidationType.PARAMS,
|
||||
value: getErrorValue(req.params, parsed.error),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (query) {
|
||||
const parsed = query.safeParse(req.query);
|
||||
if (!parsed.success) {
|
||||
const value = getErrorValue(req.query, parsed.error);
|
||||
errors.push({
|
||||
errors: parsed.error,
|
||||
type: ValidationType.QUERY,
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (body) {
|
||||
const parsed = body.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
errors.push({
|
||||
errors: parsed.error,
|
||||
type: ValidationType.BODY,
|
||||
value: getErrorValue(req.body, parsed.error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
const message = JSON.stringify(
|
||||
[
|
||||
// `(${errors[0].type})`,
|
||||
`(${errors[0].type}: ${errors[0].errors.issues[0].path[0]})`,
|
||||
errors[0].value && `[${errors[0].value}]`,
|
||||
errors[0].errors.issues[0].message,
|
||||
]
|
||||
.filter((x) => x)
|
||||
.join(' ')
|
||||
);
|
||||
|
||||
throw ApiError.badRequest(message);
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
// const requiredErrorMessage = (
|
||||
// type: 'Query' | 'Body' | 'Params',
|
||||
// key: string
|
||||
// ) => {
|
||||
// return `(${type}) [${key}] Required`;
|
||||
// };
|
||||
|
||||
export const paginationValidation = {
|
||||
skip: z.string().refine((value) => {
|
||||
const parsed = Number(value);
|
||||
return !Number.isNaN(parsed) && parsed >= 0;
|
||||
}),
|
||||
take: z.string().refine((value) => {
|
||||
const parsed = Number(value);
|
||||
return !Number.isNaN(parsed) && parsed >= 0;
|
||||
}),
|
||||
};
|
||||
|
||||
export const serverUrlIdValidation = {
|
||||
serverUrlId: z.optional(z.string().uuid()),
|
||||
};
|
||||
|
||||
export const idValidation = (property: string) => {
|
||||
return { [property]: z.string().uuid() };
|
||||
};
|
||||
|
||||
export const serverFolderIdValidation = {
|
||||
serverFolderId: z.optional(z.string().uuid().array()),
|
||||
};
|
||||
|
||||
export const orderByValidation = {
|
||||
orderBy: z.nativeEnum(SortOrder),
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
idValidation,
|
||||
paginationValidation,
|
||||
serverFolderIdValidation,
|
||||
} from './shared.validation';
|
||||
|
||||
const list = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('serverId') }),
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
...serverFolderIdValidation,
|
||||
albumIds: z.optional(z.string()),
|
||||
artistIds: z.optional(z.string()),
|
||||
songIds: z.optional(z.string()),
|
||||
}),
|
||||
};
|
||||
|
||||
export const songsValidation = {
|
||||
list,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import { idValidation } from './shared.validation';
|
||||
|
||||
const detail = {
|
||||
body: z.object({}),
|
||||
params: z.object({ ...idValidation('id') }),
|
||||
query: z.object({}),
|
||||
};
|
||||
|
||||
export const usersValidation = {
|
||||
detail,
|
||||
};
|
||||
Reference in New Issue
Block a user