This commit is contained in:
2026-02-12 10:24:03 +09:00
parent 3cea3e66d0
commit 8f1d75b4d5
41 changed files with 22902 additions and 43669 deletions

View File

@@ -1,6 +1,4 @@
import { createHash } from "node:crypto";
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { clearKisApprovalKeyCache } from "@/lib/kis/approval";
import type { KisCredentialInput } from "@/lib/kis/config";
import { getKisConfig } from "@/lib/kis/config";
@@ -26,10 +24,6 @@ interface KisTokenCache {
expiresAt: number;
}
interface PersistedTokenCache {
[cacheKey: string]: KisTokenCache;
}
interface KisRevokeResponse {
code?: number | string;
message?: string;
@@ -39,7 +33,6 @@ interface KisRevokeResponse {
const tokenCacheMap = new Map<string, KisTokenCache>();
const tokenIssueInFlightMap = new Map<string, Promise<KisTokenCache>>();
const TOKEN_REFRESH_BUFFER_MS = 60 * 1000;
const TOKEN_CACHE_FILE_PATH = join(process.cwd(), ".tmp", "kis-token-cache.json");
function hashKey(value: string) {
return createHash("sha256").update(value).digest("hex");
@@ -50,59 +43,6 @@ function getTokenCacheKey(credentials?: KisCredentialInput) {
return `${config.tradingEnv}:${hashKey(config.appKey)}`;
}
async function readPersistedTokenCache() {
try {
const raw = await readFile(TOKEN_CACHE_FILE_PATH, "utf8");
return JSON.parse(raw) as PersistedTokenCache;
} catch {
return {};
}
}
async function writePersistedTokenCache(next: PersistedTokenCache) {
await mkdir(join(process.cwd(), ".tmp"), { recursive: true });
await writeFile(TOKEN_CACHE_FILE_PATH, JSON.stringify(next), "utf8");
}
async function getPersistedToken(cacheKey: string) {
const cache = await readPersistedTokenCache();
const token = cache[cacheKey];
if (!token) return null;
if (token.expiresAt - TOKEN_REFRESH_BUFFER_MS <= Date.now()) {
delete cache[cacheKey];
await writePersistedTokenCache(cache);
return null;
}
return token;
}
async function setPersistedToken(cacheKey: string, token: KisTokenCache) {
const cache = await readPersistedTokenCache();
cache[cacheKey] = token;
await writePersistedTokenCache(cache);
}
async function clearPersistedToken(cacheKey: string) {
const cache = await readPersistedTokenCache();
if (!(cacheKey in cache)) return;
delete cache[cacheKey];
if (Object.keys(cache).length === 0) {
try {
await unlink(TOKEN_CACHE_FILE_PATH);
} catch {
// ignore when file does not exist
}
return;
}
await writePersistedTokenCache(cache);
}
function tryParseTokenResponse(rawText: string): KisTokenResponse {
try {
return JSON.parse(rawText) as KisTokenResponse;
@@ -226,12 +166,6 @@ export async function getKisAccessToken(credentials?: KisCredentialInput) {
return cached.token;
}
const persisted = await getPersistedToken(cacheKey);
if (persisted) {
tokenCacheMap.set(cacheKey, persisted);
return persisted.token;
}
const inFlight = tokenIssueInFlightMap.get(cacheKey);
if (inFlight) {
const shared = await inFlight;
@@ -246,7 +180,6 @@ export async function getKisAccessToken(credentials?: KisCredentialInput) {
});
tokenCacheMap.set(cacheKey, next);
await setPersistedToken(cacheKey, next);
return next.token;
}
@@ -289,7 +222,6 @@ export async function revokeKisAccessToken(credentials?: KisCredentialInput) {
tokenCacheMap.delete(cacheKey);
tokenIssueInFlightMap.delete(cacheKey);
await clearPersistedToken(cacheKey);
clearKisApprovalKeyCache(credentials);
return payload.message ?? "액세스 토큰 폐기가 완료되었습니다.";