feat: add optimistic locking and improve type safety
- Implement optimistic locking for deposit balance updates - Prevent race conditions in concurrent deposit requests - Add automatic retry with exponential backoff (max 3 attempts) - Add version column to user_deposits table - Improve type safety across codebase - Add explicit types for Namecheap API responses - Add typed function arguments (ManageDepositArgs, etc.) - Remove `any` types from deposit-agent and tool files - Add reconciliation job for balance integrity verification - Compare user_deposits.balance vs SUM(confirmed transactions) - Alert admin on discrepancy detection - Set up test environment with Vitest + Miniflare - Add 50+ test cases for deposit system - Add helper functions for test data creation - Update documentation - Add migration guide for version columns - Document optimistic locking patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,11 @@
|
||||
import type { Env } from '../types';
|
||||
import type {
|
||||
Env,
|
||||
OpenAIResponse,
|
||||
BraveSearchResponse,
|
||||
BraveSearchResult,
|
||||
Context7SearchResponse,
|
||||
Context7DocsResponse
|
||||
} from '../types';
|
||||
import { retryWithBackoff, RetryError } from '../utils/retry';
|
||||
import { createLogger } from '../utils/logger';
|
||||
import { getOpenAIUrl } from '../utils/api-urls';
|
||||
@@ -85,7 +92,7 @@ export async function executeSearchWeb(args: { query: string }, env?: Env): Prom
|
||||
{ maxRetries: 2 } // 번역은 중요하지 않으므로 재시도 2회로 제한
|
||||
);
|
||||
if (translateRes.ok) {
|
||||
const translateData = await translateRes.json() as any;
|
||||
const translateData = await translateRes.json() as OpenAIResponse;
|
||||
translatedQuery = translateData.choices?.[0]?.message?.content?.trim() || query;
|
||||
logger.info('번역', { original: query, translated: translatedQuery });
|
||||
}
|
||||
@@ -112,7 +119,7 @@ export async function executeSearchWeb(args: { query: string }, env?: Env): Prom
|
||||
if (!response.ok) {
|
||||
return `🔍 검색 오류: ${response.status}`;
|
||||
}
|
||||
const data = await response.json() as any;
|
||||
const data = await response.json() as BraveSearchResponse;
|
||||
|
||||
// Web 검색 결과 파싱
|
||||
const webResults = data.web?.results || [];
|
||||
@@ -120,7 +127,7 @@ export async function executeSearchWeb(args: { query: string }, env?: Env): Prom
|
||||
return `🔍 "${query}"에 대한 검색 결과가 없습니다.`;
|
||||
}
|
||||
|
||||
const results = webResults.slice(0, 3).map((r: any, i: number) =>
|
||||
const results = webResults.slice(0, 3).map((r: BraveSearchResult, i: number) =>
|
||||
`${i + 1}. <b>${r.title}</b>\n ${r.description}\n ${r.url}`
|
||||
).join('\n\n');
|
||||
|
||||
@@ -149,7 +156,7 @@ export async function executeLookupDocs(args: { library: string; query: string }
|
||||
() => fetch(searchUrl),
|
||||
{ maxRetries: 3 }
|
||||
);
|
||||
const searchData = await searchResponse.json() as any;
|
||||
const searchData = await searchResponse.json() as Context7SearchResponse;
|
||||
|
||||
if (!searchData.libraries?.length) {
|
||||
return `📚 "${library}" 라이브러리를 찾을 수 없습니다.`;
|
||||
@@ -163,7 +170,7 @@ export async function executeLookupDocs(args: { library: string; query: string }
|
||||
() => fetch(docsUrl),
|
||||
{ maxRetries: 3 }
|
||||
);
|
||||
const docsData = await docsResponse.json() as any;
|
||||
const docsData = await docsResponse.json() as Context7DocsResponse;
|
||||
|
||||
if (docsData.error) {
|
||||
return `📚 문서 조회 실패: ${docsData.message || docsData.error}`;
|
||||
|
||||
Reference in New Issue
Block a user