feat: add cache-control to key api endpoints

pull/715/head
Travis Fischer 2025-06-29 06:48:46 -05:00
rodzic 1150299056
commit 4d2674fc2d
10 zmienionych plików z 60 dodań i 7 usunięć

Wyświetl plik

@ -1,6 +1,7 @@
import type { RawConsumer } from '@/db'
import type { AuthenticatedHonoContext } from '@/lib/types'
import { setPublicCacheControl } from '@/lib/cache-control'
import { env } from '@/lib/env'
export function setAdminCacheControlForConsumer(
c: AuthenticatedHonoContext,
@ -11,8 +12,15 @@ export function setAdminCacheControlForConsumer(
!consumer.activated ||
!consumer.isStripeSubscriptionActive
) {
setPublicCacheControl(c.res, '10s')
// TODO: should we cache free-tier consumers for longer on prod?
// We really don't want free tier customers to cause our backend API so
// much traffic, but we'd also like for customers upgrading to a paid tier
// to have a snappy, smooth experience – without having to wait for their
// free tier subscription to expire from the cache.
setPublicCacheControl(c.res, env.isProd ? '30s' : '10s')
} else {
setPublicCacheControl(c.res, '1m')
// We don't want the gateway hitting our API too often, so cache active
// customer subscriptions for longer in production
setPublicCacheControl(c.res, env.isProd ? '30m' : '1m')
}
}

Wyświetl plik

@ -5,7 +5,9 @@ import type { AuthenticatedHonoEnv } from '@/lib/types'
import { schema } from '@/db'
import { acl } from '@/lib/acl'
import { aclAdmin } from '@/lib/acl-admin'
import { setPublicCacheControl } from '@/lib/cache-control'
import { tryGetDeploymentByIdentifier } from '@/lib/deployments/try-get-deployment-by-identifier'
import { env } from '@/lib/env'
import {
openapiAuthenticatedSecuritySchemas,
openapiErrorResponse404,
@ -66,6 +68,13 @@ export function registerV1AdminGetDeploymentByIdentifier(
const hasPopulateProject = populate.includes('project')
if (env.isProd) {
// Published deployments are immutable, so cache them for longer in production
setPublicCacheControl(c.res, deployment.published ? '1h' : '5m')
} else {
setPublicCacheControl(c.res, '10s')
}
return c.json(
parseZodSchema(schema.deploymentAdminSelectSchema, {
...deployment,

Wyświetl plik

@ -4,6 +4,7 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
import { schema } from '@/db'
import { aclPublicProject } from '@/lib/acl-public-project'
import { setPublicCacheControl } from '@/lib/cache-control'
import { tryGetDeploymentByIdentifier } from '@/lib/deployments/try-get-deployment-by-identifier'
import {
openapiAuthenticatedSecuritySchemas,
@ -59,6 +60,13 @@ export function registerV1GetPublicDeploymentByIdentifier(
)
aclPublicProject(deployment.project!)
if (deployment.published) {
// Note that published deployments should be immutable
setPublicCacheControl(c.res, '1m')
} else {
setPublicCacheControl(c.res, '10s')
}
return c.json(parseZodSchema(schema.deploymentSelectSchema, deployment))
})
}

Wyświetl plik

@ -4,6 +4,8 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
import { db, eq, schema } from '@/db'
import { aclPublicProject } from '@/lib/acl-public-project'
import { setPublicCacheControl } from '@/lib/cache-control'
import { env } from '@/lib/env'
import {
openapiAuthenticatedSecuritySchemas,
openapiErrorResponse404,
@ -50,7 +52,8 @@ export function registerV1GetPublicProjectByIdentifier(
...Object.fromEntries(populate.map((field) => [field, true]))
}
})
await aclPublicProject(project, projectIdentifier)
aclPublicProject(project, projectIdentifier)
setPublicCacheControl(c.res, env.isProd ? '1m' : '10s')
return c.json(parseZodSchema(schema.projectSelectSchema, project))
})

Wyświetl plik

@ -4,6 +4,8 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
import { db, eq, schema } from '@/db'
import { aclPublicProject } from '@/lib/acl-public-project'
import { setPublicCacheControl } from '@/lib/cache-control'
import { env } from '@/lib/env'
import {
openapiAuthenticatedSecuritySchemas,
openapiErrorResponse404,
@ -50,6 +52,7 @@ export function registerV1GetPublicProject(app: OpenAPIHono<DefaultHonoEnv>) {
}
})
aclPublicProject(project, projectId)
setPublicCacheControl(c.res, env.isProd ? '1m' : '10s')
return c.json(parseZodSchema(schema.projectSelectSchema, project))
})

Wyświetl plik

@ -1,8 +1,11 @@
import { env } from 'node:process'
import type { DefaultHonoEnv } from '@agentic/platform-hono'
import { parseZodSchema } from '@agentic/platform-core'
import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
import { and, db, eq, isNotNull, schema } from '@/db'
import { setPublicCacheControl } from '@/lib/cache-control'
import {
openapiAuthenticatedSecuritySchemas,
openapiErrorResponses
@ -60,6 +63,7 @@ export function registerV1ListPublicProjects(app: OpenAPIHono<DefaultHonoEnv>) {
offset,
limit
})
setPublicCacheControl(c.res, env.isProd ? '1m' : '10s')
return c.json(parseZodSchema(z.array(schema.projectSelectSchema), projects))
})

Wyświetl plik

@ -4,6 +4,8 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
import type { AuthenticatedHonoEnv } from '@/lib/types'
import { db, eq, schema } from '@/db'
import { acl } from '@/lib/acl'
import { setPublicCacheControl } from '@/lib/cache-control'
import { env } from '@/lib/env'
import {
openapiAuthenticatedSecuritySchemas,
openapiErrorResponse404,
@ -45,6 +47,7 @@ export function registerV1GetUser(app: OpenAPIHono<AuthenticatedHonoEnv>) {
where: eq(schema.users.id, userId)
})
assert(user, 404, `User not found "${userId}"`)
setPublicCacheControl(c.res, env.isProd ? '30s' : '10s')
return c.json(parseZodSchema(schema.userSelectSchema, user))
})

Wyświetl plik

@ -1,12 +1,25 @@
import { assert } from '@agentic/platform-core'
export type PublicCacheControlLevels = '1s' | '10s' | '1m' | '1h' | '1d'
export type PublicCacheControlLevels =
| '1s'
| '10s'
| '30s'
| '1m'
| '5m'
| '10m'
| '30m'
| '1h'
| '1d'
const publicCacheControlLevelsMap: Record<PublicCacheControlLevels, string> = {
'1s': 'public, max-age=1, s-maxage=1 stale-while-revalidate=0',
'10s': 'public, max-age=10, s-maxage=10 stale-while-revalidate=1',
'30s': 'public, max-age=30, s-maxage=30 stale-while-revalidate=5',
'1m': 'public, max-age=60, s-maxage=60 stale-while-revalidate=10',
'1h': 'public, max-age=3600, s-maxage=3600, stale-while-revalidate=300',
'5m': 'public, max-age=300, s-maxage=300 stale-while-revalidate=60',
'10m': 'public, max-age=600, s-maxage=600 stale-while-revalidate=120',
'30m': 'public, max-age=1800, s-maxage=1800 stale-while-revalidate=300',
'1h': 'public, max-age=3600, s-maxage=3600, stale-while-revalidate=500',
'1d': 'public, max-age=86400, s-maxage=86400, stale-while-revalidate=3600'
}

Wyświetl plik

@ -180,7 +180,7 @@ export function recordToolCallUsage({
// If there's a consumer and it hasn't been activated yet, make sure it's
// activated. This may be called multiple times if the consumer is cached,
// but this method is intentionally idempotent, and we don't cache non-
// activated consumers for long, so shouldn't be a problem.
// activated consumers for long, so it shouldn't be a problem.
waitUntil(client.adminActivateConsumer({ consumerId: consumer.id }))
}

Wyświetl plik

@ -11,6 +11,9 @@
- should we bypass stripe for `free` plans to increase conversions?
- handle browser back/forward with `?next=`
- add some social proof to signup page
- example usage
- fix mcp examples
- add example usage to project detail pages
- docs
- main readme
- sub readmes (https://www.npmjs.com/package/@agentic/cli)
@ -23,7 +26,6 @@
- finesse header (mobile)
- create agentic products for legacy tools
- add basic legal terms and privacy policy (and update links in stripe)
- add caching to public projects api endpoints
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
- add feature about optimized context to marketing site
- ensure all agentic tool inputSchemas support openai strict mode by default