경로 이동
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
import type { LoginCtx } from "./ReqLogin.js";
|
||||
import type { SessionCtx } from "./StartSession.js";
|
||||
import type { ProcDecoratorGenerator } from "./base.js";
|
||||
|
||||
export type GameLoginCtx = {
|
||||
generalID: number;
|
||||
generalName: string;
|
||||
gameLoginDate: Date;
|
||||
}
|
||||
|
||||
export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<GameLoginCtx, Q> {
|
||||
return async (ctx) => {
|
||||
//NOTE: 게임 서버별로 gameLoginCtx를 따로 가져야 하는가?
|
||||
let gameLoginCtx = ctx.session.getItem<GameLoginCtx>(`gameLoginCtx`);
|
||||
if(gameLoginCtx){
|
||||
if(gameLoginCtx.gameLoginDate >= ctx.loginDate){
|
||||
return [{
|
||||
result: true,
|
||||
},{
|
||||
...gameLoginCtx,
|
||||
...ctx,
|
||||
}];
|
||||
}
|
||||
gameLoginCtx = undefined;
|
||||
}
|
||||
|
||||
//TODO: DB에서 generalID를 가져오는 로직
|
||||
|
||||
return [{
|
||||
result: false,
|
||||
type: 'Required GameLogin',
|
||||
info: 'NotYetImplemented'
|
||||
}, ctx];
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { SessionCtx } from "./StartSession.js";
|
||||
import type { ProcDecorator } from "./base.js";
|
||||
|
||||
export type LoginCtx = {
|
||||
userID: number;
|
||||
userName: string;
|
||||
userLevel: number;
|
||||
allowServerAction: Record<string, number>;
|
||||
loginDate: Date;
|
||||
}
|
||||
export const loginCtxSessionKey = 'loginCtx';
|
||||
|
||||
export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q> {
|
||||
return (ctx) => {
|
||||
const loginCtx = ctx.session.getItem<LoginCtx>(loginCtxSessionKey);
|
||||
if(!loginCtx){
|
||||
return [{
|
||||
result: false,
|
||||
type: 'Required Login',
|
||||
info: 'ReqLogin'
|
||||
}, ctx];
|
||||
}
|
||||
|
||||
return [{
|
||||
result: true,
|
||||
},{
|
||||
...loginCtx,
|
||||
...ctx,
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { type Session, type SessionData } from "express-session";
|
||||
import type { Empty, ProcDecoratorGenerator } from "./base.js";
|
||||
|
||||
export type SessionCtx = {
|
||||
session: {
|
||||
clear: () => Promise<void>;
|
||||
removeItem: (key: string) => boolean;
|
||||
getItem: <T>(key: string) => T | undefined;
|
||||
setItem: (key: string, value: unknown) => void;
|
||||
raw: Session & Partial<SessionData> & Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
|
||||
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
||||
return (inCtx, req) => {
|
||||
if (!req.session) {
|
||||
throw 'Express-session required';
|
||||
}
|
||||
const sessionObj = {
|
||||
raw: req.session
|
||||
} as SessionCtx['session'];
|
||||
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,
|
||||
},
|
||||
{
|
||||
session: sessionObj,
|
||||
...inCtx,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
import type { Request, Response } from "express";
|
||||
|
||||
type MayBePromise<T> = T | Promise<T>;
|
||||
|
||||
export type Empty = Record<string, never>;
|
||||
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<Out extends object, In = Empty> {
|
||||
(inCtx: In & Partial<Out>, req: Request, res: Response)
|
||||
: MayBePromise<[DecoratorResultTrue, Out] | [DecoratorResultFalse, In & Partial<Out>]>;
|
||||
}
|
||||
|
||||
export interface PostProcDecorator<T extends object> {
|
||||
(ctx: T, preResult: DecoratorResult, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorResult, T]>;
|
||||
}
|
||||
|
||||
export interface ProcDecoratorRunner<Out extends object, In> {
|
||||
(inCtx: In, req: Request, res: Response): MayBePromise<[DecoratorStack, Out]>;
|
||||
}
|
||||
|
||||
export interface PostProcDecoratorRunner<T extends object> {
|
||||
(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<any, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type PlainPostDecorator = PostProcDecorator<any>;
|
||||
|
||||
|
||||
export type ProcDecoratorGenerator<Out extends object, In = Empty> = ProcDecorator<Out & In, In>;
|
||||
export type ProcDecoratorPrePostGenerator<Out extends object, In> = [ProcDecorator<Out & In, In>, PostProcDecorator<Out & In>];
|
||||
|
||||
export type ProcDecoratorChain = readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[];
|
||||
|
||||
export type ResolveChain<T> = T extends undefined ? Empty : T extends ProcDecoratorChain ? Resolve<PackChain<T>> : never;
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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
|
||||
export type ParseOutType<T> = T extends ProcDecorator<infer B, object> ? B : never;
|
||||
|
||||
export const EmptyProcDecorator: readonly [ProcDecoratorRunner<Empty, Empty>, PostProcDecoratorRunner<Empty>] = [
|
||||
async (ctx) => [[], ctx], async (ctx, stack) => [stack, ctx]
|
||||
];
|
||||
|
||||
export function declProcDecorators<T extends ProcDecoratorChain>(...decorators: T) {
|
||||
type OutType = Resolve<PackChain<T>>;
|
||||
|
||||
const preDecorator: PlainDecorator[] = [];
|
||||
const postDecorator: (PlainPostDecorator | undefined)[] = [];
|
||||
if (decorators) {
|
||||
for (const procGen of decorators) {
|
||||
const proc = procGen();
|
||||
if (Array.isArray(proc)) {
|
||||
preDecorator.push(proc[0]);
|
||||
postDecorator.push(proc[1]);
|
||||
}
|
||||
else {
|
||||
preDecorator.push(proc);
|
||||
postDecorator.push(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const packedDecorators: readonly [ProcDecoratorRunner<OutType, Empty>, PostProcDecoratorRunner<OutType>] = [
|
||||
async (ctx, req, res) => {
|
||||
let rctx = ctx as unknown as OutType;
|
||||
const decoratorStack: DecoratorStack = [];
|
||||
if (!preDecorator.length) {
|
||||
return [decoratorStack, rctx];
|
||||
}
|
||||
|
||||
for (const [idx, proc] of preDecorator.entries()) {
|
||||
try {
|
||||
const [stackResult, newCtx] = await proc(rctx, req, res);
|
||||
decoratorStack.push(stackResult);
|
||||
|
||||
if (stackResult.result) {
|
||||
rctx = newCtx;
|
||||
continue;
|
||||
}
|
||||
|
||||
return [decoratorStack, newCtx];
|
||||
}
|
||||
catch (e) {
|
||||
while (decoratorStack.length > idx) {
|
||||
decoratorStack.pop();
|
||||
}
|
||||
decoratorStack.push({
|
||||
result: false,
|
||||
type: 'PreThrow',
|
||||
info: `internal error: ${e}`,
|
||||
});
|
||||
|
||||
return [decoratorStack, rctx];
|
||||
}
|
||||
}
|
||||
|
||||
return [decoratorStack, rctx];
|
||||
},
|
||||
async (ctx, stack, req, res) => {
|
||||
if (!postDecorator.length) {
|
||||
return [stack, ctx];
|
||||
}
|
||||
let isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result);
|
||||
for (let idx = stack.length - 1; idx >= 0; idx--) {
|
||||
const preStackResult = stack[idx] as DecoratorResult;
|
||||
if (!preStackResult.result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const proc = postDecorator[idx];
|
||||
|
||||
if (!proc) {
|
||||
if (isValidRoute) {
|
||||
stack.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute);
|
||||
if (postStackResult.result) {
|
||||
if (isValidRoute) {
|
||||
stack.pop();
|
||||
}
|
||||
ctx = nextCtx;
|
||||
continue;
|
||||
}
|
||||
|
||||
isValidRoute = false;
|
||||
stack[idx] = postStackResult;
|
||||
|
||||
ctx = nextCtx;
|
||||
}
|
||||
catch (e) {
|
||||
isValidRoute = false;
|
||||
stack[idx] = {
|
||||
result: false,
|
||||
type: 'PostThrow',
|
||||
info: `internal error: ${e}`,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
return [stack, ctx];
|
||||
},
|
||||
] as const;
|
||||
|
||||
return packedDecorators;
|
||||
}
|
||||
|
||||
type Compose2<D extends object, C, B, A = Empty> = B extends C ? ProcDecorator<D & B, A> : never;
|
||||
|
||||
type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type PD2<B extends object, A extends object> = () => [ProcDecorator<B, A>, PostProcDecorator<any>];
|
||||
|
||||
export type PackChain<T> =
|
||||
T extends readonly [] ? ProcDecorator<Empty, Empty> :
|
||||
T extends readonly [PD1<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||
T extends readonly [PD2<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||
T extends readonly [PD1<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<D, C, B, A>, ...R]> :
|
||||
T extends readonly [PD2<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<D, C, B, A>, ...R]> :
|
||||
T extends readonly [PD1<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<D, C, B, A>, ...R]> :
|
||||
T extends readonly [PD2<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<D, C, B, A>, ...R]> :
|
||||
never;
|
||||
|
||||
type Resolve<T> = T extends ProcDecorator<infer B, infer A> ? Empty extends A ? B : never : never;
|
||||
@@ -1,9 +1,9 @@
|
||||
import { GET } from "../defs.js";
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs.js";
|
||||
import { declProcDecorators } from "../ProcDecorator/base.js";
|
||||
import { ReqLogin } from "../ProcDecorator/ReqLogin.js";
|
||||
import { StartSession } from "../ProcDecorator/StartSession.js";
|
||||
import { GET } from "../../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../../apiStructure/defs.js";
|
||||
import { declProcDecorators } from "../../../ProcDecorator/base.js";
|
||||
import { ReqLogin } from "../../../ProcDecorator/ReqLogin.js";
|
||||
import { StartSession } from "../../../ProcDecorator/StartSession.js";
|
||||
|
||||
type BaseAPI = typeof structure.Login.ReqNonce;
|
||||
type RType = ExtractResponse<BaseAPI>;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { POST } from "../defs.js";
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs.js";
|
||||
import { StartSession } from "../ProcDecorator/StartSession.js";
|
||||
import { delay } from "../../util/delay.js";
|
||||
import { declProcDecorators } from "../ProcDecorator/base.js";
|
||||
import { POST } from "../../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../../apiStructure/defs.js";
|
||||
import { StartSession } from "../../../ProcDecorator/StartSession.js";
|
||||
import { delay } from "../../../util/delay.js";
|
||||
import { declProcDecorators } from "../../../ProcDecorator/base.js";
|
||||
import { z } from "zod";
|
||||
import { type LoginCtx, loginCtxSessionKey } from "../ProcDecorator/ReqLogin.js";
|
||||
import { type LoginCtx, loginCtxSessionKey } from "../../../ProcDecorator/ReqLogin.js";
|
||||
|
||||
type BaseAPI = typeof structure.Login.LoginByID;
|
||||
type RType = ExtractResponse<BaseAPI>;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { POST } from "../defs.js";
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs.js";
|
||||
import { StartSession } from "../ProcDecorator/StartSession.js";
|
||||
import { delay } from "../../util/delay.js";
|
||||
import { declProcDecorators } from "../ProcDecorator/base.js";
|
||||
import { type LoginCtx, loginCtxSessionKey } from "../ProcDecorator/ReqLogin.js";
|
||||
import { POST } from "../../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../../apiStructure/defs.js";
|
||||
import { StartSession } from "../../../ProcDecorator/StartSession.js";
|
||||
import { delay } from "../../../util/delay.js";
|
||||
import { declProcDecorators } from "../../../ProcDecorator/base.js";
|
||||
import { type LoginCtx, loginCtxSessionKey } from "../../../ProcDecorator/ReqLogin.js";
|
||||
import { z } from "zod";
|
||||
type BaseAPI = typeof structure.Login.LoginByToken;
|
||||
type RType = ExtractResponse<BaseAPI>;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { GET } from "../defs.js";
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs.js";
|
||||
import { StartSession } from "../ProcDecorator/StartSession.js";
|
||||
import { declProcDecorators } from "../ProcDecorator/base.js";
|
||||
import { GET } from "../../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../../apiStructure/defs.js";
|
||||
import { StartSession } from "../../../ProcDecorator/StartSession.js";
|
||||
import { declProcDecorators } from "../../../ProcDecorator/base.js";
|
||||
|
||||
type BaseAPI = typeof structure.Login.ReqNonce;
|
||||
type RType = ExtractResponse<BaseAPI>;
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { APINamespaceType } from "../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { APINamespaceType } from "../../defs.js";
|
||||
import { LoginByID } from "./LoginByID.js";
|
||||
import { LoginByToken } from "./LoginByToken.js";
|
||||
import { ReqNonce } from "./ReqNonce.js";
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GET } from "../defs.js";
|
||||
import type { structure } from "../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs.js";
|
||||
import { EmptyProcDecorator } from "../ProcDecorator/base.js";
|
||||
import { GET } from "../../defs.js";
|
||||
import type { structure } from "../../../apiStructure/sammoRootAPI.js";
|
||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../../apiStructure/defs.js";
|
||||
import { EmptyProcDecorator } from "../../../ProcDecorator/base.js";
|
||||
|
||||
type BaseAPI = typeof structure.Login.test;
|
||||
type RType = ExtractResponse<BaseAPI>;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
|
||||
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 { Empty, PostProcDecoratorRunner, ProcDecoratorRunner } from './ProcDecorator/base.js';
|
||||
import type { Empty, PostProcDecoratorRunner, ProcDecoratorRunner } from '../ProcDecorator/base.js';
|
||||
import type { ZodType } from 'zod';
|
||||
|
||||
export type APINamespace = {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { structure } from "../apiStructure/sammoRootAPI.js";
|
||||
import type { APINamespaceType } from "./defs.js";
|
||||
import { Login } from "./Login/index.js";
|
||||
import { Login } from "./RootAPI/Login/index.js";
|
||||
|
||||
export const sammoRootAPI = {
|
||||
Login,
|
||||
|
||||
Reference in New Issue
Block a user