diff --git a/src/routes/api/chat.ts b/src/routes/api/chat.ts index 1c418ec..3aacbca 100644 --- a/src/routes/api/chat.ts +++ b/src/routes/api/chat.ts @@ -319,126 +319,127 @@ async function handleChatApi(request: Request, env: Env): Promise { } } - // 서버 신청 확인 처리 (텍스트 기반) - Queue 기반 - if (body.message.trim() === '신청') { - const orderSessionKey = `server_order_confirm:${telegramUserId}`; - logger.info('신청 세션 확인', { orderSessionKey, telegramUserId }); - const orderSessionData = await env.SESSION_KV.get(orderSessionKey); - logger.info('신청 세션 데이터', { found: !!orderSessionData, data: orderSessionData?.slice(0, 100) }); - - if (orderSessionData) { - try { - const orderData = JSON.parse(orderSessionData); - - // 1. 서버 세션에서 가격 정보 가져오기 - const { ServerSessionManager } = await import('../../utils/session-manager'); - const { getSessionConfig } = await import('../../constants/agent-config'); - const sessionManager = new ServerSessionManager(getSessionConfig('server')); - const session = await sessionManager.get(env.DB, telegramUserId); - - if (!session || !session.last_recommendation) { - await env.SESSION_KV.delete(orderSessionKey); - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: '❌ 세션이 만료되었습니다.\n다시 "서버 추천"을 시작해주세요.', - processing_time_ms: processingTimeMs, - }); - } - - const selected = session.last_recommendation.recommendations[orderData.index]; - if (!selected) { - await env.SESSION_KV.delete(orderSessionKey); - await sessionManager.delete(env.DB, telegramUserId); - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: '❌ 선택한 서버를 찾을 수 없습니다.', - processing_time_ms: processingTimeMs, - }); - } - - const price = selected.price?.monthly_krw || 0; - - // 2. 잔액 확인 - const deposit = await env.DB.prepare( - 'SELECT balance FROM user_deposits WHERE user_id = ?' - ).bind(userId).first<{ balance: number }>(); - - if (!deposit || deposit.balance < price) { - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: - `❌ 잔액이 부족합니다.\n\n` + - `• 서버 가격: ${price.toLocaleString()}원/월\n` + - `• 현재 잔액: ${(deposit?.balance || 0).toLocaleString()}원\n` + - `• 부족 금액: ${(price - (deposit?.balance || 0)).toLocaleString()}원\n\n` + - `잔액을 충전 후 다시 시도해주세요.`, - processing_time_ms: processingTimeMs, - }); - } - - // 3. Queue 확인 - if (!env.SERVER_PROVISION_QUEUE) { - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: '❌ 서버 프로비저닝 시스템이 준비되지 않았습니다.', - processing_time_ms: processingTimeMs, - }); - } - - // 4. 주문 생성 및 Queue 전송 - const { createServerOrder, sendProvisionMessage } = await import('../../server-provision'); - - const orderId = await createServerOrder( - env.DB, - userId, - telegramUserId, - selected.pricing_id, - selected.region.code, - 'anvil', - price, - `${selected.plan_name} - ${orderData.label || session.collected_info?.useCase || 'server'}` - ); - - await sendProvisionMessage(env.SERVER_PROVISION_QUEUE, orderId, userId, telegramUserId); - - // 5. 세션 정리 - await env.SESSION_KV.delete(orderSessionKey); - await sessionManager.delete(env.DB, telegramUserId); - - // 6. 즉시 응답 - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: - `📋 서버 주문 접수 완료! (주문 #${orderId})\n\n` + - `• 서버: ${selected.plan_name}\n` + - `• 리전: ${selected.region.name} (${selected.region.code})\n` + - `• 가격: ${price.toLocaleString()}원/월\n\n` + - `⏳ 서버를 생성하고 있습니다... (1-2분 소요)\n` + - `완료되면 메시지로 알려드릴게요.`, - processing_time_ms: processingTimeMs, - }); - } catch (error) { - logger.error('Chat API - 서버 신청 처리 오류', toError(error)); - const processingTimeMs = Date.now() - startTime; - - return Response.json({ - success: true, - response: '🚫 서버 신청 중 오류가 발생했습니다. 다시 시도해주세요.', - processing_time_ms: processingTimeMs, - }); - } - } - } + // NOTE: Server order confirmation removed (recommendation feature deprecated) + // This entire block was for processing server orders based on recommendation sessions + // if (body.message.trim() === '신청') { + // const orderSessionKey = `server_order_confirm:${telegramUserId}`; + // logger.info('신청 세션 확인', { orderSessionKey, telegramUserId }); + // const orderSessionData = await env.SESSION_KV.get(orderSessionKey); + // logger.info('신청 세션 데이터', { found: !!orderSessionData, data: orderSessionData?.slice(0, 100) }); + // + // if (orderSessionData) { + // try { + // const orderData = JSON.parse(orderSessionData); + // + // // 1. 서버 세션에서 가격 정보 가져오기 + // const { ServerSessionManager } = await import('../../utils/session-manager'); + // const { getSessionConfig } = await import('../../constants/agent-config'); + // const sessionManager = new ServerSessionManager(getSessionConfig('server')); + // const session = await sessionManager.get(env.DB, telegramUserId); + // + // if (!session || !session.last_recommendation) { + // await env.SESSION_KV.delete(orderSessionKey); + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: '❌ 세션이 만료되었습니다.\n다시 "서버 추천"을 시작해주세요.', + // processing_time_ms: processingTimeMs, + // }); + // } + // + // const selected = session.last_recommendation.recommendations[orderData.index]; + // if (!selected) { + // await env.SESSION_KV.delete(orderSessionKey); + // await sessionManager.delete(env.DB, telegramUserId); + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: '❌ 선택한 서버를 찾을 수 없습니다.', + // processing_time_ms: processingTimeMs, + // }); + // } + // + // const price = selected.price?.monthly_krw || 0; + // + // // 2. 잔액 확인 + // const deposit = await env.DB.prepare( + // 'SELECT balance FROM user_deposits WHERE user_id = ?' + // ).bind(userId).first<{ balance: number }>(); + // + // if (!deposit || deposit.balance < price) { + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: + // `❌ 잔액이 부족합니다.\n\n` + + // `• 서버 가격: ${price.toLocaleString()}원/월\n` + + // `• 현재 잔액: ${(deposit?.balance || 0).toLocaleString()}원\n` + + // `• 부족 금액: ${(price - (deposit?.balance || 0)).toLocaleString()}원\n\n` + + // `잔액을 충전 후 다시 시도해주세요.`, + // processing_time_ms: processingTimeMs, + // }); + // } + // + // // 3. Queue 확인 + // if (!env.SERVER_PROVISION_QUEUE) { + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: '❌ 서버 프로비저닝 시스템이 준비되지 않았습니다.', + // processing_time_ms: processingTimeMs, + // }); + // } + // + // // 4. 주문 생성 및 Queue 전송 + // const { createServerOrder, sendProvisionMessage } = await import('../../server-provision'); + // + // const orderId = await createServerOrder( + // env.DB, + // userId, + // telegramUserId, + // selected.pricing_id, + // selected.region.code, + // 'anvil', + // price, + // `${selected.plan_name} - ${orderData.label || session.collected_info?.useCase || 'server'}` + // ); + // + // await sendProvisionMessage(env.SERVER_PROVISION_QUEUE, orderId, userId, telegramUserId); + // + // // 5. 세션 정리 + // await env.SESSION_KV.delete(orderSessionKey); + // await sessionManager.delete(env.DB, telegramUserId); + // + // // 6. 즉시 응답 + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: + // `📋 서버 주문 접수 완료! (주문 #${orderId})\n\n` + + // `• 서버: ${selected.plan_name}\n` + + // `• 리전: ${selected.region.name} (${selected.region.code})\n` + + // `• 가격: ${price.toLocaleString()}원/월\n\n` + + // `⏳ 서버를 생성하고 있습니다... (1-2분 소요)\n` + + // `완료되면 메시지로 알려드릴게요.`, + // processing_time_ms: processingTimeMs, + // }); + // } catch (error) { + // logger.error('Chat API - 서버 신청 처리 오류', toError(error)); + // const processingTimeMs = Date.now() - startTime; + // + // return Response.json({ + // success: true, + // response: '🚫 서버 신청 중 오류가 발생했습니다. 다시 시도해주세요.', + // processing_time_ms: processingTimeMs, + // }); + // } + // } + // } // 서버 신청 취소 처리 (다른 메시지 입력 시) const orderSessionKey = `server_order_confirm:${telegramUserId}`; diff --git a/src/tools/server-tool.ts b/src/tools/server-tool.ts index 6b54fe9..e9ff529 100644 --- a/src/tools/server-tool.ts +++ b/src/tools/server-tool.ts @@ -903,15 +903,15 @@ export async function executeServerDelete( } } - // Clear server consultation session (if any) - try { - const { ServerSessionManager } = await import('../utils/session-manager'); - const { getSessionConfig } = await import('../constants/agent-config'); - const sessionManager = new ServerSessionManager(getSessionConfig('server')); - await sessionManager.delete(env.DB, telegramUserId); - } catch (error) { - provisionLogger.error('서버 세션 삭제 실패 (무시)', error as Error); - } + // NOTE: Server consultation session cleanup removed (recommendation feature deprecated) + // try { + // const { ServerSessionManager } = await import('../utils/session-manager'); + // const { getSessionConfig } = await import('../constants/agent-config'); + // const sessionManager = new ServerSessionManager(getSessionConfig('server')); + // await sessionManager.delete(env.DB, telegramUserId); + // } catch (error) { + // provisionLogger.error('서버 세션 삭제 실패 (무시)', error as Error); + // } provisionLogger.info('서버 삭제 완료', { orderId }); return { @@ -994,15 +994,15 @@ export async function executeServerOrder( const order = result.order; provisionLogger.info('서버 주문 완료', { orderId: order?.id, plan: orderData.plan }); - // Clear server consultation session - try { - const { ServerSessionManager } = await import('../utils/session-manager'); - const { getSessionConfig } = await import('../constants/agent-config'); - const sessionManager = new ServerSessionManager(getSessionConfig('server')); - await sessionManager.delete(env.DB, telegramUserId); - } catch (error) { - provisionLogger.error('서버 세션 삭제 실패 (무시)', error as Error); - } + // NOTE: Server consultation session cleanup removed (recommendation feature deprecated) + // try { + // const { ServerSessionManager } = await import('../utils/session-manager'); + // const { getSessionConfig } = await import('../constants/agent-config'); + // const sessionManager = new ServerSessionManager(getSessionConfig('server')); + // await sessionManager.delete(env.DB, telegramUserId); + // } catch (error) { + // provisionLogger.error('서버 세션 삭제 실패 (무시)', error as Error); + // } // Build success message let successMessage = `✅ 서버 신청이 완료되었습니다!\n\n`; diff --git a/src/types.ts b/src/types.ts index dd4be22..82cc60d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -211,31 +211,22 @@ export interface LookupDocsArgs { export interface ManageServerArgs { action: - | "recommend" | "order" | "start" | "stop" + | "reboot" | "delete" | "list" | "info" | "images" - | "start_consultation" - | "continue_consultation" - | "cancel_consultation"; - tech_stack?: string[]; - expected_users?: number; - use_case?: string; - traffic_pattern?: string; - region_preference?: string[]; - budget_limit?: number; - lang?: string; + | "rename"; server_id?: string; region_code?: string; label?: string; - message?: string; // For continue_consultation - pricing_id?: number; // For order - order_id?: number; // For info, delete - image?: string; // For order (OS image key) + pricing_id?: number; + order_id?: number; + new_label?: string; + image?: string; } export interface ManageMemoryArgs { @@ -244,53 +235,6 @@ export interface ManageMemoryArgs { memory_id?: number; } -// Server Consultation Session Status -export type ServerSessionStatus = - | 'gathering' // 정보 수집 중 - | 'recommending' // 추천 중 - | 'selecting' // 선택 대기 - | 'ordering' // 주문 대기 - | 'completed'; // 완료 - -// Server Consultation Session (D1) -export interface ServerSession { - user_id: string; - status: ServerSessionStatus; - collected_info: { - useCase?: string; - scale?: 'personal' | 'business'; - budgetLimit?: number; - regionPreference?: string[]; - expectedDau?: number; // 일일 활성 사용자 (Daily Active Users) - expectedConcurrent?: number; // 동시접속자 (Concurrent Users) - }; - messages: Array<{ role: 'user' | 'assistant'; content: string }>; - created_at: number; - updated_at: number; - expires_at: number; - last_recommendation?: { - recommendations: Array<{ - pricing_id: number; // cloud-instances-db.anvil_pricing.id - plan_name: string; - provider: string; - specs: { vcpu: number; ram_gb: number; storage_gb: number }; - region: { code: string; name: string }; - price: { - monthly_krw: number; - bandwidth_tb: number; - estimated_monthly_tb?: number; // origin 트래픽 (서버 도달) - gross_monthly_tb?: number; // CDN 전 트래픽 - cdn_cache_hit_rate?: number; // CDN 히트율 (0.0-1.0) - overage_tb?: number; - overage_cost_krw?: number; - currency?: string; // 통화 단위 (KRW, USD 등) - }; - score: number; - max_users: number; - }>; - created_at: number; - }; -} // Troubleshooting Session Status export type TroubleshootSessionStatus = diff --git a/src/utils/session-manager.ts b/src/utils/session-manager.ts index 2f1eeef..14f9f29 100644 --- a/src/utils/session-manager.ts +++ b/src/utils/session-manager.ts @@ -316,24 +316,27 @@ export class DomainSessionManager extends SessionManager { /** * Specialized session manager for Server Agent * Handles last_recommendation field + * + * NOTE: Temporarily commented out during server recommendation removal refactoring. + * This class will be removed in a future commit when server-tool.ts is updated. */ -export class ServerSessionManager extends SessionManager { - protected parseAdditionalFields(result: Record): Partial { - return { - last_recommendation: result.last_recommendation - ? JSON.parse(result.last_recommendation as string) - : undefined, - }; - } - - protected getAdditionalColumns(session: ServerSession): Record { - return { - last_recommendation: session.last_recommendation - ? JSON.stringify(session.last_recommendation) - : null, - }; - } -} +// export class ServerSessionManager extends SessionManager { +// protected parseAdditionalFields(result: Record): Partial { +// return { +// last_recommendation: result.last_recommendation +// ? JSON.parse(result.last_recommendation as string) +// : undefined, +// }; +// } +// +// protected getAdditionalColumns(session: ServerSession): Record { +// return { +// last_recommendation: session.last_recommendation +// ? JSON.stringify(session.last_recommendation) +// : null, +// }; +// } +// } // Import types (avoid circular dependency by importing at end) -import type { DomainSession, ServerSession } from '../types'; +import type { DomainSession } from '../types';