gateway 코드 이식 중

This commit is contained in:
2023-09-22 21:34:01 +09:00
parent 438ee85ed2
commit 93686c4912
18 changed files with 431 additions and 2 deletions
@@ -0,0 +1,22 @@
import type { structure } from "@sammo/api_def/gateway";
import { StartSession } from "@sammo/server_util";
import { GET, type APIReturnType } from "@strpc/express";
import { declProcDecorators } from "@strpc/express/proc_decorator";
import { ReqGatewayLogin } from "../procDecorator/ReqGatewayLogin.js";
type BaseAPI = typeof structure.GetGameLoginToken;
type RType = APIReturnType<BaseAPI>;
export const GameLoginTokenSessionKey = "GameLoginToken";
export const GetGameLoginToken = GET<BaseAPI>()(declProcDecorators(
StartSession,
ReqGatewayLogin,
))(
async (query, ctx): RType => {
return {
result: false,
reason: 'NotYetImplemented',
};
}
);
@@ -0,0 +1,18 @@
import type { structure } from "@sammo/api_def/gateway";
import { StartSession } from "@sammo/server_util";
import { GET, type APIReturnType } from "@strpc/express";
import { declProcDecorators } from "@strpc/express/proc_decorator";
import { ReqGatewayLogin } from "../../procDecorator/ReqGatewayLogin.js";
type BaseAPI = typeof structure.Login.ReqNonce;
type RType = APIReturnType<BaseAPI>;
const argValidator = undefined;
export const ReqNonce = GET<BaseAPI>(argValidator)(declProcDecorators(
StartSession,
ReqGatewayLogin,
))(
(query, ctx): RType => {
throw new Error("Method not implemented.");
}
);
+61
View File
@@ -0,0 +1,61 @@
import type { structure } from "@sammo/api_def/gateway";
import { StartSession } from "@sammo/server_util";
import { POST, type APIReturnType } from "@strpc/express";
import { declProcDecorators } from "@strpc/express/proc_decorator";
import { z } from "zod";
import { loginCtxSessionKey, type GatewayLoginCtx } from "../../procDecorator/ReqGatewayLogin.js";
import { delay } from "@sammo/util";
type BaseAPI = typeof structure.Login.LoginByID;
type RType = APIReturnType<BaseAPI>;
const LoginByIDReq = z.object({
id: z.string(),
password: z.string(),
});
export const LoginByID = POST<BaseAPI>(LoginByIDReq)(declProcDecorators(
StartSession,
))(
async (query, ctx, req, res): RType => {
const id = query.id;
const password = query.password;
//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 userName = "test";
const userLevel = 1;
const nextToken: [number, string] = [1, "1234567890"];
const loginCtx: GatewayLoginCtx = {
userID,
userName,
userLevel,
allowServerAction: new Set(),
loginDate: new Date(),
}
ctx.session.setItem(loginCtxSessionKey, loginCtx);
return {
result: true,
nextToken,
}
});
@@ -0,0 +1,48 @@
import type { structure } from "@sammo/api_def/gateway";
import { StartSession } from "@sammo/server_util";
import { POST, type APIReturnType } from "@strpc/express";
import { declProcDecorators } from "@strpc/express/proc_decorator";
import { z } from "zod";
import { loginCtxSessionKey, type GatewayLoginCtx } from "../../procDecorator/ReqGatewayLogin.js";
type BaseAPI = typeof structure.Login.LoginByToken;
type RType = APIReturnType<BaseAPI>;
const LoginByTokenReq = z.object({
token_id: z.number(),
hashedToken: z.string(),
});
export const LoginByToken = POST<BaseAPI>(LoginByTokenReq)(declProcDecorators(
StartSession,
))
(async (query, ctx) => {
query.hashedToken;
ctx.session.clear();
await delay(1);
//무언가 로그인
//TODO: DB는 어디서 들고옴?
const userID = 1;
const userName = "test";
const userLevel = 1;
const nextToken: [number, string] = [1, "1234567890"];
const loginCtx: GatewayLoginCtx = {
userID,
userName,
userLevel,
allowServerAction: new Map(),
loginDate: new Date(),
}
ctx.session.setItem(loginCtxSessionKey, loginCtx);
//throw new Error("Method not implemented.");
return {
result: true,
nextToken,
}
});
+30
View File
@@ -0,0 +1,30 @@
import type { structure } from "@sammo/api_def/gateway";
import { StartSession } from "@sammo/server_util";
import { GET, type APIReturnType } from "@strpc/express";
import { declProcDecorators } from "@strpc/express/proc_decorator";
type BaseAPI = typeof structure.Login.ReqNonce;
type RType = APIReturnType<BaseAPI>;
export const ReqNonceSessionKey = 'loginNonce';
export const ReqNonce = GET<BaseAPI>(undefined)(declProcDecorators(
StartSession,
))(
async (query, ctx): RType => {
const nonce = ctx.session.getItem<string>(ReqNonceSessionKey);
if (nonce !== undefined) {
return {
loginNonce: nonce,
result: true,
}
}
const newNonce = "1234567890";
ctx.session.setItem(ReqNonceSessionKey, newNonce);
return {
loginNonce: newNonce,
result: true,
}
}
)
+13
View File
@@ -0,0 +1,13 @@
import type { structure } from "../../../apiStructure/sammoGatewayAPI.js";
import type { APINamespaceType } from "../../defs.js";
import { LoginByID } from "./LoginByID.js";
import { LoginByToken } from "./LoginByToken.js";
import { ReqNonce } from "./ReqNonce.js";
import { test } from "./test.js";
export const Login = {
LoginByID,
LoginByToken,
ReqNonce,
test,
} satisfies APINamespaceType<typeof structure.Login>;
+9
View File
@@ -0,0 +1,9 @@
import type { structure } from "../apiStructure/sammoGatewayAPI.js";
import type { APINamespaceType } from "./defs.js";
import { GetGameLoginToken } from "./GatewayAPI/GetGameLoginToken.js";
import { Login } from "./GatewayAPI/Login/index.js";
export const sammoGatewayAPI = {
Login,
GetGameLoginToken
} satisfies APINamespaceType<typeof structure>;
+9
View File
@@ -0,0 +1,9 @@
import type { structure } from "@sammo/api_def/gateway";
import { GetGameLoginToken } from "./GetGameLoginToken.js";
import { Login } from "./Login/index.js";
import type { APINamespaceType } from "@strpc/express";
export const sammoGatewayAPI = {
Login,
GetGameLoginToken
} satisfies APINamespaceType<typeof structure>;
@@ -0,0 +1,33 @@
import type { SessionCtx } from "@sammo/server_util";
import type { GatewayActionType, ServerActionType } from "../schema/User.js";
import type { ProcDecorator } from "@strpc/express/proc_decorator";
export type GatewayLoginCtx = {
userID: number;
userName: string;
userLevel: number;
allowServerAction: Map<string, Set<ServerActionType>>;
allowGatewayAction: Set<GatewayActionType>;
loginDate: Date;
}
export const loginCtxSessionKey = 'loginCtx';
export function ReqGatewayLogin<Q extends SessionCtx>(): ProcDecorator<GatewayLoginCtx & Q, Q> {
return (ctx) => {
const loginCtx = ctx.session.getItem<GatewayLoginCtx>(loginCtxSessionKey);
if(!loginCtx){
return [{
result: false,
type: 'Required Login',
info: 'ReqLogin'
}, ctx];
}
return [{
result: true,
},{
...loginCtx,
...ctx,
}];
}
}
+90
View File
@@ -0,0 +1,90 @@
import { Schema, model } from 'mongoose';
export type UserIDType = number;
const validOAuthTypeList = ['KAKAO', 'NONE'] as const;
export type validOAuthType = typeof validOAuthTypeList[number];
const validServerActionTypeList = [
'Update', 'UpdateByGitPath', 'ShowErrorLog',
'CloseServer', 'OpenServer', 'StopAndResumeServer', 'OpenVote',
] as const;
export type ServerActionType = typeof validServerActionTypeList[number];
const validGatewayActionTypeList = [
'Update', 'UpdateByGitPath', 'ShowErrorLog', 'ResetUserPassword', 'ChangeGatewayState', 'DeleteUser',
] as const;
export type GatewayActionType = typeof validGatewayActionTypeList[number];
interface IUser {
_id: UserIDType;
oauthID?: bigint;
id: string;
email: string;
oauthType: validOAuthType;
oauthInfo?: object;
tokenValidUntil?: Date;
userSalt: string;
hashedPassword: string;
allowThirdPartyUse: boolean;
userName: string;
allowServerAction?: Map<string, ServerActionType[]>;
allowGatewayAction?: ServerActionType[];
penalty?: Map<string, Date>;
picture?: string;
useImgSvr?: boolean;
regDate: Date;
deleteAfter?: Date;
}
export const User = new Schema<IUser>({
_id: { type: Number, required: true },
oauthID: { type: BigInt, required: false },
id: { type: String, required: true },
email: { type: String, required: true },
oauthType: { type: String, required: true, enum: validOAuthTypeList },
oauthInfo: { type: Object, required: false },
tokenValidUntil: { type: Date, required: false },
userSalt: { type: String, required: true },
hashedPassword: { type: String, required: true },
allowThirdPartyUse: { type: Boolean, required: true },
userName: { type: String, required: true },
allowServerAction: {
type: Map, required: false, of: {
type: Array,
of: { type: String, enum: validServerActionTypeList }
}
},
allowGatewayAction: {
type: Array, required: false, of: {
type: String, enum: validGatewayActionTypeList
}
},
penalty: { type: Map, required: false, of: Date },
picture: { type: String, required: false },
useImgSvr: { type: Boolean, required: false },
regDate: { type: Date, required: true },
deleteAfter: { type: Date, required: false },
}, { autoIndex: false, autoCreate: false })
.index({ id: 1 }, { unique: true })
.index({ email: 1 }, { unique: true })
.index({ oauthID: 1 }, { unique: true, sparse: true })
.index({ deleteAfter: 1 }) // 자동 삭제 아님!
;
export default model<IUser>('User', User);
+30
View File
@@ -0,0 +1,30 @@
import { Schema, model, } from 'mongoose';
import { type UserIDType } from './User.js';
export type LogType = 'register'
| 'login_pw' | 'login_token' | 'login_oauth' | 'logout'
| 'change_pw' | 'reset_pw';
const LogTypeList: LogType[] = [
'register',
'login_pw', 'login_token', 'login_oauth', 'logout',
'change_pw', 'reset_pw',
];
interface IUserLog {
userID: UserIDType;
logDate: Date;
logType: LogType;
action: object;
}
export const UserLog = new Schema<IUserLog>({
userID: { type: Number, required: true },
logDate: { type: Date, required: true },
logType: { type: String, required: true, enum: LogTypeList },
action: { type: Object, required: true },
}, { autoIndex: false, autoCreate: false, })
.index({ userID: 1, logDate: 1 })
.index({ logDate: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 365 * 3 })
;
export default model<IUserLog>('UserLog', UserLog);