api 설계 변경
This commit is contained in:
@@ -1,22 +0,0 @@
|
|||||||
import type { LoginCtx } from "./ReqLogin.js";
|
|
||||||
import type { SessionCtx } from "./StartSession.js";
|
|
||||||
import type { ProcDecoratorGenerator } from "./base.js";
|
|
||||||
import type { Request, Response } from "express";
|
|
||||||
|
|
||||||
|
|
||||||
export interface UserLevelCtx {
|
|
||||||
userLevel: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ParseUserLevel<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<UserLevelCtx, Q> {
|
|
||||||
return (ctx) => {
|
|
||||||
const userLevel = 123;
|
|
||||||
ctx.setValue('userLevel', userLevel);
|
|
||||||
return [{
|
|
||||||
result: true,
|
|
||||||
}, {
|
|
||||||
userLevel,
|
|
||||||
...ctx,
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +1,35 @@
|
|||||||
import type { LoginCtx } from "./ReqLogin.js";
|
import type { LoginCtx } from "./ReqLogin.js";
|
||||||
import type { SessionCtx } from "./StartSession.js";
|
import type { SessionCtx } from "./StartSession.js";
|
||||||
import type { ProcDecoratorGenerator } from "./base.js";
|
import type { DecoratorResult, ProcDecoratorGenerator } from "./base.js";
|
||||||
import type { Request, Response } from "express";
|
|
||||||
import { delay } from "../../util/delay.js";
|
|
||||||
|
|
||||||
export type GameLoginCtx = {
|
export type GameLoginCtx = {
|
||||||
generalID: number;
|
generalID: number;
|
||||||
|
generalName: string;
|
||||||
|
gameLoginDate: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<GameLoginCtx, Q> {
|
export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<GameLoginCtx, Q> {
|
||||||
return async (inCtx) => {
|
return async (ctx) => {
|
||||||
const ctx: Q & Partial<GameLoginCtx> = inCtx;
|
//NOTE: 게임 서버별로 gameLoginCtx를 따로 가져야 하는가?
|
||||||
if (ctx.generalID !== undefined) {
|
let gameLoginCtx = ctx.session.getItem<GameLoginCtx>(`gameLoginCtx`);
|
||||||
return [{
|
if(gameLoginCtx){
|
||||||
result: true,
|
if(gameLoginCtx.gameLoginDate >= ctx.loginDate){
|
||||||
type: 'GameLogin',
|
return [{
|
||||||
info: 'UseSession',
|
result: true,
|
||||||
}, {
|
},{
|
||||||
generalID: ctx.generalID,
|
...gameLoginCtx,
|
||||||
...inCtx,
|
...ctx,
|
||||||
}, ];
|
}];
|
||||||
|
}
|
||||||
|
gameLoginCtx = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Something GameLogin');
|
//TODO: DB에서 generalID를 가져오는 로직
|
||||||
await delay(0);
|
|
||||||
|
|
||||||
inCtx.setValue('generalID', ctx.userID * 4);
|
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
result: true,
|
result: false,
|
||||||
type: 'GameLogin',
|
type: 'Required GameLogin',
|
||||||
info: 'UseDB',
|
info: 'NotYetImplemented'
|
||||||
}, {
|
}, ctx];
|
||||||
generalID: ctx.userID * 4,
|
|
||||||
...inCtx,
|
|
||||||
}, ]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
import type { SessionCtx } from "./StartSession.js";
|
import type { SessionCtx } from "./StartSession.js";
|
||||||
import type { DecoratorResultFalse, DecoratorResultTrue, ProcDecorator, ProcDecoratorGenerator } from "./base.js";
|
import type { ProcDecorator } from "./base.js";
|
||||||
|
|
||||||
export type LoginCtx = {
|
export type LoginCtx = {
|
||||||
userID: number;
|
userID: number;
|
||||||
|
userName: string;
|
||||||
|
userLevel: number;
|
||||||
|
allowServerAction: Record<string, number>;
|
||||||
|
loginDate: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q> {
|
export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q> {
|
||||||
return (ctx) => {
|
return (ctx) => {
|
||||||
const userID = ctx.userID;
|
const loginCtx = ctx.session.getItem<LoginCtx>('loginCtx');
|
||||||
|
if(!loginCtx){
|
||||||
if (userID === undefined) {
|
|
||||||
return [{
|
return [{
|
||||||
result: false,
|
result: false,
|
||||||
type: 'Required Login',
|
type: 'Required Login',
|
||||||
@@ -17,7 +20,11 @@ export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q>
|
|||||||
}, ctx];
|
}, ctx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [{ result: true }, ctx as Q & LoginCtx];
|
return [{
|
||||||
|
result: true,
|
||||||
|
},{
|
||||||
|
...loginCtx,
|
||||||
|
...ctx,
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,58 @@
|
|||||||
|
import { type Session, type SessionData } from "express-session";
|
||||||
import type { Empty, ProcDecoratorGenerator } from "./base.js";
|
import type { Empty, ProcDecoratorGenerator } from "./base.js";
|
||||||
|
|
||||||
export type SessionCtx = {
|
export type SessionCtx = {
|
||||||
clearSession: () => Promise<void>;
|
session: {
|
||||||
deleteValue: (key: string) => Promise<void>;
|
clear: () => Promise<void>;
|
||||||
getValue: <T>(key: string) => Promise<T | undefined>;
|
removeItem: (key: string) => boolean;
|
||||||
setValue: (key: string, value: unknown) => Promise<void>;
|
getItem: <T>(key: string) => T | undefined;
|
||||||
sessionRaw: {
|
setItem: (key: string, value: unknown) => void;
|
||||||
raw: object;
|
raw: Session & Partial<SessionData> & Record<string, unknown>;
|
||||||
changed: boolean;
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
||||||
return (inCtx) => {
|
return (inCtx, req) => {
|
||||||
const raw: Record<string, unknown> = {};
|
if (!req.session) {
|
||||||
return [{ result: true }, {
|
throw 'Express-session required';
|
||||||
clearSession: () => Promise.resolve(),
|
}
|
||||||
deleteValue: async (key: string) => {
|
const sessionObj = {
|
||||||
console.log('deleteValue', key);
|
raw: req.session
|
||||||
if (raw[key] !== undefined) {
|
} as SessionCtx['session'];
|
||||||
delete raw[key];
|
sessionObj.clear = () => {
|
||||||
}
|
return new Promise((resolve) => {
|
||||||
|
sessionObj.raw = req.session.regenerate(resolve) as SessionCtx['session']['raw'];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sessionObj.removeItem = (key: string): boolean => {
|
||||||
|
if (key in sessionObj.raw) {
|
||||||
|
delete sessionObj.raw[key];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sessionObj.getItem = <T>(key: string): T | undefined => {
|
||||||
|
if (!(key in sessionObj.raw)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return sessionObj.raw[key] as T;
|
||||||
|
}
|
||||||
|
sessionObj.setItem = <T>(key: string, value: T | undefined): void => {
|
||||||
|
if (value === undefined) {
|
||||||
|
sessionObj.removeItem(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sessionObj.raw[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
result: true,
|
||||||
},
|
},
|
||||||
getValue: async <T>(key: string) => {
|
{
|
||||||
return raw[key] as T | undefined;
|
session: sessionObj,
|
||||||
},
|
...inCtx,
|
||||||
setValue: async (key: string, value: unknown) => {
|
}
|
||||||
console.log('setValue', key, value);
|
]
|
||||||
raw[key] = value;
|
|
||||||
},
|
|
||||||
sessionRaw: {
|
|
||||||
raw,
|
|
||||||
changed: false,
|
|
||||||
},
|
|
||||||
...inCtx,
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,9 @@ export type DecoratorResultFalse = {
|
|||||||
export type DecoratorResult = DecoratorResultTrue | DecoratorResultFalse;
|
export type DecoratorResult = DecoratorResultTrue | DecoratorResultFalse;
|
||||||
export type DecoratorStack = DecoratorResult[];
|
export type DecoratorStack = DecoratorResult[];
|
||||||
|
|
||||||
export interface ProcDecorator<Out extends object, In extends object = Record<string, never>> {
|
export interface ProcDecorator<Out extends object, In extends object = Empty> {
|
||||||
(inCtx: In & Partial<Out>, req: Request, res: Response)
|
(inCtx: In & Partial<Out>, req: Request, res: Response)
|
||||||
: MayBePromise<[DecoratorResultTrue, Out]>
|
: MayBePromise<[DecoratorResultTrue, Out] | [DecoratorResultFalse, In & Partial<Out>]>;
|
||||||
| MayBePromise<[DecoratorResultFalse, In & Partial<Out>]>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostProcDecorator<T extends object> {
|
export interface PostProcDecorator<T extends object> {
|
||||||
@@ -50,12 +49,15 @@ export type ResolveChain<T> = T extends undefined ? Empty : T extends ProcDecora
|
|||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type ParseInType<T> = T extends ProcDecorator<any, infer A> ? A : never;
|
export type ParseInType<T> = T extends ProcDecorator<any, infer A> ? object extends A ? A : never : never;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type ParseOutType<T> = T extends ProcDecorator<infer B, any> ? B : never;
|
export type ParseOutType<T> = T extends ProcDecorator<infer B, object> ? B : never;
|
||||||
|
|
||||||
export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T) {
|
export const EmptyProcDecorator: readonly [ProcDecoratorRunner<Empty, Empty>, PostProcDecoratorRunner<Empty>] = [
|
||||||
type InType = ParseInType<PackChain<T>>;
|
async (ctx) => [[], ctx], async (ctx, stack) => [stack, ctx]
|
||||||
|
];
|
||||||
|
|
||||||
|
export function declProcDecorators<T extends ProcDecoratorChain>(...decorators: T) {
|
||||||
type OutType = ParseOutType<PackChain<T>>;
|
type OutType = ParseOutType<PackChain<T>>;
|
||||||
|
|
||||||
const preDecorator: PlainDecorator[] = [];
|
const preDecorator: PlainDecorator[] = [];
|
||||||
@@ -74,7 +76,7 @@ export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const packedDecorators: readonly [ProcDecoratorRunner<OutType, InType>, PostProcDecoratorRunner<OutType>] = [
|
const packedDecorators: readonly [ProcDecoratorRunner<OutType, Empty>, PostProcDecoratorRunner<OutType>] = [
|
||||||
async (ctx, req, res) => {
|
async (ctx, req, res) => {
|
||||||
let rctx = ctx as unknown as OutType;
|
let rctx = ctx as unknown as OutType;
|
||||||
const decoratorStack: DecoratorStack = [];
|
const decoratorStack: DecoratorStack = [];
|
||||||
@@ -111,35 +113,47 @@ export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T)
|
|||||||
return [decoratorStack, rctx];
|
return [decoratorStack, rctx];
|
||||||
},
|
},
|
||||||
async (ctx, stack, req, res) => {
|
async (ctx, stack, req, res) => {
|
||||||
if (!postDecorator) {
|
if (!postDecorator.length) {
|
||||||
return [stack, ctx];
|
return [stack, ctx];
|
||||||
}
|
}
|
||||||
const isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result);
|
let isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result);
|
||||||
while (stack.length > 0) {
|
for (let idx = stack.length - 1; idx >= 0; idx--) {
|
||||||
const preStackResult = stack.pop() as DecoratorResult;
|
const preStackResult = stack[idx] as DecoratorResult;
|
||||||
const idx = stack.length;
|
if (!preStackResult.result) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const proc = postDecorator[idx];
|
const proc = postDecorator[idx];
|
||||||
|
|
||||||
if (!proc) {
|
if (!proc) {
|
||||||
|
if (isValidRoute) {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute);
|
const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute);
|
||||||
if (!postStackResult.result) {
|
if (postStackResult.result) {
|
||||||
stack.push(postStackResult);
|
if (isValidRoute) {
|
||||||
return [stack, nextCtx];
|
stack.pop();
|
||||||
|
}
|
||||||
|
ctx = nextCtx;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValidRoute = false;
|
||||||
|
stack[idx] = postStackResult;
|
||||||
|
|
||||||
ctx = nextCtx;
|
ctx = nextCtx;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
stack.push({
|
isValidRoute = false;
|
||||||
|
stack[idx] = {
|
||||||
result: false,
|
result: false,
|
||||||
type: 'PostThrow',
|
type: 'PostThrow',
|
||||||
info: `internal error: ${e}`,
|
info: `internal error: ${e}`,
|
||||||
});
|
};
|
||||||
return [stack, ctx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -150,7 +164,7 @@ export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T)
|
|||||||
return packedDecorators;
|
return packedDecorators;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
type Compose2<B extends object, A extends object, D extends object, C> = B extends C ? ProcDecorator<D & B, A> : never;
|
||||||
|
|
||||||
type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -166,4 +180,4 @@ export type PackChain<T> =
|
|||||||
T extends readonly [PD2<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
T extends readonly [PD2<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||||
never;
|
never;
|
||||||
|
|
||||||
type Resolve<T> = T extends ProcDecorator<infer B, infer A> ? Empty extends A ? B : never : never;
|
type Resolve<T> = T extends ProcDecorator<infer B, infer A> ? Empty extends A ? B : never : never;
|
||||||
+3
-8
@@ -1,13 +1,8 @@
|
|||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import type { Callable, DefAPINamespace, DeleteAPICallT, GetAPICallT, HeadAPICallT, HttpMethod, InvalidResponse, PatchAPICallT, PostAPICallT, PutAPICallT, RawArgType, ValidResponse, recoveryMethod } from '../apiStructure/defs.js';
|
import type { Callable, DefAPINamespace, DeleteAPICallT, GetAPICallT, HeadAPICallT, HttpMethod, InvalidResponse, PatchAPICallT, PostAPICallT, PutAPICallT, RawArgType, ValidResponse, recoveryMethod } from '../apiStructure/defs.js';
|
||||||
import {
|
import type { Empty, PostProcDecoratorRunner, ProcDecoratorRunner } from './ProcDecorator/base.js';
|
||||||
validate,
|
import type { ZodType } from 'zod';
|
||||||
type ValidatorOptions,
|
|
||||||
} from "class-validator";
|
|
||||||
import { type ClassTransformOptions, plainToInstance } from "class-transformer";
|
|
||||||
import type { Empty, ParseOutType, PostProcDecoratorRunner, ProcDecorator, ProcDecoratorChain, ProcDecoratorRunner, ResolveChain } from './ProcDecorator/base.js';
|
|
||||||
import { clamp } from 'lodash-es';
|
|
||||||
|
|
||||||
export type APINamespace = {
|
export type APINamespace = {
|
||||||
[key: string]: APINamespace
|
[key: string]: APINamespace
|
||||||
@@ -26,7 +21,7 @@ export type APINamespace = {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidatorType<T> = T extends undefined ? undefined : ClassType<T>;
|
type ValidatorType<T> = T extends undefined ? undefined : ZodType<T>;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type AnyAPIExecuter = APIExecuter<any, any, any, any>;
|
export type AnyAPIExecuter = APIExecuter<any, any, any, any>;
|
||||||
|
|||||||
+47
-67
@@ -1,53 +1,48 @@
|
|||||||
import { Router, type Request, type Response } from 'express';
|
import { Router, type Request, type Response } from 'express';
|
||||||
import type { AnyAPIExecuter, APINamespace, ClassType } from './defs.js';
|
import type { AnyAPIExecuter, APINamespace } from './defs.js';
|
||||||
import { plainToInstance } from 'class-transformer';
|
import type { RawArgType } from '../apiStructure/defs.js';
|
||||||
import { validate } from 'class-validator';
|
import { z } from 'zod';
|
||||||
import type { ExtractQuery, RawArgType } from '../apiStructure/defs.js';
|
|
||||||
import type { DecoratorResult, Empty, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js';
|
|
||||||
import clamp from 'lodash-es/clamp.js';
|
|
||||||
|
|
||||||
async function parseParam<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: ClassType<Q>): Promise<Q> {
|
async function parseParam<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: z.ZodType<Q>): Promise<z.SafeParseReturnType<Q, Q>> {
|
||||||
if (!argValidator) {
|
if (!argValidator) {
|
||||||
return req.query as Q;
|
return {
|
||||||
|
success: true,
|
||||||
|
data: req.query as Q
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = req.query;
|
const query = req.query;
|
||||||
const classObject = plainToInstance(argValidator, query);
|
return await argValidator.safeParseAsync(query);
|
||||||
const errors = await validate(classObject);
|
|
||||||
if (errors.length) {
|
|
||||||
throw errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
return classObject as Q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseBody<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: ClassType<Q>): Promise<Q> {
|
async function parseBody<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: z.ZodType<Q>): Promise<z.SafeParseReturnType<Q, Q>> {
|
||||||
if (!argValidator) {
|
if (!argValidator) {
|
||||||
return req.body as Q;
|
return {
|
||||||
|
success: true,
|
||||||
|
data: req.body as Q
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = req.body;
|
const query = req.body;
|
||||||
const classObject = plainToInstance(argValidator, query);
|
return await argValidator.safeParseAsync(query);
|
||||||
const errors = await validate(classObject);
|
|
||||||
if (errors.length) {
|
|
||||||
throw errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
return classObject as Q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExecuter): Promise<void> {
|
async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExecuter): Promise<void> {
|
||||||
const [preResult, ctx] = await api.preDecorator({}, req, res);
|
const [preResult, ctx] = await api.preDecorator({}, req, res);
|
||||||
if (!preResult.every((v) => v.result)) {
|
if (!preResult.every((v) => v.result)) {
|
||||||
const lastErr = preResult[preResult.length - 1];
|
const lastErr = preResult.pop() as typeof preResult[0];
|
||||||
|
|
||||||
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, false);
|
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, false);
|
||||||
if (!postResult.every((v) => v.result)) {
|
const postErrors = postResult.filter((obj)=>!obj.result);
|
||||||
|
if (postErrors.length) {
|
||||||
//회수조차 불가능?
|
//회수조차 불가능?
|
||||||
const lastPostErr = postResult[postResult.length - 1];
|
const postErrorsStr = postErrors.map((obj)=>{
|
||||||
|
return `(${obj.type})${obj.info}`
|
||||||
|
}).join(', ');
|
||||||
res.json({
|
res.json({
|
||||||
result: false,
|
result: false,
|
||||||
path: req.path,
|
path: req.path,
|
||||||
reason: `preDecorator: ${lastErr.type} ${lastErr.info}, postDecorator: ${lastPostErr.type} ${lastPostErr.info}`,
|
reason: `preDecorator: (${lastErr.type})${lastErr.info}, postDecorator: ${postErrorsStr}`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,22 +50,25 @@ async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExe
|
|||||||
res.json({
|
res.json({
|
||||||
result: false,
|
result: false,
|
||||||
path: req.path,
|
path: req.path,
|
||||||
reason: `preDecorator: ${lastErr.type} ${lastErr.info}`,
|
reason: `preDecorator: (${lastErr.type})${lastErr.info}`,
|
||||||
})
|
})
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await api(query, ctx, req, res);
|
const result = await api(query, ctx, req, res);
|
||||||
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, true);
|
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, true);
|
||||||
if(!postResult.every((v) => v.result)) {
|
const postErrors = postResult.filter((obj)=>!obj.result);
|
||||||
|
if(postErrors.length) {
|
||||||
if(result === true) {
|
if(result === true) {
|
||||||
//NOTE: 이미 api에서 response를 보낸 특이 케이스.
|
//NOTE: 이미 api에서 response를 보낸 특이 케이스.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const lastPostErr = postResult[postResult.length - 1];
|
const postErrorsStr = postErrors.map((obj)=>{
|
||||||
|
return `(${obj.type})${obj.info}`
|
||||||
|
}).join(', ');
|
||||||
res.json({
|
res.json({
|
||||||
result: false,
|
result: false,
|
||||||
path: req.path,
|
path: req.path,
|
||||||
reason: `postDecorator: ${lastPostErr.type} ${lastPostErr.info}`,
|
reason: `postDecorator: ${postErrorsStr}`,
|
||||||
originalResult: result,
|
originalResult: result,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -89,8 +87,10 @@ export function buildAPISystem<N extends APINamespace, Q extends AnyAPIExecuter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(api)) {
|
for (const [key, value] of Object.entries(api)) {
|
||||||
|
const rkey = `/${key}`;
|
||||||
if (typeof value !== 'function') {
|
if (typeof value !== 'function') {
|
||||||
router.use(key, buildAPISystem(value));
|
router.use(rkey, buildAPISystem(value));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const executer = value;
|
const executer = value;
|
||||||
@@ -99,40 +99,20 @@ export function buildAPISystem<N extends APINamespace, Q extends AnyAPIExecuter>
|
|||||||
throw new Error('APIExecuter.reqType is not defined');
|
throw new Error('APIExecuter.reqType is not defined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (executer.httpMethod === 'get') {
|
const parser = executer.httpMethod === 'get' ? parseParam : parseBody;
|
||||||
router[executer.httpMethod](key, async (req, res) => {
|
router[executer.httpMethod](rkey, async (req, res) => {
|
||||||
let query: ExtractQuery<Q>;
|
const queryResult = await parser(req, executer.argValidator);
|
||||||
try {
|
if(!queryResult.success){
|
||||||
query = await parseParam(req, executer.argValidator);
|
res.json({
|
||||||
}
|
result: false,
|
||||||
catch (e) {
|
reason: `invalid parameter: ${queryResult.error.message}`,
|
||||||
res.json({
|
error: queryResult.error
|
||||||
result: false,
|
});
|
||||||
reason: `invalid parameter: ${e}`,
|
return;
|
||||||
});
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await apiRun(query, req, res, executer);
|
await apiRun(queryResult.data, req, res, executer);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
else {
|
|
||||||
router[executer.httpMethod](key, async (req, res) => {
|
|
||||||
let query: ExtractQuery<Q>;
|
|
||||||
try {
|
|
||||||
query = await parseBody(req, executer.argValidator);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
res.json({
|
|
||||||
result: false,
|
|
||||||
reason: `invalid parameter: ${e}`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await apiRun(query, req, res, executer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user