mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 09:15:19 +03:00
feat: Sync to OSS repo
This commit is contained in:
1
apps/api/app/utils/auth.ts
Normal file
1
apps/api/app/utils/auth.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { auth, notPublic, adminOnly, machineOnly, machineOrAdmin } from '../middleware/auth.js';
|
||||
127
apps/api/app/utils/error.ts
Normal file
127
apps/api/app/utils/error.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { ErrorResponse } from '@nestri/core/error';
|
||||
import { resolver } from 'hono-openapi/zod';
|
||||
|
||||
export const ErrorResponses = {
|
||||
400: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Validation error',
|
||||
example: {
|
||||
type: 'validation',
|
||||
code: 'invalid_parameter',
|
||||
message: 'The request was invalid',
|
||||
param: 'email'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description:
|
||||
'Bad Request - The request could not be understood or was missing required parameters.'
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Authentication error',
|
||||
example: {
|
||||
type: 'authentication',
|
||||
code: 'unauthorized',
|
||||
message: 'Authentication required'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description:
|
||||
'Unauthorized - Authentication is required and has failed or has not been provided.'
|
||||
},
|
||||
403: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Permission error',
|
||||
example: {
|
||||
type: 'forbidden',
|
||||
code: 'permission_denied',
|
||||
message: 'You do not have permission to access this resource'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description: 'Forbidden - You do not have permission to access this resource.'
|
||||
},
|
||||
404: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Not found error',
|
||||
example: {
|
||||
type: 'not_found',
|
||||
code: 'resource_not_found',
|
||||
message: 'The requested resource could not be found'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description: 'Not Found - The requested resource does not exist.'
|
||||
},
|
||||
409: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Conflict Error',
|
||||
example: {
|
||||
type: 'already_exists',
|
||||
code: 'resource_already_exists',
|
||||
message: 'The resource could not be created because it already exists'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description: 'Conflict - The resource could not be created because it already exists.'
|
||||
},
|
||||
429: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Rate limit error',
|
||||
example: {
|
||||
type: 'rate_limit',
|
||||
code: 'too_many_requests',
|
||||
message: 'Rate limit exceeded'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description: 'Too Many Requests - You have made too many requests in a short period of time.'
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resolver(
|
||||
ErrorResponse.meta({
|
||||
description: 'Server error',
|
||||
example: {
|
||||
type: 'internal',
|
||||
code: 'internal_error',
|
||||
message: 'Internal server error'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
description: 'Internal Server Error - Something went wrong on our end.'
|
||||
}
|
||||
};
|
||||
64
apps/api/app/utils/hook.ts
Normal file
64
apps/api/app/utils/hook.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
Env,
|
||||
ValidationTargets,
|
||||
Context,
|
||||
TypedResponse,
|
||||
Input,
|
||||
MiddlewareHandler
|
||||
} from 'hono';
|
||||
import { ZodError, ZodSchema, z } from 'zod';
|
||||
|
||||
type Hook<
|
||||
T,
|
||||
E extends Env,
|
||||
P extends string,
|
||||
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
||||
O = {}
|
||||
> = (
|
||||
result: (
|
||||
| {
|
||||
success: true;
|
||||
data: T;
|
||||
}
|
||||
| {
|
||||
success: false;
|
||||
error: ZodError;
|
||||
data: T;
|
||||
}
|
||||
) & {
|
||||
target: Target;
|
||||
},
|
||||
c: Context<E, P>
|
||||
) => Response | void | TypedResponse<O> | Promise<Response | void | TypedResponse<O>>;
|
||||
type HasUndefined<T> = undefined extends T ? true : false;
|
||||
declare const zValidator: <
|
||||
T extends ZodSchema<any, z.ZodTypeDef, any>,
|
||||
Target extends keyof ValidationTargets,
|
||||
E extends Env,
|
||||
P extends string,
|
||||
In = z.input<T>,
|
||||
Out = z.output<T>,
|
||||
I extends Input = {
|
||||
in: HasUndefined<In> extends true
|
||||
? {
|
||||
[K in Target]?:
|
||||
| (In extends ValidationTargets[K]
|
||||
? In
|
||||
: { [K2 in keyof In]?: ValidationTargets[K][K2] | undefined })
|
||||
| undefined;
|
||||
}
|
||||
: {
|
||||
[K_1 in Target]: In extends ValidationTargets[K_1]
|
||||
? In
|
||||
: { [K2_1 in keyof In]: ValidationTargets[K_1][K2_1] };
|
||||
};
|
||||
out: { [K_2 in Target]: Out };
|
||||
},
|
||||
V extends I = I
|
||||
>(
|
||||
target: Target,
|
||||
schema: T,
|
||||
hook?: Hook<z.TypeOf<T>, E, P, Target, {}> | undefined
|
||||
) => MiddlewareHandler<E, P, V>;
|
||||
|
||||
export { type Hook, zValidator };
|
||||
4
apps/api/app/utils/index.ts
Normal file
4
apps/api/app/utils/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './auth';
|
||||
export * from './error';
|
||||
export * from './result';
|
||||
export * from './validator';
|
||||
6
apps/api/app/utils/result.ts
Normal file
6
apps/api/app/utils/result.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { resolver } from 'hono-openapi/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function Result<T extends z.ZodTypeAny>(schema: T) {
|
||||
return resolver(z.object({ data: schema }));
|
||||
}
|
||||
70
apps/api/app/utils/validator.ts
Normal file
70
apps/api/app/utils/validator.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ErrorCodes } from '@nestri/core/error';
|
||||
import type { MiddlewareHandler, ValidationTargets } from 'hono';
|
||||
import { validator as zodValidator } from 'hono-openapi/zod';
|
||||
import { z, ZodSchema } from 'zod';
|
||||
|
||||
import type { Hook } from './hook';
|
||||
|
||||
type ZodIssueExtended = z.ZodIssue & {
|
||||
expected?: unknown;
|
||||
received?: unknown;
|
||||
};
|
||||
|
||||
export const validator = <T extends ZodSchema, Target extends keyof ValidationTargets>(
|
||||
target: Target,
|
||||
schema: T
|
||||
): MiddlewareHandler<
|
||||
Record<string, unknown>,
|
||||
string,
|
||||
{
|
||||
in: {
|
||||
[K in Target]: z.input<T>;
|
||||
};
|
||||
out: {
|
||||
[K in Target]: z.output<T>;
|
||||
};
|
||||
}
|
||||
> => {
|
||||
const standardErrorHandler: Hook<z.infer<T>, any, any, Target> = (result, c) => {
|
||||
if (!result.success) {
|
||||
const issues = result.error.issues || result.error.errors || [];
|
||||
const firstIssue = issues[0];
|
||||
const fieldPath = Array.isArray(firstIssue?.path)
|
||||
? firstIssue.path.join('.')
|
||||
: firstIssue?.path;
|
||||
|
||||
let errorCode = ErrorCodes.Validation.INVALID_PARAMETER;
|
||||
if (firstIssue?.code === 'invalid_type' && firstIssue?.received === 'undefined') {
|
||||
errorCode = ErrorCodes.Validation.MISSING_REQUIRED_FIELD;
|
||||
} else if (
|
||||
['invalid_string', 'invalid_date', 'invalid_regex'].includes(firstIssue?.code as string)
|
||||
) {
|
||||
errorCode = ErrorCodes.Validation.INVALID_FORMAT;
|
||||
}
|
||||
|
||||
const response = {
|
||||
type: 'validation',
|
||||
code: errorCode,
|
||||
message: firstIssue?.message,
|
||||
param: fieldPath,
|
||||
details:
|
||||
issues.length > 1
|
||||
? {
|
||||
issues: issues.map((issue: ZodIssueExtended) => ({
|
||||
path: Array.isArray(issue.path) ? issue.path.join('.') : issue.path,
|
||||
code: issue.code,
|
||||
message: issue.message,
|
||||
expected: issue.expected,
|
||||
received: issue.received
|
||||
}))
|
||||
}
|
||||
: undefined
|
||||
};
|
||||
|
||||
console.log('Validation error in validator:', response);
|
||||
return c.json(response, 400);
|
||||
}
|
||||
};
|
||||
|
||||
return zodValidator(target, schema, standardErrorHandler);
|
||||
};
|
||||
Reference in New Issue
Block a user