From c88c2e0dd0fc4ad675b02aeeab04586a4160d4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Hedenstr=C3=B6m?= <erik@hedenstroem.com> Date: Fri, 1 Mar 2024 15:34:46 +0000 Subject: [PATCH] Added checks for authorized keys --- .gitlab-ci.yml | 12 ++++++++++ src/index.ts | 59 +++++++++++++++++++++++--------------------------- wrangler.toml | 49 ++--------------------------------------- 3 files changed, 41 insertions(+), 79 deletions(-) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7143263 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,12 @@ +image: node:lts-alpine + +stages: + - publish + +publish: + stage: publish + only: + - main + script: + - npm ci + - npm run deploy diff --git a/src/index.ts b/src/index.ts index c9599f2..b18da2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,4 @@ -/** - * Welcome to Cloudflare Workers! This is your first worker. - * - * - Run `npm run dev` in your terminal to start a development server - * - Open a browser tab at http://localhost:8787/ to see your worker in action - * - Run `npm run deploy` to publish your worker - * - * Learn more at https://developers.cloudflare.com/workers/ - */ - export interface Env { - // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/ - // MY_KV_NAMESPACE: KVNamespace; - // - // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/ - // MY_DURABLE_OBJECT: DurableObjectNamespace; - // - // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/ - // MY_BUCKET: R2Bucket; - // - // Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/ - // MY_SERVICE: Fetcher; - // - // Example binding to a Queue. Learn more at https://developers.cloudflare.com/queues/javascript-apis/ - // MY_QUEUE: Queue; ALLOWED_OPENAI_KEYS: string; HELICONE_KEY: string; } @@ -33,23 +9,42 @@ export default { // The URL you want to proxy the requests to const url = new URL(request.url); - // Modify the request to point to the target URL - // For example, if you're proxying to https://example.com + // Modify the request to point to the Helicone URL url.hostname = "oai.hconeai.com"; - + + if (request.method === "OPTIONS") { + // Send OPTIONS request to the OpenAI API + url.hostname = "api.openai.com"; + } else { + // Check that there is a bearer token in the Authorization header + const authHeader = request.headers.get("Authorization"); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return new Response("Unauthorized", { status: 401 }); + } + // Check that the token is one of the allowed OpenAI keys + const token = authHeader.substring(7); + if (!env.ALLOWED_OPENAI_KEYS.split(",").includes(token)) { + return new Response("Forbidden", { status: 403 }); + } + } + + // Remove the /helicone-proxy prefix from the URL + if (url.pathname.startsWith('/helicone-proxy')) { + url.pathname = url.pathname.substring('/helicone-proxy'.length); + } + // Clone the request to modify it const modifiedRequest = new Request(url.toString(), request); - + // Add your custom header to the request modifiedRequest.headers.set("Host", url.hostname); modifiedRequest.headers.set("Helicone-Auth", "Bearer " + env.HELICONE_KEY); - console.log(modifiedRequest); - + // Make the fetch request with the modified request const response = await fetch(modifiedRequest); - + // Return the response from the target server return response; - //return new Response('Hello World!'); + }, }; diff --git a/wrangler.toml b/wrangler.toml index 476900f..b5e7b8f 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -2,50 +2,5 @@ name = "helicone-proxy" main = "src/index.ts" compatibility_date = "2024-02-23" -# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) -# Note: Use secrets to store sensitive data. -# Docs: https://developers.cloudflare.com/workers/platform/environment-variables -# [vars] -# MY_VARIABLE = "production_value" - -# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. -# Docs: https://developers.cloudflare.com/workers/runtime-apis/kv -# [[kv_namespaces]] -# binding = "MY_KV_NAMESPACE" -# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - -# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. -# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ -# [[r2_buckets]] -# binding = "MY_BUCKET" -# bucket_name = "my-bucket" - -# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. -# Docs: https://developers.cloudflare.com/queues/get-started -# [[queues.producers]] -# binding = "MY_QUEUE" -# queue = "my-queue" - -# Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. -# Docs: https://developers.cloudflare.com/queues/get-started -# [[queues.consumers]] -# queue = "my-queue" - -# Bind another Worker service. Use this binding to call another Worker without network overhead. -# Docs: https://developers.cloudflare.com/workers/platform/services -# [[services]] -# binding = "MY_SERVICE" -# service = "my-service" - -# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. -# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. -# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects -# [[durable_objects.bindings]] -# name = "MY_DURABLE_OBJECT" -# class_name = "MyDurableObject" - -# Durable Object migrations. -# Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations -# [[migrations]] -# tag = "v1" -# new_classes = ["MyDurableObject"] +[dev] + port = 3000 -- GitLab