(request: Request, env: Env, ctx: ExecutionContext)
| 140 | */ |
| 141 | const worker = { |
| 142 | async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { |
| 143 | // logger.info(`Received request: ${request.method} ${request.url}`); |
| 144 | // --- Pre-flight Checks --- |
| 145 | |
| 146 | // 1. Critical configuration check: Ensure custom domain is set. |
| 147 | const previewDomain = getPreviewDomain(env); |
| 148 | if (!previewDomain || previewDomain.trim() === '') { |
| 149 | logger.error('FATAL: env.CUSTOM_DOMAIN is not configured in wrangler.toml or the Cloudflare dashboard.'); |
| 150 | return new Response('Server configuration error: Application domain is not set.', { status: 500 }); |
| 151 | } |
| 152 | |
| 153 | const url = new URL(request.url); |
| 154 | const { hostname, pathname } = url; |
| 155 | |
| 156 | // 2. Security: Immediately reject any requests made via an IP address. |
| 157 | const ipRegex = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; |
| 158 | if (ipRegex.test(hostname)) { |
| 159 | return new Response('Access denied. Please use the assigned domain name.', { status: 403 }); |
| 160 | } |
| 161 | |
| 162 | // --- Domain-based Routing --- |
| 163 | |
| 164 | // Normalize hostnames for both local development (localhost) and production. |
| 165 | const isMainDomainRequest = |
| 166 | hostname === env.CUSTOM_DOMAIN || hostname === 'localhost'; |
| 167 | const isSubdomainRequest = |
| 168 | hostname.endsWith(`.${previewDomain}`) || |
| 169 | (hostname.endsWith('.localhost') && hostname !== 'localhost'); |
| 170 | |
| 171 | // Route 1: Main Platform Request (e.g., build.cloudflare.dev or localhost) |
| 172 | if (isMainDomainRequest) { |
| 173 | // Handle Git protocol endpoints directly |
| 174 | // Route: /apps/:id.git/info/refs or /apps/:id.git/git-upload-pack |
| 175 | if (isGitProtocolRequest(pathname)) { |
| 176 | return handleGitProtocolRequest(request, env, ctx); |
| 177 | } |
| 178 | |
| 179 | // Serve static assets for all non-API routes from the ASSETS binding. |
| 180 | if (!pathname.startsWith('/api/')) { |
| 181 | return env.ASSETS.fetch(request); |
| 182 | } |
| 183 | // AI Gateway proxy for generated apps |
| 184 | if (pathname.startsWith('/api/proxy/openai')) { |
| 185 | // Only handle requests from valid origins of the preview domain |
| 186 | const origin = request.headers.get('Origin'); |
| 187 | const previewDomain = getPreviewDomain(env); |
| 188 | |
| 189 | logger.info(`Origin: ${origin}, Preview Domain: ${previewDomain}`); |
| 190 | |
| 191 | return proxyToAiGateway(request, env, ctx); |
| 192 | // if (origin && origin.endsWith(`.${previewDomain}`)) { |
| 193 | // return proxyToAiGateway(request, env, ctx); |
| 194 | // } |
| 195 | // logger.warn(`Access denied. Invalid origin: ${origin}, preview domain: ${previewDomain}`); |
| 196 | // return new Response('Access denied. Invalid origin.', { status: 403 }); |
| 197 | } |
| 198 | |
| 199 | // Handle all API requests with the main Hono application. |
no test coverage detected