차트 설정 수정

This commit is contained in:
Macbook
2026-06-01 01:35:31 +09:00
parent 332019866a
commit f2208aab99
28 changed files with 546 additions and 333 deletions
+47 -7
View File
@@ -90,6 +90,18 @@ async function requestOrThrow<T>(path: string, init?: RequestInit): Promise<T> {
return JSON.parse(text) as T;
}
/** 서버에 없는 전략 ID — 삭제·만료 후 반복 404 방지 (태블릿·Safari 네트워크 로그 완화) */
const strategyMissingIds = new Set<number>();
export function isStrategyMissingOnServer(strategyId: number): boolean {
return strategyId > 0 && strategyMissingIds.has(strategyId);
}
function markStrategyMissingFromPath(pathOnly: string): void {
const m = pathOnly.match(/^\/strategies\/(\d+)(?:\/|$)/);
if (m) strategyMissingIds.add(Number(m[1]));
}
async function request<T>(path: string, init?: RequestInit): Promise<T | null> {
try {
const res = await fetch(`${API_BASE}${path}`, {
@@ -100,8 +112,16 @@ async function request<T>(path: string, init?: RequestInit): Promise<T | null> {
if (!res.ok) {
const method = init?.method ?? 'GET';
const guest403 = res.status === 403 && !hasRegisteredUser();
const missing404 = res.status === 404 && method === 'GET';
if (!guest403 && !missing404) {
const pathOnly = path.split('?')[0];
if (res.status === 404 && /^\/strategies\/\d+/.test(pathOnly)) {
markStrategyMissingFromPath(pathOnly);
}
const quiet404 = res.status === 404 && (
method === 'GET'
|| pathOnly.includes('/repair-timeframes')
|| /^\/strategies\/\d+/.test(pathOnly)
);
if (!guest403 && !quiet404) {
console.error(`[backendApi] ${method} ${path}${res.status}`);
}
return null;
@@ -1061,7 +1081,7 @@ export async function loadStrategies(): Promise<StrategyDto[]> {
/** 단일 전략 로드 */
export async function loadStrategy(id: number): Promise<StrategyDto | null> {
if (!hasRegisteredUser()) return null;
if (!hasRegisteredUser() || id <= 0 || strategyMissingIds.has(id)) return null;
return request<StrategyDto>(`/strategies/${id}`);
}
@@ -1401,10 +1421,30 @@ export async function loadStrategyTimeframes(strategyId: number): Promise<string
return list?.length ? list : ['1m'];
}
/** DB DSL TIMEFRAME 래핑 보정 (3분봉 전략이 1m으로 평가되는 레거시 수정) */
export async function repairStrategyTimeframes(strategyId: number): Promise<void> {
if (!hasRegisteredUser()) return;
await request(`/strategies/${strategyId}/repair-timeframes`, { method: 'POST' });
const repairSkipIds = new Set<number>();
const repairInflight = new Set<number>();
/** DB DSL TIMEFRAME 래핑 보정 — 전략 없으면 호출 안 함(404 방지) */
export async function repairStrategyTimeframes(strategyId: number): Promise<boolean> {
if (!hasRegisteredUser() || strategyId <= 0 || strategyMissingIds.has(strategyId)) return false;
if (repairSkipIds.has(strategyId) || repairInflight.has(strategyId)) return false;
repairInflight.add(strategyId);
try {
const exists = await loadStrategy(strategyId);
if (!exists) {
repairSkipIds.add(strategyId);
return false;
}
const ok = await request<StrategyDto>(
`/strategies/${strategyId}/repair-timeframes`,
{ method: 'POST' },
);
if (!ok) repairSkipIds.add(strategyId);
return ok != null;
} finally {
repairInflight.delete(strategyId);
}
}
/** 차트 실시간 파이프라인 pin (STOMP warm-up) */