feat: WIP kittens

pull/715/head
Travis Fischer 2025-05-31 16:48:27 +07:00
rodzic 80bff87be6
commit 67f564bff8
10 zmienionych plików z 52 dodań i 39 usunięć

Wyświetl plik

@ -15,3 +15,5 @@ STRIPE_WEBHOOK_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
AGENTIC_ADMIN_API_KEY=

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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>

Wyświetl plik

@ -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()
}

Wyświetl plik

@ -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>(

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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)
// }
// }

Wyświetl plik

@ -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