refactor: app.js를 ES6 모듈로 분리
## 변경사항 - app.js (1370줄) → 7개 모듈 (1427줄) - ES6 import/export 문법 사용 - Alpine.js 호환성 유지 (window 전역 노출) ## 모듈 구조 - js/config.js: 상수/설정 (WIZARD_CONFIG, PRICING_DATA, MOCK_*) - js/api.js: ApiService - js/utils.js: formatPrice, switchTab, ping 시뮬레이션 - js/wizard.js: 서버 추천 마법사 로직 - js/pricing.js: 가격표 컴포넌트 - js/dashboard.js: 대시보드 및 텔레그램 연동 - js/app.js: 메인 통합 (모든 모듈 import) ## HTML 변경 - <script type="module" src="js/app.js">로 변경 - 기존 기능 모두 정상 작동 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* Shared proxy utilities for Cloudflare Pages Functions
|
||||
* Handles CORS, error responses, and Worker API forwarding
|
||||
*/
|
||||
|
||||
export interface Env {
|
||||
WORKER_API_KEY: string;
|
||||
WORKER_API_URL: string;
|
||||
DB: D1Database;
|
||||
}
|
||||
|
||||
export interface ErrorResponse {
|
||||
success: false;
|
||||
error: string;
|
||||
details?: any;
|
||||
}
|
||||
|
||||
const CORS_HEADERS = {
|
||||
'Access-Control-Allow-Origin': 'https://hosting.anvil.it.com',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Create CORS preflight response
|
||||
*/
|
||||
export function createCorsPreflightResponse(): Response {
|
||||
return new Response(null, {
|
||||
status: 204,
|
||||
headers: CORS_HEADERS,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create error response with CORS headers
|
||||
*/
|
||||
export function createErrorResponse(
|
||||
error: string,
|
||||
status: number = 500,
|
||||
details?: any
|
||||
): Response {
|
||||
const body: ErrorResponse = {
|
||||
success: false,
|
||||
error,
|
||||
...(details && { details }),
|
||||
};
|
||||
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: {
|
||||
...CORS_HEADERS,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy request to Worker API with authentication
|
||||
*/
|
||||
export async function proxyToWorker(
|
||||
env: Env,
|
||||
path: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<Response> {
|
||||
const workerUrl = `${env.WORKER_API_URL}${path}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(workerUrl, {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
'X-API-Key': env.WORKER_API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
// Clone response to add CORS headers
|
||||
const body = await response.text();
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: {
|
||||
...CORS_HEADERS,
|
||||
'Content-Type': response.headers.get('Content-Type') || 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`[Proxy] Failed to fetch ${workerUrl}:`, error);
|
||||
return createErrorResponse(
|
||||
'Failed to connect to API server',
|
||||
503,
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build query string from URL search params
|
||||
*/
|
||||
export function buildQueryString(searchParams: URLSearchParams): string {
|
||||
const params = searchParams.toString();
|
||||
return params ? `?${params}` : '';
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Health check endpoint
|
||||
* GET /api/health → Worker GET /health
|
||||
*/
|
||||
|
||||
import { type PagesFunction } from '@cloudflare/workers-types';
|
||||
import { Env, createCorsPreflightResponse, proxyToWorker } from '../_shared/proxy';
|
||||
|
||||
export const onRequestGet: PagesFunction<Env> = async ({ env }) => {
|
||||
return proxyToWorker(env, '/health', {
|
||||
method: 'GET',
|
||||
});
|
||||
};
|
||||
|
||||
export const onRequestOptions: PagesFunction<Env> = async () => {
|
||||
return createCorsPreflightResponse();
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Instances query endpoint
|
||||
* GET /api/instances → Worker GET /instances
|
||||
*/
|
||||
|
||||
import { type PagesFunction } from '@cloudflare/workers-types';
|
||||
import {
|
||||
Env,
|
||||
createCorsPreflightResponse,
|
||||
proxyToWorker,
|
||||
buildQueryString,
|
||||
} from '../_shared/proxy';
|
||||
|
||||
export const onRequestGet: PagesFunction<Env> = async ({ request, env }) => {
|
||||
const url = new URL(request.url);
|
||||
const queryString = buildQueryString(url.searchParams);
|
||||
|
||||
return proxyToWorker(env, `/instances${queryString}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
};
|
||||
|
||||
export const onRequestOptions: PagesFunction<Env> = async () => {
|
||||
return createCorsPreflightResponse();
|
||||
};
|
||||
@@ -8,7 +8,10 @@
|
||||
*/
|
||||
|
||||
import { type PagesFunction } from '@cloudflare/workers-types';
|
||||
import { Env, createCorsPreflightResponse } from '../_shared/proxy';
|
||||
|
||||
interface Env {
|
||||
DB: D1Database;
|
||||
}
|
||||
|
||||
interface InstanceRow {
|
||||
instance_id: string;
|
||||
@@ -201,5 +204,5 @@ export const onRequestGet: PagesFunction<Env> = async ({ env }) => {
|
||||
};
|
||||
|
||||
export const onRequestOptions: PagesFunction<Env> = async () => {
|
||||
return createCorsPreflightResponse();
|
||||
return new Response(null, { status: 204, headers: CORS_HEADERS });
|
||||
};
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Recommendation endpoint
|
||||
* POST /api/recommend → Worker POST /recommend
|
||||
*/
|
||||
|
||||
import { type PagesFunction } from '@cloudflare/workers-types';
|
||||
import {
|
||||
Env,
|
||||
createCorsPreflightResponse,
|
||||
createErrorResponse,
|
||||
proxyToWorker,
|
||||
} from '../_shared/proxy';
|
||||
|
||||
export const onRequestPost: PagesFunction<Env> = async ({ request, env }) => {
|
||||
try {
|
||||
// Read request body
|
||||
const body = await request.text();
|
||||
|
||||
// Validate JSON
|
||||
if (body) {
|
||||
try {
|
||||
JSON.parse(body);
|
||||
} catch {
|
||||
return createErrorResponse('Invalid JSON in request body', 400);
|
||||
}
|
||||
}
|
||||
|
||||
return proxyToWorker(env, '/recommend', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Recommend] Failed to process request:', error);
|
||||
return createErrorResponse(
|
||||
'Failed to process recommendation request',
|
||||
500,
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const onRequestOptions: PagesFunction<Env> = async () => {
|
||||
return createCorsPreflightResponse();
|
||||
};
|
||||
Reference in New Issue
Block a user