From e381dd5a7e27b30372991cfcb8ab11ff745d680f Mon Sep 17 00:00:00 2001 From: Hide_D Date: Tue, 8 Aug 2023 17:10:15 +0000 Subject: [PATCH] =?UTF-8?q?=EB=8C=80=EC=B6=A9=EC=9D=B4=EB=9F=B0=EB=8A=90?= =?UTF-8?q?=EB=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/api/Login/ChangePassword.ts | 16 +++ server/api/Login/LoginByID.ts | 53 +++++++-- server/api/Login/LoginByToken.ts | 42 +++++-- server/api/Login/ReqNonce.ts | 40 ++++--- server/api/Login/test.ts | 15 +-- server/api/ProcDecorator/ParseUserLevel.ts | 7 +- server/api/ProcDecorator/ReqGameLogin.ts | 21 +++- server/api/ProcDecorator/ReqLogin.ts | 27 +++-- server/api/ProcDecorator/StartSession.ts | 39 +++++++ server/api/defs.ts | 130 +++++++++++++++------ 10 files changed, 296 insertions(+), 94 deletions(-) create mode 100644 server/api/Login/ChangePassword.ts create mode 100644 server/api/ProcDecorator/StartSession.ts diff --git a/server/api/Login/ChangePassword.ts b/server/api/Login/ChangePassword.ts new file mode 100644 index 0000000..50f105c --- /dev/null +++ b/server/api/Login/ChangePassword.ts @@ -0,0 +1,16 @@ +import { ngGET } from "../defs"; +import type { structure } from "../../apiStructure/sammoRootAPI"; +import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; + +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 = ngGET(argValidator)(procDecorator)( + (query, ctx, req, res) => { + throw new Error("Method not implemented."); + } +); \ No newline at end of file diff --git a/server/api/Login/LoginByID.ts b/server/api/Login/LoginByID.ts index bbf1575..256137a 100644 --- a/server/api/Login/LoginByID.ts +++ b/server/api/Login/LoginByID.ts @@ -1,24 +1,55 @@ -import { POST } from "../defs"; +import { POST, ngPOST } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import { StartSession } from "../ProcDecorator/StartSession"; +import { IsString } from "class-validator"; +import { delay } from "../../util/delay"; type BaseAPI = typeof structure.Login.LoginByID; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; - -class Q implements QType { +class ArgValidator implements QType { + @IsString() username!: string; + @IsString() password!: string; } +const procDecorator = [ + StartSession, +] as const; -const procDecorator = [] as const; +export const LoginByID = ngPOST(ArgValidator)(procDecorator)( + async (query, ctx, req, res) => { + const username = query.username; + const password = query.password; -export class LoginByID extends POST{ - readonly argValidator = Q; - readonly procDecorator = procDecorator; - protected LoginByID = Symbol("LoginByID");//TODO: remove this - protected override async api(query: QType, ctx,): Promise { - throw new Error("Method not implemented."); + //TODO: DB에서 뭔가 가져와야 함 + await delay(1); + + if(Math.random() < 0.3){ + return { + result: false, + reason: "로그인 실패", + reqOTP: false, + } + } + + if(Math.random() < 0.5){ + return { + result: false, + reason: "OTP 인증 필요", + reqOTP: true, + } + } + + const userID = 1; + const nextToken: [number, string] = [1, "1234567890"]; + + await ctx.setValue("userID", userID); + return { + 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 36ba39d..0748b2c 100644 --- a/server/api/Login/LoginByToken.ts +++ b/server/api/Login/LoginByToken.ts @@ -1,16 +1,42 @@ -import { POST } from "../defs"; +import { ngPOST } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; - +import { StartSession } from "../ProcDecorator/StartSession"; +import { IsNumber, IsString } from "class-validator"; +import { delay } from "../../util/delay"; 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 = ngPOST(ArgValidator)(procDecorator)( + async (query, ctx) => { + query.hashedToken; + ctx.clearSession(); + + await delay(1); + //무언가 로그인 + //TODO: DB는 어디서 들고옴? + + const userID = 1; + const nextToken: [number, string] = [1, "1234567890"]; + await ctx.setValue("userID", userID); + + + //throw new Error("Method not implemented."); + return { + result: true, + nextToken, + } -export class LoginByToken extends POST{ - readonly argValidator = undefined; - protected LoginByToken = Symbol("LoginByToken");//TODO: remove this - protected override async api(query: QType): Promise { - throw new Error("Method not implemented."); } -} \ 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 3398070..d8fcbe8 100644 --- a/server/api/Login/ReqNonce.ts +++ b/server/api/Login/ReqNonce.ts @@ -1,23 +1,35 @@ -import { ngGET } from "../defs"; +import { APINamespace, APINamespaceType, ngGET } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; -import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import type { DefAPINamespace, ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; +import { StartSession } from "../ProcDecorator/StartSession"; type BaseAPI = typeof structure.Login.ReqNonce; type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; +const argValidator = undefined; +const procDecorator = [ + StartSession, +] as const; -/*export class ReqNonce extends GET{ - readonly argValidator = undefined; - readonly procDecorator = [] as const; - protected ReqNonce = Symbol("ReqNonce");//TODO: remove this - protected override async api(query: QType): Promise { - throw new Error("Method not implemented."); - } -}*/ +export const ReqNonce = ngGET(argValidator)(procDecorator)( + async (query, ctx) => { + const nonce = await ctx.getValue("nonce"); + if(nonce !== undefined){ + return { + loginNonce: nonce, + result: true, + } + } -export const ReqNonce = ngGET(undefined)([] as const)( - (query, ctx, req, res) => { - throw new Error("Method not implemented."); + const newNonce = "1234567890"; + await ctx.setValue("nonce", newNonce); + return { + loginNonce: newNonce, + result: true, + } } -); \ No newline at end of file +); + +type BaseAPI2 = APINamespaceType['ReqNonce']; +type A = typeof ReqNonce; \ No newline at end of file diff --git a/server/api/Login/test.ts b/server/api/Login/test.ts index cf3bfbb..4984da4 100644 --- a/server/api/Login/test.ts +++ b/server/api/Login/test.ts @@ -1,4 +1,4 @@ -import { GET } from "../defs"; +import { GET, ngGET } from "../defs"; import type { structure } from "../../apiStructure/sammoRootAPI"; import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs"; @@ -7,13 +7,8 @@ type RType = ExtractResponse; type EType = ExtractError; type QType = ExtractQuery; -export class test extends GET{ - readonly argValidator = undefined; - protected ReqNonce = Symbol("ReqNonce");//TODO: remove this - protected override async api(query: QType): Promise { - return { - hello: "world", - result: true, - } +export const test = ngGET(undefined)(undefined)( + (query, ctx, req, res) => { + 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 d39d411..9099731 100644 --- a/server/api/ProcDecorator/ParseUserLevel.ts +++ b/server/api/ProcDecorator/ParseUserLevel.ts @@ -1,4 +1,5 @@ import { LoginCtx } from "./ReqLogin"; +import { SessionCtx } from "./StartSession"; import type { ProcDecoratorGenerator } from "./base"; import type { Request, Response } from "express"; @@ -7,10 +8,12 @@ export interface UserLevelCtx { userLevel: number; } -export function ParseUserLevel(): ProcDecoratorGenerator { +export function ParseUserLevel(): ProcDecoratorGenerator { return (inCtx) => { + const userLevel = 123; + inCtx.setValue('userLevel', userLevel); return { - userLevel: 123, + userLevel, ...inCtx, } } diff --git a/server/api/ProcDecorator/ReqGameLogin.ts b/server/api/ProcDecorator/ReqGameLogin.ts index 3695d68..90e4294 100644 --- a/server/api/ProcDecorator/ReqGameLogin.ts +++ b/server/api/ProcDecorator/ReqGameLogin.ts @@ -1,15 +1,30 @@ import { LoginCtx } from "./ReqLogin"; +import { SessionCtx } from "./StartSession"; import type { ProcDecoratorGenerator } from "./base"; import type { Request, Response } from "express"; +import { delay } from "../../util/delay.js"; export type GameLoginCtx = { generalID: number; } -export function ReqGameLogin(): ProcDecoratorGenerator { - return (inCtx) => { +export function ReqGameLogin(): ProcDecoratorGenerator { + return async (inCtx) => { + const ctx: Q & Partial = inCtx; + if (ctx.generalID !== undefined) { + return { + generalID: ctx.generalID, + ...inCtx, + }; + } + + console.log('Something GameLogin'); + await delay(0); + + inCtx.setValue('generalID', ctx.userID * 4); + return { - generalID: inCtx.userID * 4, + generalID: ctx.userID * 4, ...inCtx, } } diff --git a/server/api/ProcDecorator/ReqLogin.ts b/server/api/ProcDecorator/ReqLogin.ts index b279153..df62193 100644 --- a/server/api/ProcDecorator/ReqLogin.ts +++ b/server/api/ProcDecorator/ReqLogin.ts @@ -1,20 +1,23 @@ -import type { Empty, ProcDecoratorPrePostGenerator } from "./base"; +import { SessionCtx } from "./StartSession"; +import type { InvalidProc, ProcDecoratorGenerator } from "./base"; export type LoginCtx = { userID: number; } -export function ReqLogin(): ProcDecoratorPrePostGenerator { - return [ - (inCtx) => { +export function ReqLogin(): ProcDecoratorGenerator { + return (inCtx) => { + const ctx: Q & Partial = inCtx; + const userID = ctx.userID; + + if (userID === undefined) { return { - userID: 999, - ...inCtx, - }; - }, - (inCtx) => { - console.log('ReqLogin post'); - return inCtx; + result: false, + reason: 'Required Login', + invalidProcSymbol: 'ReqLogin' + } satisfies InvalidProc; } - ] + + return ctx as LoginCtx & Q; + } } \ No newline at end of file diff --git a/server/api/ProcDecorator/StartSession.ts b/server/api/ProcDecorator/StartSession.ts new file mode 100644 index 0000000..6400307 --- /dev/null +++ b/server/api/ProcDecorator/StartSession.ts @@ -0,0 +1,39 @@ +import type { Empty, ProcDecoratorGenerator } from "./base"; + +export type SessionCtx = { + clearSession: () => Promise; + deleteValue: (key: string) => Promise; + getValue: (key: string) => Promise; + setValue: (key: string, value: unknown) => Promise; + sessionRaw: { + raw: object; + changed: boolean; + }; +} + +export function StartSession(): ProcDecoratorGenerator { + return (inCtx) => { + const raw: object = {}; + return { + clearSession: () => Promise.resolve(), + deleteValue: async (key: string) => { + console.log('deleteValue', key); + if (raw[key] !== undefined) { + delete raw[key]; + } + }, + getValue: async (key: string) => { + return raw[key] as T | undefined; + }, + setValue: async (key: string, value: unknown) => { + console.log('setValue', key, value); + raw[key] = value; + }, + sessionRaw: { + raw, + changed: false, + }, + ...inCtx, + }; + } +} \ No newline at end of file diff --git a/server/api/defs.ts b/server/api/defs.ts index 13a8d8a..94a3b5c 100644 --- a/server/api/defs.ts +++ b/server/api/defs.ts @@ -94,14 +94,14 @@ export abstract class APIExecuter= 0; i--) { const proc = this.postDecorator[i]; - if(!proc){ + if (!proc) { continue; } @@ -131,7 +131,7 @@ export abstract class APIExecuter{ +interface ngAPIExecuter { (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response): Promise; httpMethod: HttpMethod; argValidator?: ValidatorType; @@ -171,19 +171,38 @@ interface ngAPIExecuter | undefined)[]; } -interface ngAPIGenerator{ - (procDecoratorChain: PD): ngAPIExecuter; +export interface iAPI_GET extends ngAPIExecuter { + httpMethod: 'get'; } -function ngGenerateAPI(httpMethod: HttpMethod, args: { - argValidator?: ValidatorType, +export interface iAPI_POST extends ngAPIExecuter { + httpMethod: 'post'; +} + +export interface iAPI_PUT extends ngAPIExecuter { + httpMethod: 'put'; +} + +export interface iAPI_DELETE extends ngAPIExecuter { + httpMethod: 'delete'; +} + +export interface iAPI_PATCH extends ngAPIExecuter { + httpMethod: 'patch'; +} + +export interface iAPI_HEAD extends ngAPIExecuter { + httpMethod: 'head'; +} + +function ngGenerateAPI(httpMethod: HttpMethod, + argValidator: ValidatorType | undefined, procDecoratorChain: PD, callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise, -}): ngAPIExecuter{ - const { argValidator, procDecoratorChain, callback } = args; +): ngAPIExecuter { const preDecorator: ProcDecorator[] = []; const postDecorator: (ProcDecorator | undefined)[] = []; - if(procDecoratorChain){ + if (procDecoratorChain) { for (const procGen of procDecoratorChain) { const proc = procGen(); if (Array.isArray(proc)) { @@ -208,41 +227,84 @@ function ngGenerateAPI(argValidator?: ValidatorType){ +export function ngGET(argValidator?: ValidatorType) { return (procDecoratorChain: PD) => { return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { - return ngGenerateAPI('get', { + return ngGenerateAPI( + 'get', argValidator, procDecoratorChain, callback, - }); + ) as iAPI_GET; } } } -interface ngGET extends ngAPIExecuter{ - httpMethod: 'get'; +export function ngPOST(argValidator?: ValidatorType) { + return (procDecoratorChain: PD) => { + return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { + return ngGenerateAPI( + 'post', + argValidator, + procDecoratorChain, + callback, + ) as iAPI_POST; + } + } } -interface ngPOST extends ngAPIExecuter{ - httpMethod: 'post'; +export function ngPUT(argValidator?: ValidatorType) { + return (procDecoratorChain: PD) => { + return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { + return ngGenerateAPI( + 'put', + argValidator, + procDecoratorChain, + callback, + ) as iAPI_PUT; + } + } } -interface ngPUT extends ngAPIExecuter{ - httpMethod: 'put'; +export function ngDELETE(argValidator?: ValidatorType) { + return (procDecoratorChain: PD) => { + return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { + return ngGenerateAPI( + 'delete', + argValidator, + procDecoratorChain, + callback, + ) as iAPI_DELETE; + } + } } -interface ngDELETE extends ngAPIExecuter{ - httpMethod: 'delete'; +export function ngPATCH(argValidator?: ValidatorType) { + return (procDecoratorChain: PD) => { + return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { + return ngGenerateAPI( + 'patch', + argValidator, + procDecoratorChain, + callback, + ) as iAPI_PATCH; + } + } } -interface ngPATCH extends ngAPIExecuter{ - httpMethod: 'patch'; +export function ngHEAD(argValidator?: ValidatorType) { + return (procDecoratorChain: PD) => { + return (callback: (query: Q, ctx: ResolveChain, expressReq: Request, expressRes: Response) => Promise) => { + return ngGenerateAPI( + 'head', + argValidator, + procDecoratorChain, + callback, + ) as iAPI_HEAD; + } + } } -interface ngHEAD extends ngAPIExecuter{ - httpMethod: 'head'; -} export abstract class APIBodyParseExecuter extends APIExecuter{ @@ -321,17 +383,17 @@ export type ClassType = new (...args: any[]) => T; export type APIServerType = // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends GetAPICallT ? ClassType> : + T extends GetAPICallT ? iAPI_GET : // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends PostAPICallT ? ClassType> : + T extends PostAPICallT ? iAPI_POST : // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends PutAPICallT ? ClassType> : + T extends PutAPICallT ? iAPI_PUT : // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends DeleteAPICallT ? ClassType> : + T extends DeleteAPICallT ? iAPI_DELETE : // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends PatchAPICallT ? ClassType> : + T extends PatchAPICallT ? iAPI_PATCH : // eslint-disable-next-line @typescript-eslint/no-explicit-any - T extends HeadAPICallT ? ClassType> : + T extends HeadAPICallT ? iAPI_HEAD : never; export type APINamespaceType = { [K in keyof T]: