diff --git a/log-collector/.gitignore b/log-collector/.gitignore new file mode 100644 index 0000000000..073e02da0a --- /dev/null +++ b/log-collector/.gitignore @@ -0,0 +1,3 @@ +node_modules +.wrangler +.dev.vars diff --git a/log-collector/package.json b/log-collector/package.json new file mode 100644 index 0000000000..674b2fed34 --- /dev/null +++ b/log-collector/package.json @@ -0,0 +1,15 @@ +{ + "name": "log-collector", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "wrangler dev", + "deploy": "wrangler deploy", + "cf-typegen": "wrangler types" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250124.0", + "typescript": "^5.5.2", + "wrangler": "^3.101.0" + } +} diff --git a/log-collector/src/index.ts b/log-collector/src/index.ts new file mode 100644 index 0000000000..a325c9f061 --- /dev/null +++ b/log-collector/src/index.ts @@ -0,0 +1,49 @@ +export interface Env { + PROFOUND_API_URL: string; + PROFOUND_API_KEY: string; +} + +export default { + async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + const response = await fetch(request); + const responseClone = response.clone(); + + ctx.waitUntil(handleRequest(request, responseClone, env)); + return response; + } +} satisfies ExportedHandler; + +async function handleRequest(request: Request, response: Response, env: Env) { + const requestUrl = new URL(request.url); + + const headerSize = Array.from(response.headers.entries()).reduce( + (total, [key, value]) => total + key.length + value.length + 4, + 0 + ); + + const responseBody = await response.blob(); + const bodySize = responseBody.size; + const totalBytesSent = headerSize + bodySize; + + const logData = { + timestamp: Date.now(), + host: requestUrl.hostname, + method: request.method, + pathname: requestUrl.pathname, + query_params: Object.fromEntries(requestUrl.searchParams), + ip: request.headers.get('cf-connecting-ip'), + userAgent: request.headers.get('user-agent'), + referer: request.headers.get('referer'), + bytes: totalBytesSent, + status: response.status + }; + + await fetch(env.PROFOUND_API_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': env.PROFOUND_API_KEY + }, + body: JSON.stringify([logData]) + }).catch((error) => console.error('Failed to send logs:', error)); +} diff --git a/log-collector/tsconfig.json b/log-collector/tsconfig.json new file mode 100644 index 0000000000..f2af70dda3 --- /dev/null +++ b/log-collector/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types/2023-07-01"], + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/log-collector/wrangler.json b/log-collector/wrangler.json new file mode 100644 index 0000000000..5ba48ac535 --- /dev/null +++ b/log-collector/wrangler.json @@ -0,0 +1,16 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "log-collector", + "main": "src/index.ts", + "compatibility_date": "2025-01-29", + "observability": { + "enabled": true + }, + "route": { + "pattern": "appwrite.io/*", + "zone_name": "appwrite.io" + }, + "vars": { + "PROFOUND_API_URL": "https://artemis.api.tryprofound.com/v1/logs/cloudflare_worker" + } +}