07f7c65054
* Introducing Modular Asynchronous Reliable Queueing System (MarQS). Works in dev * Convert MarQS to using lua and dealing with concurrency * Simplified the timeout queue and current concurrency is now a set instead of a flat value (to support idempotency) * Implement task heartbeating and reconnect the background workers CLI when the websocket connection reconnects * Start adding internal telemetry support for the server * Get env vars to work in dev and implement prisma tracing in webapp * Cleanup telemetry and implement it in the consumer * Implement dequeuing a message from a parent shared queue * Implement a custom logger exporter instead of using console log exporter * Use node instead of shell for generating protocol buffer code * Propogate trace context into debug logs, and allow turning off logger exporter through env vars * Switch to using baselime for internal otel data * Make orgMember optional to fix type issues * Provide the CLI dev env vars through the CLI, don’t build dotenv into facade * Removed the logger import * Address Matt’s comments * Addressing more of Matt’s comments * Handle sending an execution after a websocket connection closes * Remove auth from the env attributes to prevent obfuscation
95 lines
2.4 KiB
TypeScript
95 lines
2.4 KiB
TypeScript
import { Prettify } from "@trigger.dev/core";
|
|
import { z } from "zod";
|
|
import {
|
|
findEnvironmentByApiKey,
|
|
findEnvironmentByPublicApiKey,
|
|
} from "~/models/runtimeEnvironment.server";
|
|
|
|
type Optional<T, K extends keyof T> = Prettify<Omit<T, K> & Partial<Pick<T, K>>>;
|
|
|
|
const AuthorizationHeaderSchema = z.string().regex(/^Bearer .+$/);
|
|
|
|
export type AuthenticatedEnvironment = Optional<
|
|
NonNullable<Awaited<ReturnType<typeof findEnvironmentByApiKey>>>,
|
|
"orgMember"
|
|
>;
|
|
|
|
type ApiAuthenticationResult = {
|
|
apiKey: string;
|
|
type: "PUBLIC" | "PRIVATE";
|
|
environment: AuthenticatedEnvironment;
|
|
};
|
|
|
|
export async function authenticateApiRequest(
|
|
request: Request,
|
|
{ allowPublicKey = false }: { allowPublicKey?: boolean } = {}
|
|
): Promise<ApiAuthenticationResult | undefined> {
|
|
const apiKey = getApiKeyFromRequest(request);
|
|
if (!apiKey) {
|
|
return;
|
|
}
|
|
|
|
return authenticateApiKey(apiKey, { allowPublicKey });
|
|
}
|
|
|
|
export async function authenticateApiKey(
|
|
apiKey: string,
|
|
{ allowPublicKey = false }: { allowPublicKey?: boolean } = {}
|
|
): Promise<ApiAuthenticationResult | undefined> {
|
|
const result = getApiKeyResult(apiKey);
|
|
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
//if it's a public API key and we don't allow public keys, return
|
|
if (!allowPublicKey) {
|
|
const environment = await findEnvironmentByApiKey(result.apiKey);
|
|
if (!environment) return;
|
|
return {
|
|
...result,
|
|
environment,
|
|
};
|
|
}
|
|
|
|
switch (result.type) {
|
|
case "PUBLIC": {
|
|
const environment = await findEnvironmentByPublicApiKey(result.apiKey);
|
|
if (!environment) return;
|
|
return {
|
|
...result,
|
|
environment,
|
|
};
|
|
}
|
|
case "PRIVATE": {
|
|
const environment = await findEnvironmentByApiKey(result.apiKey);
|
|
if (!environment) return;
|
|
return {
|
|
...result,
|
|
environment,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
export function isPublicApiKey(key: string) {
|
|
return key.startsWith("pk_");
|
|
}
|
|
|
|
export function getApiKeyFromRequest(request: Request) {
|
|
const rawAuthorization = request.headers.get("Authorization");
|
|
|
|
const authorization = AuthorizationHeaderSchema.safeParse(rawAuthorization);
|
|
if (!authorization.success) {
|
|
return;
|
|
}
|
|
|
|
const apiKey = authorization.data.replace(/^Bearer /, "");
|
|
return apiKey;
|
|
}
|
|
|
|
export function getApiKeyResult(apiKey: string) {
|
|
const type = isPublicApiKey(apiKey) ? ("PUBLIC" as const) : ("PRIVATE" as const);
|
|
return { apiKey, type };
|
|
}
|