kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: WIP kittens
rodzic
80bff87be6
commit
67f564bff8
|
@ -15,3 +15,5 @@ STRIPE_WEBHOOK_SECRET=
|
|||
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
|
||||
AGENTIC_ADMIN_API_KEY=
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { assert } from '@agentic/platform-core'
|
||||
|
||||
import { db, eq, type RawDeployment, schema } from '@/db'
|
||||
|
||||
/**
|
||||
|
@ -18,6 +20,8 @@ export async function getDeploymentById({
|
|||
project?: true
|
||||
}
|
||||
}): Promise<RawDeployment | undefined> {
|
||||
assert(deploymentId, 400, 'Missing required deployment id')
|
||||
|
||||
const deployment = await db.query.deployments.findFirst({
|
||||
...dbQueryOpts,
|
||||
where: eq(schema.deployments.id, deploymentId)
|
||||
|
|
|
@ -6,7 +6,8 @@ import { db, deploymentIdSchema, eq, type RawDeployment, schema } from '@/db'
|
|||
import { ensureAuthUser } from '@/lib/ensure-auth-user'
|
||||
|
||||
/**
|
||||
* Attempts to find the Deployment matching the given deployment identifier.
|
||||
* Attempts to find the Deployment matching the given deployment ID or
|
||||
* identifier.
|
||||
*
|
||||
* Throws a HTTP 404 error if not found.
|
||||
*
|
||||
|
@ -26,6 +27,7 @@ export async function tryGetDeploymentByIdentifier(
|
|||
}
|
||||
}
|
||||
): Promise<RawDeployment> {
|
||||
assert(deploymentIdentifier, 400, 'Missing required deployment identifier')
|
||||
const user = await ensureAuthUser(ctx)
|
||||
|
||||
// First check if the identifier is a deployment ID
|
||||
|
|
|
@ -20,7 +20,9 @@ export const envSchema = z.object({
|
|||
STRIPE_WEBHOOK_SECRET: z.string().nonempty(),
|
||||
|
||||
GITHUB_CLIENT_ID: z.string().nonempty(),
|
||||
GITHUB_CLIENT_SECRET: z.string().nonempty()
|
||||
GITHUB_CLIENT_SECRET: z.string().nonempty(),
|
||||
|
||||
AGENTIC_ADMIN_API_KEY: z.string().nonempty()
|
||||
})
|
||||
export type Env = z.infer<typeof envSchema>
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { assert } from '@agentic/platform-core'
|
||||
import { createMiddleware } from 'hono/factory'
|
||||
|
||||
import type { RawUser } from '@/db'
|
||||
import type { AuthenticatedEnv } from '@/lib/types'
|
||||
import { authClient } from '@/lib/auth/client'
|
||||
import { subjects } from '@/lib/auth/subjects'
|
||||
|
||||
import { env } from '../env'
|
||||
|
||||
export const authenticate = createMiddleware<AuthenticatedEnv>(
|
||||
async function authenticateMiddleware(ctx, next) {
|
||||
const credentials = ctx.req.raw.headers.get('Authorization')
|
||||
|
@ -20,12 +23,32 @@ export const authenticate = createMiddleware<AuthenticatedEnv>(
|
|||
const token = parts.at(-1)
|
||||
assert(token, 401, 'Unauthorized')
|
||||
|
||||
const verified = await authClient.verify(subjects, token)
|
||||
assert(!verified.err, 401, 'Unauthorized')
|
||||
// TODO: Use a more secure way to authenticate admin requests that doesn't
|
||||
// use a single API key and isn't vulnerable to timing attacks.
|
||||
// eslint-disable-next-line security/detect-possible-timing-attacks
|
||||
if (token === env.AGENTIC_ADMIN_API_KEY) {
|
||||
ctx.set('userId', 'admin')
|
||||
ctx.set('user', {
|
||||
id: 'admin',
|
||||
name: 'Admin',
|
||||
username: 'admin',
|
||||
role: 'admin',
|
||||
email: 'admin@agentic.so',
|
||||
isEmailVerified: true,
|
||||
image: undefined,
|
||||
stripeCustomerId: undefined,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: undefined
|
||||
} as RawUser)
|
||||
} else {
|
||||
const verified = await authClient.verify(subjects, token)
|
||||
assert(!verified.err, 401, 'Unauthorized')
|
||||
|
||||
const userId = verified.subject.properties.id
|
||||
assert(userId, 401, 'Unauthorized')
|
||||
ctx.set('userId', userId)
|
||||
const userId = verified.subject.properties.id
|
||||
assert(userId, 401, 'Unauthorized')
|
||||
ctx.set('userId', userId)
|
||||
}
|
||||
|
||||
await next()
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { createMiddleware } from 'hono/factory'
|
|||
import type { DefaultEnv } from '@/lib/types'
|
||||
import { ConsoleLogger } from '@/lib/logger'
|
||||
|
||||
/** Monotonically increasing ID for insertId. */
|
||||
/** Monotonically increasing request IDs for logging / tracing. */
|
||||
const eventId = new EventId()
|
||||
|
||||
export const init = createMiddleware<DefaultEnv>(
|
||||
|
|
|
@ -6,7 +6,7 @@ import { db, eq, projectIdSchema, type RawProject, schema } from '@/db'
|
|||
import { ensureAuthUser } from '@/lib/ensure-auth-user'
|
||||
|
||||
/**
|
||||
* Attempts to find the Project matching the given identifier.
|
||||
* Attempts to find the Project matching the given ID or identifier.
|
||||
*
|
||||
* Throws a HTTP 404 error if not found.
|
||||
*
|
||||
|
@ -27,6 +27,7 @@ export async function tryGetProjectByIdentifier(
|
|||
}
|
||||
}
|
||||
): Promise<RawProject> {
|
||||
assert(projectIdentifier, 400, 'Missing required project identifier')
|
||||
const user = await ensureAuthUser(ctx)
|
||||
|
||||
// First check if the identifier is a project ID
|
||||
|
@ -35,7 +36,7 @@ export async function tryGetProjectByIdentifier(
|
|||
...dbQueryOpts,
|
||||
where: eq(schema.projects.id, projectIdentifier)
|
||||
})
|
||||
assert(project, 404, `project not found "${projectIdentifier}"`)
|
||||
assert(project, 404, `Project not found "${projectIdentifier}"`)
|
||||
return project
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ import type {
|
|||
|
||||
import type { AgenticEnv } from './env'
|
||||
|
||||
export type AdminConsumer = Consumer & {
|
||||
user: User
|
||||
}
|
||||
|
||||
export type Context = ExecutionContext & {
|
||||
req: Request
|
||||
env: AgenticEnv
|
||||
|
@ -24,7 +28,3 @@ export interface ResolvedOriginRequest {
|
|||
ip?: string
|
||||
pricingPlanSlug?: string
|
||||
}
|
||||
|
||||
export type AdminConsumer = Consumer & {
|
||||
user: User
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ export default {
|
|||
function recordTimespans() {
|
||||
const now = Date.now()
|
||||
originTimespan = now - originStartTime!
|
||||
gatewayTimespan = now - gatewayStartTime!
|
||||
gatewayTimespan = now - gatewayStartTime
|
||||
}
|
||||
|
||||
const client = new AgenticApiClient({
|
||||
|
@ -59,7 +59,7 @@ export default {
|
|||
return handleOptions(inputReq)
|
||||
}
|
||||
|
||||
const { originReq, ...call } = await resolveOriginRequest(ctx)
|
||||
const resolvedOriginRequest = await resolveOriginRequest(ctx)
|
||||
|
||||
try {
|
||||
const originReqCacheKey = await getOriginRequestCacheKey(originReq)
|
||||
|
@ -149,26 +149,3 @@ export default {
|
|||
}
|
||||
}
|
||||
} satisfies ExportedHandler<Env>
|
||||
|
||||
// const handler = {
|
||||
// fetch: (req: Request, env: Env, executionCtx: ExecutionContext) => {
|
||||
// const parsedEnv = zEnv.safeParse(env)
|
||||
// if (!parsedEnv.success) {
|
||||
// new ConsoleLogger({
|
||||
// requestId: '',
|
||||
// environment: env.ENVIRONMENT,
|
||||
// application: 'api'
|
||||
// }).fatal(`BAD_ENVIRONMENT: ${parsedEnv.error.message}`)
|
||||
// return Response.json(
|
||||
// {
|
||||
// code: 'BAD_ENVIRONMENT',
|
||||
// message: 'Some environment variables are missing or are invalid',
|
||||
// errors: parsedEnv.error
|
||||
// },
|
||||
// { status: 500 }
|
||||
// )
|
||||
// }
|
||||
|
||||
// return app.fetch(req, parsedEnv.data, executionCtx)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
- API gateway MCP server vs OpenAPI API gateway
|
||||
- share hono middleware and utils across apps/api and apps/gateway
|
||||
- or combine these together? ehhhh
|
||||
- add username / team name blacklist
|
||||
- admin, internal, mcp, etc
|
||||
|
||||
## License
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue