refactor: tighten backend and logic boundaries
This commit is contained in:
@@ -46,13 +46,6 @@ const decodeImage = (input: string): Buffer => {
|
||||
return buffer;
|
||||
};
|
||||
|
||||
const SEOUL_OFFSET_MS = 9 * 60 * 60 * 1000;
|
||||
|
||||
export const kstDayStart = (value: Date): Date => {
|
||||
const shifted = new Date(value.getTime() + SEOUL_OFFSET_MS);
|
||||
return new Date(Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate()) - SEOUL_OFFSET_MS);
|
||||
};
|
||||
|
||||
const assertIconChangeAvailable = (user: UserRecord, now: Date): void => {
|
||||
if (
|
||||
user.picture !== 'default.jpg' &&
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
import { createSimplePasswordHasher, type PasswordHasher } from './passwordHasher.js';
|
||||
import { createPasswordHasher, type PasswordHasher } from './passwordHasher.js';
|
||||
import type {
|
||||
AdminUserListItem,
|
||||
CreateUserInput,
|
||||
@@ -24,7 +24,7 @@ const toAdminUserListItem = (user: UserRecord): AdminUserListItem => ({
|
||||
});
|
||||
|
||||
// 유저 데이터 저장소를 메모리로 대체한 임시 구현.
|
||||
export const createInMemoryUserRepository = (hasher: PasswordHasher = createSimplePasswordHasher()): UserRepository => {
|
||||
export const createInMemoryUserRepository = (hasher: PasswordHasher = createPasswordHasher()): UserRepository => {
|
||||
const usersByName = new Map<string, UserRecord>();
|
||||
const usersByOauthId = new Map<string, UserRecord>();
|
||||
const usersByEmail = new Map<string, UserRecord>();
|
||||
|
||||
@@ -6,10 +6,10 @@ import type { KakaoOAuthClient, KakaoOAuthToken, KakaoUserInfo } from './kakaoCl
|
||||
import type { OAuthSessionStore } from './oauthSessionStore.js';
|
||||
import type { UserOAuthInfo, UserRecord, UserRepository } from './userRepository.js';
|
||||
|
||||
export const KAKAO_LOGIN_SCOPES = ['account_email', 'talk_message'] as const;
|
||||
export const KAKAO_OTP_TTL_SECONDS = 180;
|
||||
export const KAKAO_OTP_ATTEMPTS = 3;
|
||||
export const KAKAO_TALK_VERIFICATION_DAYS = 10;
|
||||
const KAKAO_LOGIN_SCOPES = ['account_email', 'talk_message'] as const;
|
||||
const KAKAO_OTP_TTL_SECONDS = 180;
|
||||
const KAKAO_OTP_ATTEMPTS = 3;
|
||||
const KAKAO_TALK_VERIFICATION_DAYS = 10;
|
||||
|
||||
export type KakaoVerificationErrorCode =
|
||||
| 'EMAIL_REQUIRED'
|
||||
|
||||
@@ -97,6 +97,3 @@ export const createPasswordHasher = (options: { legacyGlobalSalt?: string } = {}
|
||||
return { ok: false, needsUpgrade: false };
|
||||
},
|
||||
});
|
||||
|
||||
// 기존 import 지점을 깨지 않되 새 계정은 Argon2id를 사용한다.
|
||||
export const createSimplePasswordHasher = createPasswordHasher;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GatewayPrisma, type GatewayPrismaClient } from '@sammo-ts/infra';
|
||||
|
||||
import { createSimplePasswordHasher, type PasswordHasher } from './passwordHasher.js';
|
||||
import { createPasswordHasher, type PasswordHasher } from './passwordHasher.js';
|
||||
import type {
|
||||
AdminUserListItem,
|
||||
CreateUserInput,
|
||||
@@ -158,7 +158,7 @@ const mapSpecialAccessGrant = (row: {
|
||||
|
||||
export const createPostgresUserRepository = (
|
||||
prisma: GatewayPrismaClient,
|
||||
hasher: PasswordHasher = createSimplePasswordHasher()
|
||||
hasher: PasswordHasher = createPasswordHasher()
|
||||
): UserRepository => {
|
||||
return {
|
||||
async findById(id: string): Promise<UserRecord | null> {
|
||||
@@ -406,7 +406,9 @@ export const createPostgresUserRepository = (
|
||||
if (result.count !== 1) {
|
||||
return null;
|
||||
}
|
||||
return mapSpecialAccessGrant(await prisma.specialAccountAccessGrant.findUniqueOrThrow({ where: { id: grantId } }));
|
||||
return mapSpecialAccessGrant(
|
||||
await prisma.specialAccountAccessGrant.findUniqueOrThrow({ where: { id: grantId } })
|
||||
);
|
||||
},
|
||||
async updateIcon(userId: string, picture: string, imageServer: number, updatedAt: Date): Promise<void> {
|
||||
await prisma.appUser.update({
|
||||
|
||||
@@ -21,7 +21,7 @@ const isWideCodePoint = (codePoint: number): boolean =>
|
||||
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
||||
(codePoint >= 0x20000 && codePoint <= 0x3fffd));
|
||||
|
||||
export const legacyStringWidth = (value: string): number =>
|
||||
const legacyStringWidth = (value: string): number =>
|
||||
Array.from(value).reduce((width, character) => {
|
||||
const codePoint = character.codePointAt(0) ?? 0;
|
||||
return width + (isWideCodePoint(codePoint) ? 2 : 1);
|
||||
|
||||
@@ -6,8 +6,7 @@ export * from './config.js';
|
||||
export * from './context.js';
|
||||
export * from './router.js';
|
||||
export * from './server.js';
|
||||
import { GatewayPrisma } from '@sammo-ts/infra';
|
||||
export { GatewayPrisma };
|
||||
import type { GatewayPrisma } from '@sammo-ts/infra';
|
||||
export type JsonObject = GatewayPrisma.JsonObject;
|
||||
export type JsonArray = GatewayPrisma.JsonArray;
|
||||
export * from './orchestrator/profileRepository.js';
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface BuildRunner {
|
||||
}
|
||||
|
||||
export const MAX_BUILD_OUTPUT_CHARS = 64 * 1024;
|
||||
export const DEFAULT_RELEASE_TURBO_CONCURRENCY = 1;
|
||||
const DEFAULT_RELEASE_TURBO_CONCURRENCY = 1;
|
||||
|
||||
export const resolveReleaseTurboConcurrency = (env?: Record<string, string>): number => {
|
||||
const configured = env?.RELEASE_TURBO_CONCURRENCY?.trim();
|
||||
|
||||
@@ -469,10 +469,10 @@ export const buildProcessDefinitions = (
|
||||
|
||||
const sanitizeArtifactName = (value: string): string => value.replace(/[^0-9A-Za-z._-]+/g, '_');
|
||||
|
||||
export const buildProfileFrontendOutDir = (workspaceRoot: string, profileName: string): string =>
|
||||
const buildProfileFrontendOutDir = (workspaceRoot: string, profileName: string): string =>
|
||||
path.join(workspaceRoot, '.release-dist', sanitizeArtifactName(profileName), 'game-frontend');
|
||||
|
||||
export const buildProfileFrontendCommands = (
|
||||
const buildProfileFrontendCommands = (
|
||||
workspaceRoot: string,
|
||||
profile: Pick<GatewayProfileRecord, 'profileName' | 'profile' | 'apiPort'>,
|
||||
env?: Record<string, string>
|
||||
|
||||
@@ -2,8 +2,7 @@ import type { GatewayPrisma, GatewayPrismaClient } from '@sammo-ts/infra';
|
||||
|
||||
import type { GatewayOperationStatus, GatewaySourceMode } from './profileRepository.js';
|
||||
|
||||
export const GATEWAY_RELEASE_OPERATION_TYPES = ['DEPLOY', 'ROLLBACK'] as const;
|
||||
export type GatewayReleaseOperationType = (typeof GATEWAY_RELEASE_OPERATION_TYPES)[number];
|
||||
export type GatewayReleaseOperationType = 'DEPLOY' | 'ROLLBACK';
|
||||
|
||||
export interface GatewayReleaseStateRecord {
|
||||
id: string;
|
||||
@@ -46,7 +45,7 @@ export interface GatewayReleaseOperationCreateInput {
|
||||
requestedBy: string;
|
||||
}
|
||||
|
||||
export const GATEWAY_RELEASE_LOG_LEVELS = ['INFO', 'OUTPUT', 'ERROR'] as const;
|
||||
const GATEWAY_RELEASE_LOG_LEVELS = ['INFO', 'OUTPUT', 'ERROR'] as const;
|
||||
export type GatewayReleaseLogLevel = (typeof GATEWAY_RELEASE_LOG_LEVELS)[number];
|
||||
|
||||
export interface GatewayReleaseLogRecord {
|
||||
|
||||
@@ -14,14 +14,11 @@ export type GatewayProfileStatus = (typeof GATEWAY_PROFILE_STATUSES)[number];
|
||||
export const GATEWAY_BUILD_STATUSES = ['IDLE', 'QUEUED', 'RUNNING', 'FAILED', 'SUCCEEDED'] as const;
|
||||
export type GatewayBuildStatus = (typeof GATEWAY_BUILD_STATUSES)[number];
|
||||
|
||||
export const GATEWAY_OPERATION_TYPES = ['RESET', 'DEPLOY', 'START', 'STOP'] as const;
|
||||
export type GatewayOperationType = (typeof GATEWAY_OPERATION_TYPES)[number];
|
||||
export type GatewayOperationType = 'RESET' | 'DEPLOY' | 'START' | 'STOP';
|
||||
|
||||
export const GATEWAY_OPERATION_STATUSES = ['QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELLED'] as const;
|
||||
export type GatewayOperationStatus = (typeof GATEWAY_OPERATION_STATUSES)[number];
|
||||
export type GatewayOperationStatus = 'QUEUED' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED';
|
||||
|
||||
export const GATEWAY_SOURCE_MODES = ['BRANCH', 'COMMIT'] as const;
|
||||
export type GatewaySourceMode = (typeof GATEWAY_SOURCE_MODES)[number];
|
||||
export type GatewaySourceMode = 'BRANCH' | 'COMMIT';
|
||||
|
||||
export interface GatewayOperationRecord {
|
||||
id: string;
|
||||
@@ -57,7 +54,7 @@ export interface GatewayOperationCreateInput {
|
||||
scheduledAt?: string;
|
||||
}
|
||||
|
||||
export const GATEWAY_OPERATION_LOG_LEVELS = ['INFO', 'OUTPUT', 'ERROR'] as const;
|
||||
const GATEWAY_OPERATION_LOG_LEVELS = ['INFO', 'OUTPUT', 'ERROR'] as const;
|
||||
export type GatewayOperationLogLevel = (typeof GATEWAY_OPERATION_LOG_LEVELS)[number];
|
||||
|
||||
export interface GatewayOperationLogRecord {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const resolveGatewayProfileKoreanName = (profile: string, configuredName?
|
||||
return gatewayProfileKoreanNames.get(profile) ?? profile;
|
||||
};
|
||||
|
||||
export const compareGatewayProfiles = (
|
||||
const compareGatewayProfiles = (
|
||||
left: { profile: string; instanceKey: string },
|
||||
right: { profile: string; instanceKey: string }
|
||||
): number => {
|
||||
|
||||
Reference in New Issue
Block a user