mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-17 08:54:27 +02:00
Add validations, req.authUser
This commit is contained in:
@@ -5,7 +5,7 @@ export const authenticateAdmin = (
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
) => {
|
) => {
|
||||||
if (!req.auth.isAdmin) {
|
if (!req.authUser.isAdmin) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
error: {
|
error: {
|
||||||
message: 'This action requires an administrator account.',
|
message: 'This action requires an administrator account.',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ServerFolderPermissions,
|
ServerCredential,
|
||||||
ServerPermissions,
|
ServerFolderPermission,
|
||||||
|
ServerPermission,
|
||||||
User,
|
User,
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import { NextFunction, Request, Response } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
@@ -9,8 +10,9 @@ import passport from 'passport';
|
|||||||
export type AuthUser = User & {
|
export type AuthUser = User & {
|
||||||
flatServerFolderPermissions: string[];
|
flatServerFolderPermissions: string[];
|
||||||
flatServerPermissions: string[];
|
flatServerPermissions: string[];
|
||||||
serverFolderPermissions: ServerFolderPermissions[];
|
serverFolderPermissions: ServerFolderPermission[];
|
||||||
serverPermissions: ServerPermissions[];
|
serverId?: string;
|
||||||
|
serverPermissions: ServerPermission[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authenticate = (
|
export const authenticate = (
|
||||||
@@ -46,27 +48,36 @@ export const authenticate = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const flatServerFolderPermissions = user.serverFolderPermissions.map(
|
const flatServerFolderPermissions = user.serverFolderPermissions.map(
|
||||||
(permission: ServerFolderPermissions) => permission.serverFolderId
|
(permission: ServerFolderPermission) => permission.serverFolderId
|
||||||
);
|
);
|
||||||
|
|
||||||
const flatServerPermissions = user.serverPermissions.map(
|
const flatServerPermissions = user.serverPermissions.map(
|
||||||
(permission: ServerPermissions) => permission.serverId
|
(permission: ServerPermission) => permission.serverId
|
||||||
);
|
);
|
||||||
|
|
||||||
const auth = {
|
const serverCredentials = user.serverCredentials.map(
|
||||||
|
(credential: ServerCredential) => ({
|
||||||
|
id: credential.id,
|
||||||
|
serverId: credential.serverId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const props = {
|
||||||
createdAt: user?.createdAt,
|
createdAt: user?.createdAt,
|
||||||
enabled: user?.enabled,
|
enabled: user?.enabled,
|
||||||
flatServerFolderPermissions,
|
flatServerFolderPermissions,
|
||||||
flatServerPermissions,
|
flatServerPermissions,
|
||||||
id: user?.id,
|
id: user?.id,
|
||||||
isAdmin: user?.isAdmin,
|
isAdmin: user?.isAdmin,
|
||||||
|
server: req.params.serverId,
|
||||||
|
serverCredentials,
|
||||||
serverFolderPermissions: user?.serverFolderPermissions,
|
serverFolderPermissions: user?.serverFolderPermissions,
|
||||||
serverPermissions: user?.serverPermissions,
|
serverPermissions: user?.serverPermissions,
|
||||||
updatedAt: user?.updatedAt,
|
updatedAt: user?.updatedAt,
|
||||||
username: user?.username,
|
username: user?.username,
|
||||||
};
|
};
|
||||||
|
|
||||||
req.auth = auth;
|
req.authUser = props;
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
declare namespace Express {
|
declare namespace Express {
|
||||||
export interface Request {
|
export interface Request {
|
||||||
auth: any;
|
authUser: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { paginationValidation, idValidation } from './shared.validation';
|
|||||||
|
|
||||||
export const list = {
|
export const list = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object({}),
|
params: z.object({ ...idValidation('serverId') }),
|
||||||
query: z.object({
|
query: z.object({
|
||||||
...paginationValidation,
|
...paginationValidation,
|
||||||
serverFolderIds: z.string().min(1),
|
serverFolderIds: z.string().min(1),
|
||||||
@@ -12,7 +12,7 @@ export const list = {
|
|||||||
|
|
||||||
export const detail = {
|
export const detail = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object({ ...idValidation }),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,23 +5,27 @@ import {
|
|||||||
orderByValidation,
|
orderByValidation,
|
||||||
paginationValidation,
|
paginationValidation,
|
||||||
serverFolderIdValidation,
|
serverFolderIdValidation,
|
||||||
|
serverUrlIdValidation,
|
||||||
} from './shared.validation';
|
} from './shared.validation';
|
||||||
|
|
||||||
const list = {
|
const list = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object({}),
|
params: z.object({ ...idValidation('serverId') }),
|
||||||
query: z.object({
|
query: z.object({
|
||||||
...paginationValidation,
|
...paginationValidation,
|
||||||
...serverFolderIdValidation,
|
...serverFolderIdValidation,
|
||||||
...orderByValidation,
|
...orderByValidation,
|
||||||
|
...serverUrlIdValidation,
|
||||||
sortBy: z.nativeEnum(AlbumSort),
|
sortBy: z.nativeEnum(AlbumSort),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const detail = {
|
const detail = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({
|
||||||
|
...serverUrlIdValidation,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const albumsValidation = {
|
export const albumsValidation = {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const list = {
|
|||||||
|
|
||||||
const detail = {
|
const detail = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import { albumArtistsValidation } from './album-artists.validation';
|
||||||
import { albumsValidation } from './albums.validation';
|
import { albumsValidation } from './albums.validation';
|
||||||
|
import { artistsValidation } from './artists.validation';
|
||||||
|
import { authValidation } from './auth.validation';
|
||||||
import { serversValidation } from './servers.validation';
|
import { serversValidation } from './servers.validation';
|
||||||
import { songsValidation } from './songs.validation';
|
import { songsValidation } from './songs.validation';
|
||||||
import { usersValidation } from './users.validation';
|
import { usersValidation } from './users.validation';
|
||||||
@@ -6,7 +9,10 @@ import { usersValidation } from './users.validation';
|
|||||||
export { validateRequest, TypedRequest } from './shared.validation';
|
export { validateRequest, TypedRequest } from './shared.validation';
|
||||||
|
|
||||||
export const validation = {
|
export const validation = {
|
||||||
|
albumArtists: albumArtistsValidation,
|
||||||
albums: albumsValidation,
|
albums: albumsValidation,
|
||||||
|
artists: artistsValidation,
|
||||||
|
auth: authValidation,
|
||||||
servers: serversValidation,
|
servers: serversValidation,
|
||||||
songs: songsValidation,
|
songs: songsValidation,
|
||||||
users: usersValidation,
|
users: usersValidation,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { idValidation } from './shared.validation';
|
|||||||
|
|
||||||
const detail = {
|
const detail = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,18 +27,25 @@ const create = {
|
|||||||
|
|
||||||
const scan = {
|
const scan = {
|
||||||
body: z.object({ serverFolderId: z.string().array().optional() }),
|
body: z.object({ serverFolderId: z.string().array().optional() }),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const refresh = {
|
const refresh = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
|
query: z.object({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCredential = {
|
||||||
|
body: z.object({ credential: z.string() }),
|
||||||
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const serversValidation = {
|
export const serversValidation = {
|
||||||
create,
|
create,
|
||||||
|
createCredential,
|
||||||
detail,
|
detail,
|
||||||
refresh,
|
refresh,
|
||||||
scan,
|
scan,
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ type RequestValidation<TParams, TQuery, TBody> = {
|
|||||||
type ErrorListItem = {
|
type ErrorListItem = {
|
||||||
errors: ZodError<any>;
|
errors: ZodError<any>;
|
||||||
type: ValidationType;
|
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>(
|
export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
|
||||||
@@ -38,29 +48,45 @@ export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
|
|||||||
if (params) {
|
if (params) {
|
||||||
const parsed = params.safeParse(req.params);
|
const parsed = params.safeParse(req.params);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
errors.push({ errors: parsed.error, type: ValidationType.PARAMS });
|
errors.push({
|
||||||
|
errors: parsed.error,
|
||||||
|
type: ValidationType.PARAMS,
|
||||||
|
value: getErrorValue(req.params, parsed.error),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query) {
|
if (query) {
|
||||||
const parsed = query.safeParse(req.query);
|
const parsed = query.safeParse(req.query);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
errors.push({ errors: parsed.error, type: ValidationType.QUERY });
|
const value = getErrorValue(req.query, parsed.error);
|
||||||
|
errors.push({
|
||||||
|
errors: parsed.error,
|
||||||
|
type: ValidationType.QUERY,
|
||||||
|
value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (body) {
|
if (body) {
|
||||||
const parsed = body.safeParse(req.body);
|
const parsed = body.safeParse(req.body);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
errors.push({ errors: parsed.error, type: ValidationType.BODY });
|
errors.push({
|
||||||
|
errors: parsed.error,
|
||||||
|
type: ValidationType.BODY,
|
||||||
|
value: getErrorValue(req.body, parsed.error),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
const message = JSON.stringify(
|
const message = JSON.stringify(
|
||||||
[
|
[
|
||||||
`(${errors[0].type})`,
|
// `(${errors[0].type})`,
|
||||||
`[${errors[0].errors.issues[0].path[0]}]`,
|
`(${errors[0].type}: ${errors[0].errors.issues[0].path[0]})`,
|
||||||
|
errors[0].value && `[${errors[0].value}]`,
|
||||||
errors[0].errors.issues[0].message,
|
errors[0].errors.issues[0].message,
|
||||||
].join(' ')
|
]
|
||||||
|
.filter((x) => x)
|
||||||
|
.join(' ')
|
||||||
);
|
);
|
||||||
|
|
||||||
throw ApiError.badRequest(message);
|
throw ApiError.badRequest(message);
|
||||||
@@ -87,8 +113,12 @@ export const paginationValidation = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const idValidation = {
|
export const serverUrlIdValidation = {
|
||||||
id: z.string().uuid(),
|
serverUrlId: z.optional(z.string().uuid()),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const idValidation = (property: string) => {
|
||||||
|
return { [property]: z.string().uuid() };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const serverFolderIdValidation = {
|
export const serverFolderIdValidation = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
|
|
||||||
const list = {
|
const list = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('serverId') }),
|
||||||
query: z.object({
|
query: z.object({
|
||||||
...paginationValidation,
|
...paginationValidation,
|
||||||
...serverFolderIdValidation,
|
...serverFolderIdValidation,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { idValidation } from './shared.validation';
|
|||||||
|
|
||||||
const detail = {
|
const detail = {
|
||||||
body: z.object({}),
|
body: z.object({}),
|
||||||
params: z.object(idValidation),
|
params: z.object({ ...idValidation('id') }),
|
||||||
query: z.object({}),
|
query: z.object({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user