diff --git a/CLAUDE.md b/CLAUDE.md index f2ca179..653502d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,6 @@ This is a GraphQL API server for OSWatcher that uses Neo4j as the database and p - GraphQL API with WebSocket subscriptions - REST API for blob downloads (authorization check disabled by default -- see below) - S3/MinIO integration for blob storage -- PostHog analytics proxy (production only) - Windows registry value filtering for sensitive data (disabled by default -- see below) **Open-source release note (2026-08-11):** blob download authorization and @@ -60,7 +59,6 @@ The main server file that orchestrates all components: - Sets up three endpoints: - `/graphql` - GraphQL API with WebSocket subscriptions - `/blob/:hash` - REST API for blob downloads - - `/events` - PostHog analytics proxy (production only) - Implements security features: - Rate limiting (100 requests/minute, 5 WebSocket connections per IP) - Query complexity validation (max 100 fields) @@ -126,7 +124,7 @@ See README.md for complete `.env` setup. Key variables: - **Neo4j**: `NEO4J_URI`, `NEO4J_USER`, `NEO4J_PASSWORD` - **Storage**: `OBJECT_STORAGE_URI`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, `MINIO_OBJECTS_BUCKET_NAME` - **Authorization**: `RESTRICTED_BRANCH_NAME` - branch containing restricted blobs -- **Optional**: `POSTHOG_HOST`, `POSTHOG_PROJECT_API_KEY`, `NODE_ENV`, `SENSITIVE_REGISTRY_VALUES` +- **Optional**: `NODE_ENV`, `SENSITIVE_REGISTRY_VALUES` ## Testing Strategy - Jest 30 with ts-jest for TypeScript support diff --git a/README.md b/README.md index 145bf98..2ca4940 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,6 @@ MINIO_OBJECTS_BUCKET_NAME=objects # re-enabled (see docs/reference/access-restrictions.md) RESTRICTED_BRANCH_NAME= -# Optional - PostHog Analytics (production only) -POSTHOG_HOST=https://us.i.posthog.com -POSTHOG_PROJECT_API_KEY= - # Optional - Environment NODE_ENV=development diff --git a/package-lock.json b/package-lock.json index 6ac9d7f..14792c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@graphql-tools/schema": "^10.0.29", "@neo4j/graphql": "^5.12.10", "@neo4j/graphql-ogm": "5.11.4", - "axios": "1.12.2", "cors": "^2.8.5", "debug": "^4.4.3", "dotenv": "17.2.2", @@ -5764,11 +5763,6 @@ "retry": "0.13.1" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/auto-bind": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", @@ -5795,17 +5789,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/babel-jest": { "version": "30.1.2", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.2.tgz", @@ -6358,17 +6341,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -6562,14 +6534,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6775,21 +6739,6 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -7332,26 +7281,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -7397,22 +7326,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -9300,6 +9213,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "peer": true, "engines": { "node": ">= 0.6" } @@ -9308,6 +9222,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -9961,12 +9876,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index e5f2d38..6dbf06c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "@graphql-tools/schema": "^10.0.29", "@neo4j/graphql": "^5.12.10", "@neo4j/graphql-ogm": "5.11.4", - "axios": "1.12.2", "cors": "^2.8.5", "debug": "^4.4.3", "dotenv": "17.2.2", diff --git a/src/index.ts b/src/index.ts index a05712b..7bc087b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,6 @@ import { expressMiddleware } from "@as-integrations/express5"; // that does matter is enforced server-side: rate limiting and restricted-branch // filtering. import cors from "cors"; -import axios from "axios"; import { createServer } from "http"; import { WebSocketServer } from "ws"; import { useServer } from "graphql-ws/use/ws"; @@ -55,13 +54,6 @@ const env = cleanEnv(process.env, { desc: "MinIO bucket name for blob storage", }), // Optional environment variables - POSTHOG_HOST: url({ - default: "https://us.i.posthog.com", - desc: "PostHog analytics host", - }), - POSTHOG_PROJECT_API_KEY: str({ - desc: "PostHog project API key", - }), NODE_ENV: str({ choices: ["development", "production", "test"], default: "development", @@ -327,53 +319,6 @@ async function main() { // Start Apollo Server await server.start(); - // PostHog events endpoint - only in production - if (isProduction && env.POSTHOG_PROJECT_API_KEY) { - app.use( - "/events", - cors(), - express.raw({ type: "*/*", limit: "10mb" }), - async (req: Request, res: Response) => { - try { - const posthogPath = req.originalUrl.replace( - "/events", - "", - ); - const fullUrl = `${env.POSTHOG_HOST}${posthogPath}`; - - // Forward the request exactly as received - const response = await axios({ - method: req.method, - url: fullUrl, - data: req.body, - headers: { - ...req.headers, - host: new URL(env.POSTHOG_HOST).host, - Authorization: `Bearer ${env.POSTHOG_PROJECT_API_KEY}`, - }, - decompress: false, - }); - - // Forward the response exactly as received - res.status(response.status); - Object.entries(response.headers).forEach( - ([key, value]) => { - res.setHeader(key, value); - }, - ); - res.send(response.data); - } catch (error: unknown) { - // Log full error for debugging but don't expose details - console.error("Error proxying PostHog event:", error); - res.status(502).json({ - error: "Service temporarily unavailable", - }); - } - }, - ); - console.log(`📊 PostHog events endpoint enabled in production`); - } - // Blob REST API - mounted before GraphQL app.use( "/blob", @@ -406,11 +351,6 @@ async function main() { console.log(`🚀 Server ready at http://localhost:4000/graphql`); console.log(`🔌 WebSocket ready at ws://localhost:4000/graphql`); console.log(`📦 Blob API ready at http://localhost:4000/blob`); - if (isProduction) { - console.log( - `📊 PostHog events endpoint ready at http://localhost:4000/events`, - ); - } }); } catch (error) { console.error("❌ Server startup failed:", error);