diff --git a/server/api/Login/ChangePassword.ts b/server/api/Login/ChangePassword.ts index ef82b79..745dbf7 100644 --- a/server/api/Login/ChangePassword.ts +++ b/server/api/Login/ChangePassword.ts @@ -1,16 +1,17 @@ import { GET } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import { declProcDecorators } from "../ProcDecorator/base"; type BaseAPI = typeof structure.Login.ReqNonce; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; const argValidator = undefined; -const procDecorator = [] as const; -export const ReqNonce = GET(argValidator)(procDecorator)( - (query, ctx, req, res) => { +export const ReqNonce = GET + (argValidator) + (declProcDecorators([] as const)) + (() => { throw new Error("Method not implemented."); - } -); \ No newline at end of file + }); \ No newline at end of file diff --git a/server/api/Login/LoginByID.ts b/server/api/Login/LoginByID.ts index 109e724..b9dfa75 100644 --- a/server/api/Login/LoginByID.ts +++ b/server/api/Login/LoginByID.ts @@ -4,6 +4,7 @@ import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStruc import { StartSession } from "../ProcDecorator/StartSession"; import { IsString } from "class-validator"; import { delay } from "../../util/delay"; +import { declProcDecorators } from "../ProcDecorator/base"; type BaseAPI = typeof structure.Login.LoginByID; type RType = ExtractResponse; @@ -15,19 +16,20 @@ class ArgValidator implements QType { @IsString() password!: string; } -const procDecorator = [ - StartSession, -] as const; -export const LoginByID = POST(ArgValidator)(procDecorator)( - async (query, ctx, req, res) => { +export const LoginByID = POST + (ArgValidator) + (declProcDecorators([ + StartSession, + ] as const)) + (async (query, ctx, req, res) => { const username = query.username; const password = query.password; //TODO: DB에서 뭔가 가져와야 함 await delay(1); - if(Math.random() < 0.3){ + if (Math.random() < 0.3) { return { result: false, reason: "로그인 실패", @@ -35,7 +37,7 @@ export const LoginByID = POST(ArgValidator)(procDecorator)( } } - if(Math.random() < 0.5){ + if (Math.random() < 0.5) { return { result: false, reason: "OTP 인증 필요", @@ -51,5 +53,4 @@ export const LoginByID = POST(ArgValidator)(procDecorator)( result: true, nextToken, } - } -); \ No newline at end of file + }); \ No newline at end of file diff --git a/server/api/Login/LoginByToken.ts b/server/api/Login/LoginByToken.ts index 892cb94..156cd38 100644 --- a/server/api/Login/LoginByToken.ts +++ b/server/api/Login/LoginByToken.ts @@ -4,22 +4,25 @@ import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStruc import { StartSession } from "../ProcDecorator/StartSession"; import { IsNumber, IsString } from "class-validator"; import { delay } from "../../util/delay"; +import { PackChain, ParseInType, ParseOutType, declProcDecorators } from "../ProcDecorator/base"; type BaseAPI = typeof structure.Login.LoginByToken; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; + class ArgValidator implements QType { @IsNumber() token_id!: number; @IsString() hashedToken!: string; } -const procDecorator = [ - StartSession, -] as const; -export const LoginByToken = POST(ArgValidator)(procDecorator)( - async (query, ctx) => { +export const LoginByToken = POST + (ArgValidator) + (declProcDecorators([ + StartSession, + ] as const)) + (async (query, ctx) => { query.hashedToken; ctx.clearSession(); @@ -38,5 +41,4 @@ export const LoginByToken = POST(ArgValidator)(procDecorato nextToken, } - } -); \ No newline at end of file + }); \ No newline at end of file diff --git a/server/api/Login/ReqNonce.ts b/server/api/Login/ReqNonce.ts index dded3b1..17b390c 100644 --- a/server/api/Login/ReqNonce.ts +++ b/server/api/Login/ReqNonce.ts @@ -1,21 +1,22 @@ -import { type APINamespaceType, GET as GET } from "../defs"; +import { GET } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; -import type { DefAPINamespace, ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; import { StartSession } from "../ProcDecorator/StartSession"; +import { declProcDecorators } from "../ProcDecorator/base"; type BaseAPI = typeof structure.Login.ReqNonce; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; -const argValidator = undefined; -const procDecorator = [ - StartSession, -] as const; -export const ReqNonce = GET(argValidator)(procDecorator)( - async (query, ctx) => { +export const ReqNonce = GET + (undefined) + (declProcDecorators([ + StartSession, + ] as const)) + (async (query, ctx) => { const nonce = await ctx.getValue("nonce"); - if(nonce !== undefined){ + if (nonce !== undefined) { return { loginNonce: nonce, result: true, @@ -28,8 +29,4 @@ export const ReqNonce = GET(argValidator)(procDecorator)( loginNonce: newNonce, result: true, } - } -); - -type BaseAPI2 = APINamespaceType['ReqNonce']; -type A = typeof ReqNonce; \ No newline at end of file + }); \ No newline at end of file diff --git a/server/api/Login/test.ts b/server/api/Login/test.ts index 32f6981..e3fe590 100644 --- a/server/api/Login/test.ts +++ b/server/api/Login/test.ts @@ -1,14 +1,16 @@ import { GET } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import { declProcDecorators } from "../ProcDecorator/base"; type BaseAPI = typeof structure.Login.test; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; -export const test = GET(undefined)(undefined)( - (query, ctx, req, res) => { +export const test = GET + (undefined) + (declProcDecorators([] as const)) + (() => { throw new Error("Method not implemented."); - } -); \ No newline at end of file + }); \ No newline at end of file diff --git a/server/api/ProcDecorator/ParseUserLevel.ts b/server/api/ProcDecorator/ParseUserLevel.ts index fcd7006..15a6ee0 100644 --- a/server/api/ProcDecorator/ParseUserLevel.ts +++ b/server/api/ProcDecorator/ParseUserLevel.ts @@ -9,12 +9,14 @@ export interface UserLevelCtx { } export function ParseUserLevel(): ProcDecoratorGenerator { - return (inCtx) => { + return (ctx) => { const userLevel = 123; - inCtx.setValue('userLevel', userLevel); - return { + ctx.setValue('userLevel', userLevel); + return [{ + result: true, + }, { userLevel, - ...inCtx, - } + ...ctx, + }]; } } \ No newline at end of file diff --git a/server/api/ProcDecorator/ReqGameLogin.ts b/server/api/ProcDecorator/ReqGameLogin.ts index 2d594f6..ff14636 100644 --- a/server/api/ProcDecorator/ReqGameLogin.ts +++ b/server/api/ProcDecorator/ReqGameLogin.ts @@ -12,10 +12,14 @@ export function ReqGameLogin(): ProcDecoratorGe return async (inCtx) => { const ctx: Q & Partial = inCtx; if (ctx.generalID !== undefined) { - return { + return [{ + result: true, + type: 'GameLogin', + info: 'UseSession', + }, { generalID: ctx.generalID, ...inCtx, - }; + }, ]; } console.log('Something GameLogin'); @@ -23,9 +27,13 @@ export function ReqGameLogin(): ProcDecoratorGe inCtx.setValue('generalID', ctx.userID * 4); - return { + return [{ + result: true, + type: 'GameLogin', + info: 'UseDB', + }, { generalID: ctx.userID * 4, ...inCtx, - } + }, ] } } \ No newline at end of file diff --git a/server/api/ProcDecorator/ReqLogin.ts b/server/api/ProcDecorator/ReqLogin.ts index 95aa66c..5c0b29b 100644 --- a/server/api/ProcDecorator/ReqLogin.ts +++ b/server/api/ProcDecorator/ReqLogin.ts @@ -1,23 +1,23 @@ import type { SessionCtx } from "./StartSession"; -import type { InvalidProc, ProcDecoratorGenerator } from "./base"; +import type { DecoratorResultFalse, DecoratorResultTrue, ProcDecorator, ProcDecoratorGenerator } from "./base"; export type LoginCtx = { userID: number; } -export function ReqLogin(): ProcDecoratorGenerator { - return (inCtx) => { - const ctx: Q & Partial = inCtx; +export function ReqLogin(): ProcDecorator { + return (ctx) => { const userID = ctx.userID; if (userID === undefined) { - return { + return [{ result: false, - reason: 'Required Login', - invalidProcSymbol: 'ReqLogin' - } satisfies InvalidProc; + type: 'Required Login', + info: 'ReqLogin' + }, ctx]; } - return ctx as LoginCtx & Q; + return [{ result: true }, ctx as Q & LoginCtx]; + } } \ No newline at end of file diff --git a/server/api/ProcDecorator/StartSession.ts b/server/api/ProcDecorator/StartSession.ts index 8466b00..e7b57f5 100644 --- a/server/api/ProcDecorator/StartSession.ts +++ b/server/api/ProcDecorator/StartSession.ts @@ -14,7 +14,7 @@ export type SessionCtx = { export function StartSession(): ProcDecoratorGenerator { return (inCtx) => { const raw: Record = {}; - return { + return [{ result: true }, { clearSession: () => Promise.resolve(), deleteValue: async (key: string) => { console.log('deleteValue', key); @@ -34,6 +34,6 @@ export function StartSession(): ProcDecoratorGenerator changed: false, }, ...inCtx, - }; + }]; } } \ No newline at end of file diff --git a/server/api/ProcDecorator/base.ts b/server/api/ProcDecorator/base.ts index 2e9e1cb..eb8bb7e 100644 --- a/server/api/ProcDecorator/base.ts +++ b/server/api/ProcDecorator/base.ts @@ -1,131 +1,153 @@ import type { Request, Response } from "express"; -import type { InvalidResponse } from "../../apiStructure/defs"; type MayBePromise = T | Promise; export type Empty = Record; -export type DecoratorStack = { - result: boolean, - type: string, - info: string, -}[]; - +export type DecoratorResultTrue = { + result: true; + type?: string; + info?: string; +}; +export type DecoratorResultFalse = { + result: false; + type: string; + info: string; +} +export type DecoratorResult = DecoratorResultTrue | DecoratorResultFalse; +export type DecoratorStack = DecoratorResult[]; export interface ProcDecorator> { - (inCtx: In & Partial, req: Request, res: Response): MayBePromise<[Out, DecoratorStack]>; + (inCtx: In & Partial, req: Request, res: Response) + : MayBePromise<[DecoratorResultTrue, Out]> + | MayBePromise<[DecoratorResultFalse, In & Partial]>; } -export interface PostProcDecorator{ - (ctx: T, stack: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[T, DecoratorStack]>; +export interface PostProcDecorator { + (ctx: T, preResult: DecoratorResult, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorResult, T]>; } +export interface ProcDecoratorRunner { + (inCtx: In & Partial, req: Request, res: Response): MayBePromise<[DecoratorStack, Out]>; +} + +export interface PostProcDecoratorRunner { + (ctx: T, preResult: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorStack, T]>; +} + + +// eslint-disable-next-line @typescript-eslint/no-explicit-any type PlainDecorator = ProcDecorator; +// eslint-disable-next-line @typescript-eslint/no-explicit-any type PlainPostDecorator = PostProcDecorator; export type ProcDecoratorGenerator = ProcDecorator; export type ProcDecoratorPrePostGenerator = [ProcDecorator, PostProcDecorator]; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ProcDecoratorChain = undefined | readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[]; +export type ProcDecoratorChain = readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[]; export type ResolveChain = T extends undefined ? Empty : T extends ProcDecoratorChain ? Resolve> : never; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ParseInType = T extends ProcDecorator ? A : never; -type ParseOutType = T extends ProcDecorator ? B : never; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ParseInType = T extends ProcDecorator ? A : never; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ParseOutType = T extends ProcDecorator ? B : never; export function declProcDecorators(decorators: T) { type InType = ParseInType>; type OutType = ParseOutType>; const preDecorator: PlainDecorator[] = []; - const postDecorator: (PlainDecorator | undefined)[] = []; + const postDecorator: (PlainPostDecorator | undefined)[] = []; if (decorators) { for (const procGen of decorators) { const proc = procGen(); if (Array.isArray(proc)) { preDecorator.push(proc[0]); - postDecorator.push(undefined); + postDecorator.push(proc[1]); } else { preDecorator.push(proc); - postDecorator.push(proc); + postDecorator.push(undefined); } } } - const packedDecorators: readonly [ProcDecorator, PostProcDecorator] = [ + const packedDecorators: readonly [ProcDecoratorRunner, PostProcDecoratorRunner] = [ async (ctx, req, res) => { let rctx = ctx as unknown as OutType; const decoratorStack: DecoratorStack = []; if (!preDecorator) { - return [rctx, decoratorStack]; - } + return [decoratorStack, rctx]; + } for (const [idx, proc] of preDecorator.entries()) { try { - const [newCtx, [stackResult]] = await proc(rctx, req, res); + const [stackResult, newCtx] = await proc(rctx, req, res); decoratorStack.push(stackResult); - if(stackResult.result){ + if (stackResult.result) { rctx = newCtx; continue; } - return [newCtx, decoratorStack]; + return [decoratorStack, newCtx]; } catch (e) { - - return [{ + while (decoratorStack.length > idx) { + decoratorStack.pop(); + } + decoratorStack.push({ result: false, - reason: `internal error[${idx}]: ${e}`, - invalidProcInfo: [['PreThrow', idx]], - }, rctx]; + type: 'PreThrow', + info: `internal error: ${e}`, + }); + + return [decoratorStack, rctx]; } } - return [rctx, decoratorStack]; + return [decoratorStack, rctx]; }, async (ctx, stack, req, res) => { + if (!postDecorator) { + return [stack, ctx]; + } + const isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result); + while (stack.length > 0) { + const preStackResult = stack.pop() as DecoratorResult; + const idx = stack.length; + const proc = postDecorator[idx]; - return [ctx, stack]; + if (!proc) { + continue; + } + + try { + const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute); + if (!postStackResult.result) { + stack.push(postStackResult); + return [stack, nextCtx]; + } + + ctx = nextCtx; + } + catch (e) { + stack.push({ + result: false, + type: 'PostThrow', + info: `internal error: ${e}`, + }); + return [stack, ctx]; + } + + } + return [stack, ctx]; }, ] as const; return packedDecorators; - - return [ - async (ctx: InType & Partial, req: Request, res: Response) => { - return ctx as unknown as OutType; - /*if (!preDecorator) { - return ctx as Result; - } - - for (const [idx, proc] of preDecorator.entries()) { - try { - const result = await proc(ctx, req, res, true) as Result | [InvalidProc, object]; - if (Array.isArray(result)) { - const [invalidProc, erroredCtx] = result; - invalidProc.invalidProcInfo.push(['Pre', idx]); - throw [invalidProc, erroredCtx as Partial] - return ; - } - ctx = result; - } - catch (e) { - return [{ - result: false, - reason: `internal error[${idx}]: ${e}`, - invalidProcSymbol: ['preCtx', idx], - }, ctx, idx]; - } - } - return ctx as ResolveChain;*/ - }, - async (ctx, req, res) => ctx - ] } type Compose2 = D & B extends A & C ? B extends C ? ProcDecorator : never : never; @@ -134,7 +156,7 @@ type PD1 = () => ProcDecorator; // eslint-disable-next-line @typescript-eslint/no-explicit-any type PD2 = () => [ProcDecorator, PostProcDecorator]; -type PackChain = +export type PackChain = T extends readonly [] ? ProcDecorator : T extends readonly [PD1] ? ProcDecorator : T extends readonly [PD2] ? ProcDecorator : diff --git a/server/api/defs.ts b/server/api/defs.ts index 18ec97a..8d6b2dc 100644 --- a/server/api/defs.ts +++ b/server/api/defs.ts @@ -6,7 +6,7 @@ import { type ValidatorOptions, } from "class-validator"; import { type ClassTransformOptions, plainToInstance } from "class-transformer"; -import type { InvalidProc, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js'; +import type { Empty, ParseOutType, PostProcDecoratorRunner, ProcDecorator, ProcDecoratorChain, ProcDecoratorRunner, ResolveChain } from './ProcDecorator/base.js'; import { clamp } from 'lodash-es'; export type APINamespace = { @@ -31,59 +31,44 @@ type ValidatorType = T extends undefined ? undefined : ClassType; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AnyAPIExecuter = APIExecuter; -interface APIExecuter { - (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response): Promise; +interface APIExecuter { + (query: Q, ctx: CTX, expressReq: Request, expressRes: Response): Promise; httpMethod: HttpMethod; argValidator?: ValidatorType; - preDecorator: ProcDecorator[]; - postDecorator: (ProcDecorator | undefined)[]; + preDecorator: ProcDecoratorRunner; + postDecorator: PostProcDecoratorRunner; } -export interface iAPI_GET extends APIExecuter { +export interface iAPI_GET extends APIExecuter { httpMethod: 'get'; } -export interface iAPI_POST extends APIExecuter { +export interface iAPI_POST extends APIExecuter { httpMethod: 'post'; } -export interface iAPI_PUT extends APIExecuter { +export interface iAPI_PUT extends APIExecuter { httpMethod: 'put'; } -export interface iAPI_DELETE extends APIExecuter { +export interface iAPI_DELETE extends APIExecuter { httpMethod: 'delete'; } -export interface iAPI_PATCH extends APIExecuter { +export interface iAPI_PATCH extends APIExecuter { httpMethod: 'patch'; } -export interface iAPI_HEAD extends APIExecuter { +export interface iAPI_HEAD extends APIExecuter { httpMethod: 'head'; } -function generateAPI(httpMethod: HttpMethod, +function generateAPI(httpMethod: HttpMethod, argValidator: ValidatorType | undefined, - procDecoratorChain: PD, - callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise, -): APIExecuter { - const preDecorator: ProcDecorator[] = []; - const postDecorator: (ProcDecorator | undefined)[] = []; - if (procDecoratorChain) { - for (const procGen of procDecoratorChain) { - const proc = procGen(); - if (Array.isArray(proc)) { - preDecorator.push(proc[0]); - postDecorator.push(undefined); - } - else { - preDecorator.push(proc); - postDecorator.push(proc); - } - } - } - + preDecorator: ProcDecoratorRunner, + postDecorator: PostProcDecoratorRunner, + callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise, +): APIExecuter { return Object.assign( callback, { @@ -96,79 +81,85 @@ function generateAPI(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'get', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_GET; + ) as iAPI_GET; } } } export function POST(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'post', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_POST; + ) as iAPI_POST; } } } export function PUT(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'put', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_PUT; + ) as iAPI_PUT; } } } export function DELETE(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'delete', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_DELETE; + ) as iAPI_DELETE; } } } export function PATCH(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'patch', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_PATCH; + ) as iAPI_PATCH; } } } export function HEAD(argValidator?: ValidatorType) { - return (procDecoratorChain: PD) => { - return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return generateAPI( + return ([preDecorator, postDecorator]: readonly [ProcDecoratorRunner, PostProcDecoratorRunner]) => { + return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise) => { + return generateAPI( 'head', argValidator, - procDecoratorChain, + preDecorator, + postDecorator, callback, - ) as iAPI_HEAD; + ) as iAPI_HEAD; } } } diff --git a/server/api/generator.ts b/server/api/generator.ts index acca317..9bcca75 100644 --- a/server/api/generator.ts +++ b/server/api/generator.ts @@ -3,7 +3,7 @@ import type { AnyAPIExecuter, APINamespace, ClassType } from './defs.js'; import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import type { ExtractQuery, RawArgType } from '../apiStructure/defs.js'; -import type { Empty, InvalidProc, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js'; +import type { DecoratorResult, Empty, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js'; import clamp from 'lodash-es/clamp.js'; async function parseParam>(req: Request, argValidator?: ClassType): Promise { @@ -36,97 +36,43 @@ async function parseBody>(req: Request, return classObject as Q; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type PlainDecorator = ProcDecorator; - -async function preCtx>(preDecorator: PlainDecorator[], req: Request, res: Response): Promise | [InvalidProc, ResolveChain, number]> { - if (!preDecorator) { - return {} as ResolveChain; - } - - let ctx = {} as ResolveChain; - for (const [idx, proc] of preDecorator.entries()) { - try { - const result = await proc(ctx, req, res, true); - if ('invalidProcSymbol' in result) { - return [result, ctx, idx]; - } - ctx = result; - } - catch (e) { - return [{ - result: false, - reason: `internal error[${idx}]: ${e}`, - invalidProcSymbol: ['preCtx', idx], - }, ctx, idx]; - } - } - return ctx; -} - -async function postCtx(ctx: ResolveChain, postDecorator: (undefined | PlainDecorator)[], req: Request, res: Response, index = -1): Promise<[InvalidProc, number] | undefined> { - if (!postDecorator) { - return; - } - - let isValidRoute = true; - if (index !== -1 && index !== postDecorator.length) { - isValidRoute = false; - index = clamp(index, 0, postDecorator.length); - } - - for (let i = index - 1; i >= 0; i--) { - const proc = postDecorator[i]; - if (!proc) { - continue; - } - - try { - const result = await proc(ctx, req, res, isValidRoute); - if ('invalidProcSymbol' in result) { - result.invalidProcSymbol = ['postCtx', i, result.invalidProcSymbol]; - return [result, i]; - } - ctx = result; - } - catch (e) { - return [{ - result: false, - reason: `internal error[${i}]: ${e}`, - invalidProcSymbol: ['postCtx', i], - }, i]; - } - } - return; -} - async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExecuter): Promise { - const ctx = await preCtx(api.preDecorator, req, res); - if (Array.isArray(ctx)) { - const [invalidProc, errCtx, idx] = ctx; - if (idx == 0) { - res.json(invalidProc); + const [preResult, ctx] = await api.preDecorator({}, req, res); + if (!preResult.every((v) => v.result)) { + const lastErr = preResult[preResult.length - 1]; + const [postResult,] = await api.postDecorator(ctx, preResult, req, res, false); + if (!postResult.every((v) => v.result)) { + //회수조차 불가능? + const lastPostErr = postResult[postResult.length - 1]; + res.json({ + result: false, + path: req.path, + reason: `preDecorator: ${lastErr.type} ${lastErr.info}, postDecorator: ${lastPostErr.type} ${lastPostErr.info}`, + }); return; } - const result = await postCtx(errCtx, api.postDecorator, req, res, idx); - if (result === undefined) { - res.json(invalidProc); - return; - } - const [postInvalidProc,] = result; - res.json(postInvalidProc); + + res.json({ + result: false, + path: req.path, + reason: `preDecorator: ${lastErr.type} ${lastErr.info}`, + }) return; } const result = await api(query, ctx, req, res); - const postResult = await postCtx(ctx, api.postDecorator, req, res); - if (postResult !== undefined) { - if (result === true) { + const [postResult,] = await api.postDecorator(ctx, preResult, req, res, true); + if(!postResult.every((v) => v.result)) { + if(result === true) { //NOTE: 이미 api에서 response를 보낸 특이 케이스. return; } - const [invalidProc,] = postResult; - invalidProc.alreadyProcessed = true; - res.json(invalidProc); + const lastPostErr = postResult[postResult.length - 1]; + res.json({ + result: false, + path: req.path, + reason: `postDecorator: ${lastPostErr.type} ${lastPostErr.info}`, + originalResult: result, + }); return; }