refactor: extract common bandwidth formatting utilities

- Add BandwidthInfo interface to types.ts (single source of truth)
- Create formatters.ts with formatTB() and formatTrafficInfo()
- Display CDN hit rate and gross/origin traffic in recommendations
- Fix floating point formatting (consistent decimal places)
- Fix undefined handling in toLocaleString() calls
- Unify overage detection logic (overage_tb > 0 && cost > 0)
- Add CDN hit rate range validation (0-100%)
- Extract CDN_CACHE_HIT_RATES constants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-27 12:33:58 +09:00
parent 0277f0539b
commit 6392a17d4f
4 changed files with 152 additions and 40 deletions

View File

@@ -9,9 +9,10 @@
* - Brave Search / Context7 도구로 최신 트렌드 반영
*/
import type { Env, ServerSession } from './types';
import type { Env, ServerSession, BandwidthInfo } from './types';
import { createLogger } from './utils/logger';
import { executeSearchWeb, executeLookupDocs } from './tools/search-tool';
import { formatTrafficInfo } from './utils/formatters';
const logger = createLogger('server-agent');
@@ -215,11 +216,7 @@ interface RecommendResponse {
estimated_capacity?: {
max_concurrent_users?: number;
};
bandwidth_analysis?: {
estimated_monthly_tb?: number;
overage_tb?: number;
overage_cost_krw?: number;
};
bandwidth_info?: BandwidthInfo;
}>;
}
@@ -561,12 +558,32 @@ export async function processServerConsultation(
plan: selected.plan_name
});
// 트래픽 정보 포맷팅
let trafficInfo = '';
if (selected.price.estimated_monthly_tb !== undefined) {
const bandwidthInfo: BandwidthInfo = {
included_transfer_tb: selected.price.bandwidth_tb,
overage_cost_per_gb: 0,
overage_cost_per_tb: 0,
estimated_monthly_tb: selected.price.estimated_monthly_tb,
estimated_overage_tb: selected.price.overage_tb || 0,
estimated_overage_cost: selected.price.overage_cost_krw || 0,
total_estimated_cost: selected.price.monthly_krw + (selected.price.overage_cost_krw || 0),
currency: 'KRW',
gross_monthly_tb: selected.price.gross_monthly_tb,
cdn_cache_hit_rate: selected.price.cdn_cache_hit_rate,
};
trafficInfo = `${formatTrafficInfo(bandwidthInfo, 'KRW')}\n`;
}
return `🖥️ ${selected.plan_name} 신청 확인\n\n` +
`• 제공사: ${selected.provider}\n` +
`• 스펙: ${selected.specs.vcpu}vCPU / ${selected.specs.ram_gb}GB / ${selected.specs.storage_gb}GB\n` +
`• 스펙: ${selected.specs.vcpu}vCPU / ${selected.specs.ram_gb}GB RAM / ${selected.specs.storage_gb}GB SSD\n` +
`• 리전: ${selected.region.name} (${selected.region.code})\n` +
`• 가격: ₩${selected.price.monthly_krw.toLocaleString()}/월\n\n` +
`신청하시겠습니까?\n\n` +
`• 가격: ₩${selected.price.monthly_krw.toLocaleString()}/월\n` +
`• 대역폭: ${selected.price.bandwidth_tb}TB 포함\n` +
trafficInfo +
`\n신청하시겠습니까?\n\n` +
`__KEYBOARD__${keyboardData}__END__`;
} else {
return `번호를 다시 확인해주세요. 1번부터 ${session.lastRecommendation.recommendations.length}번 중에서 선택해주세요.`;
@@ -646,7 +663,12 @@ export async function processServerConsultation(
},
price: {
monthly_krw: Math.round(rec.server.monthly_price),
bandwidth_tb: rec.server.transfer_tb
bandwidth_tb: rec.server.transfer_tb,
estimated_monthly_tb: rec.bandwidth_info?.estimated_monthly_tb,
gross_monthly_tb: rec.bandwidth_info?.gross_monthly_tb,
cdn_cache_hit_rate: rec.bandwidth_info?.cdn_cache_hit_rate,
overage_tb: rec.bandwidth_info?.estimated_overage_tb,
overage_cost_krw: rec.bandwidth_info?.estimated_overage_cost
},
score: rec.score,
max_users: rec.estimated_capacity?.max_concurrent_users || 0