chore: generate fastopenapi console contracts (#38580)

This commit is contained in:
Stephen Zhou
2026-07-09 03:52:42 +00:00
committed by GitHub
parent cee88de684
commit c5cef80ea4
28 changed files with 561 additions and 47 deletions
@@ -0,0 +1,45 @@
// This file is auto-generated by @hey-api/openapi-ts
import { oc } from '@orpc/contract'
import * as z from 'zod'
import { zGetInitResponse, zPostInitBody, zPostInitResponse } from './zod.gen'
/**
* Get initialization validation status.
*/
export const get = oc
.route({
inputStructure: 'detailed',
method: 'GET',
operationId: 'getInit',
path: '/init',
summary: 'Get initialization validation status.',
tags: ['console'],
})
.output(zGetInitResponse)
/**
* Validate initialization password.
*/
export const post = oc
.route({
inputStructure: 'detailed',
method: 'POST',
operationId: 'postInit',
path: '/init',
successStatus: 201,
summary: 'Validate initialization password.',
tags: ['console'],
})
.input(z.object({ body: zPostInitBody }))
.output(zPostInitResponse)
export const init = {
get,
post,
}
export const contract = {
init,
}
@@ -0,0 +1,43 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type InitStatusResponse = {
status: 'finished' | 'not_started'
}
export type InitValidatePayload = {
password: string
}
export type InitValidateResponse = {
result: string
}
export type GetInitData = {
body?: never
path?: never
query?: never
url: '/init'
}
export type GetInitResponses = {
200: InitStatusResponse
}
export type GetInitResponse = GetInitResponses[keyof GetInitResponses]
export type PostInitData = {
body: InitValidatePayload
path?: never
query?: never
url: '/init'
}
export type PostInitResponses = {
201: InitValidateResponse
}
export type PostInitResponse = PostInitResponses[keyof PostInitResponses]
@@ -0,0 +1,36 @@
// This file is auto-generated by @hey-api/openapi-ts
import * as z from 'zod'
/**
* InitStatusResponse
*/
export const zInitStatusResponse = z.object({
status: z.enum(['finished', 'not_started']),
})
/**
* InitValidatePayload
*/
export const zInitValidatePayload = z.object({
password: z.string().max(30),
})
/**
* InitValidateResponse
*/
export const zInitValidateResponse = z.object({
result: z.string(),
})
/**
* OK
*/
export const zGetInitResponse = zInitStatusResponse
export const zPostInitBody = zInitValidatePayload
/**
* Created
*/
export const zPostInitResponse = zInitValidateResponse
@@ -35,6 +35,7 @@ export const contractLoaders = {
import('./forgot-password/orpc.gen').then(({ forgotPassword }) => ({ forgotPassword })),
form: () => import('./form/orpc.gen').then(({ form }) => ({ form })),
info: () => import('./info/orpc.gen').then(({ info }) => ({ info })),
init: () => import('./init/orpc.gen').then(({ init }) => ({ init })),
installedApps: () =>
import('./installed-apps/orpc.gen').then(({ installedApps }) => ({ installedApps })),
instructionGenerate: () =>
@@ -47,6 +48,7 @@ export const contractLoaders = {
import('./notification/orpc.gen').then(({ notification }) => ({ notification })),
notion: () => import('./notion/orpc.gen').then(({ notion }) => ({ notion })),
oauth: () => import('./oauth/orpc.gen').then(({ oauth }) => ({ oauth })),
ping: () => import('./ping/orpc.gen').then(({ ping }) => ({ ping })),
rag: () => import('./rag/orpc.gen').then(({ rag }) => ({ rag })),
refreshToken: () =>
import('./refresh-token/orpc.gen').then(({ refreshToken }) => ({ refreshToken })),
@@ -61,6 +63,7 @@ export const contractLoaders = {
import('./rule-structured-output-generate/orpc.gen').then(
({ ruleStructuredOutputGenerate }) => ({ ruleStructuredOutputGenerate }),
),
setup: () => import('./setup/orpc.gen').then(({ setup }) => ({ setup })),
snippets: () => import('./snippets/orpc.gen').then(({ snippets }) => ({ snippets })),
spec: () => import('./spec/orpc.gen').then(({ spec }) => ({ spec })),
systemFeatures: () =>
@@ -70,6 +73,7 @@ export const contractLoaders = {
test: () => import('./test/orpc.gen').then(({ test }) => ({ test })),
trialApps: () => import('./trial-apps/orpc.gen').then(({ trialApps }) => ({ trialApps })),
trialModels: () => import('./trial-models/orpc.gen').then(({ trialModels }) => ({ trialModels })),
version: () => import('./version/orpc.gen').then(({ version }) => ({ version })),
website: () => import('./website/orpc.gen').then(({ website }) => ({ website })),
workflow: () => import('./workflow/orpc.gen').then(({ workflow }) => ({ workflow })),
workflowGenerate: () =>
@@ -0,0 +1,27 @@
// This file is auto-generated by @hey-api/openapi-ts
import { oc } from '@orpc/contract'
import { zGetPingResponse } from './zod.gen'
/**
* Health check endpoint for connection testing.
*/
export const get = oc
.route({
inputStructure: 'detailed',
method: 'GET',
operationId: 'getPing',
path: '/ping',
summary: 'Health check endpoint for connection testing.',
tags: ['console'],
})
.output(zGetPingResponse)
export const ping = {
get,
}
export const contract = {
ping,
}
@@ -0,0 +1,22 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type PingResponse = {
result: string
}
export type GetPingData = {
body?: never
path?: never
query?: never
url: '/ping'
}
export type GetPingResponses = {
200: PingResponse
}
export type GetPingResponse = GetPingResponses[keyof GetPingResponses]
@@ -0,0 +1,15 @@
// This file is auto-generated by @hey-api/openapi-ts
import * as z from 'zod'
/**
* PingResponse
*/
export const zPingResponse = z.object({
result: z.string(),
})
/**
* OK
*/
export const zGetPingResponse = zPingResponse
@@ -24,6 +24,7 @@ import { files } from './files/orpc.gen'
import { forgotPassword } from './forgot-password/orpc.gen'
import { form } from './form/orpc.gen'
import { info } from './info/orpc.gen'
import { init } from './init/orpc.gen'
import { installedApps } from './installed-apps/orpc.gen'
import { instructionGenerate } from './instruction-generate/orpc.gen'
import { login } from './login/orpc.gen'
@@ -31,6 +32,7 @@ import { logout } from './logout/orpc.gen'
import { notification } from './notification/orpc.gen'
import { notion } from './notion/orpc.gen'
import { oauth } from './oauth/orpc.gen'
import { ping } from './ping/orpc.gen'
import { rag } from './rag/orpc.gen'
import { refreshToken } from './refresh-token/orpc.gen'
import { remoteFiles } from './remote-files/orpc.gen'
@@ -38,6 +40,7 @@ import { resetPassword } from './reset-password/orpc.gen'
import { ruleCodeGenerate } from './rule-code-generate/orpc.gen'
import { ruleGenerate } from './rule-generate/orpc.gen'
import { ruleStructuredOutputGenerate } from './rule-structured-output-generate/orpc.gen'
import { setup } from './setup/orpc.gen'
import { snippets } from './snippets/orpc.gen'
import { spec } from './spec/orpc.gen'
import { systemFeatures } from './system-features/orpc.gen'
@@ -46,6 +49,7 @@ import { tags } from './tags/orpc.gen'
import { test } from './test/orpc.gen'
import { trialApps } from './trial-apps/orpc.gen'
import { trialModels } from './trial-models/orpc.gen'
import { version } from './version/orpc.gen'
import { website } from './website/orpc.gen'
import { workflowGenerate } from './workflow-generate/orpc.gen'
import { workflow } from './workflow/orpc.gen'
@@ -75,6 +79,7 @@ const communityContract = {
forgotPassword,
form,
info,
init,
installedApps,
instructionGenerate,
login,
@@ -82,6 +87,7 @@ const communityContract = {
notification,
notion,
oauth,
ping,
rag,
refreshToken,
remoteFiles,
@@ -89,6 +95,7 @@ const communityContract = {
ruleCodeGenerate,
ruleGenerate,
ruleStructuredOutputGenerate,
setup,
snippets,
spec,
systemFeatures,
@@ -97,6 +104,7 @@ const communityContract = {
test,
trialApps,
trialModels,
version,
website,
workflow,
workflowGenerate,
@@ -0,0 +1,60 @@
// This file is auto-generated by @hey-api/openapi-ts
import { oc } from '@orpc/contract'
import * as z from 'zod'
import { zGetSetupResponse, zPostSetupBody, zPostSetupResponse } from './zod.gen'
/**
* Get system setup status.
*
* NOTE: This endpoint is unauthenticated by design.
*
* During first-time bootstrap there is no admin account yet, so frontend initialization must be
* able to query setup progress before any login flow exists.
*
* Only bootstrap-safe status information should be returned by this endpoint.
*
*/
export const get = oc
.route({
inputStructure: 'detailed',
method: 'GET',
operationId: 'getSetup',
path: '/setup',
summary:
'Get system setup status.\n\n NOTE: This endpoint is unauthenticated by design.\n\n During first-time bootstrap there is no admin account yet, so frontend initialization must be\n able to query setup progress before any login flow exists.\n\n Only bootstrap-safe status information should be returned by this endpoint.\n ',
tags: ['console'],
})
.output(zGetSetupResponse)
/**
* Initialize system setup with admin account.
*
* NOTE: This endpoint is unauthenticated by design for first-time bootstrap.
* Access is restricted by deployment mode (`SELF_HOSTED`), one-time setup guards,
* and init-password validation rather than user session authentication.
*
*/
export const post = oc
.route({
inputStructure: 'detailed',
method: 'POST',
operationId: 'postSetup',
path: '/setup',
successStatus: 201,
summary:
'Initialize system setup with admin account.\n\n NOTE: This endpoint is unauthenticated by design for first-time bootstrap.\n Access is restricted by deployment mode (`SELF_HOSTED`), one-time setup guards,\n and init-password validation rather than user session authentication.\n ',
tags: ['console'],
})
.input(z.object({ body: zPostSetupBody }))
.output(zPostSetupResponse)
export const setup = {
get,
post,
}
export const contract = {
setup,
}
@@ -0,0 +1,47 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type SetupStatusResponse = {
setup_at?: string | null
step: 'finished' | 'not_started'
}
export type SetupRequestPayload = {
email: string
language?: string | null
name: string
password: string
}
export type SetupResponse = {
result: string
}
export type GetSetupData = {
body?: never
path?: never
query?: never
url: '/setup'
}
export type GetSetupResponses = {
200: SetupStatusResponse
}
export type GetSetupResponse = GetSetupResponses[keyof GetSetupResponses]
export type PostSetupData = {
body: SetupRequestPayload
path?: never
query?: never
url: '/setup'
}
export type PostSetupResponses = {
201: SetupResponse
}
export type PostSetupResponse = PostSetupResponses[keyof PostSetupResponses]
@@ -0,0 +1,40 @@
// This file is auto-generated by @hey-api/openapi-ts
import * as z from 'zod'
/**
* SetupStatusResponse
*/
export const zSetupStatusResponse = z.object({
setup_at: z.string().nullish(),
step: z.enum(['finished', 'not_started']),
})
/**
* SetupRequestPayload
*/
export const zSetupRequestPayload = z.object({
email: z.string(),
language: z.string().nullish(),
name: z.string().max(30),
password: z.string(),
})
/**
* SetupResponse
*/
export const zSetupResponse = z.object({
result: z.string(),
})
/**
* OK
*/
export const zGetSetupResponse = zSetupStatusResponse
export const zPostSetupBody = zSetupRequestPayload
/**
* Created
*/
export const zPostSetupResponse = zSetupResponse
@@ -0,0 +1,29 @@
// This file is auto-generated by @hey-api/openapi-ts
import { oc } from '@orpc/contract'
import * as z from 'zod'
import { zGetVersionQuery, zGetVersionResponse } from './zod.gen'
/**
* Check for application version updates.
*/
export const get = oc
.route({
inputStructure: 'detailed',
method: 'GET',
operationId: 'getVersion',
path: '/version',
summary: 'Check for application version updates.',
tags: ['console'],
})
.input(z.object({ query: zGetVersionQuery }))
.output(zGetVersionResponse)
export const version = {
get,
}
export const contract = {
version,
}
@@ -0,0 +1,33 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type VersionResponse = {
can_auto_update: boolean
features: VersionFeatures
release_date: string
release_notes: string
version: string
}
export type VersionFeatures = {
can_replace_logo: boolean
model_load_balancing_enabled: boolean
}
export type GetVersionData = {
body?: never
path?: never
query: {
current_version: string
}
url: '/version'
}
export type GetVersionResponses = {
200: VersionResponse
}
export type GetVersionResponse = GetVersionResponses[keyof GetVersionResponses]
@@ -0,0 +1,31 @@
// This file is auto-generated by @hey-api/openapi-ts
import * as z from 'zod'
/**
* VersionFeatures
*/
export const zVersionFeatures = z.object({
can_replace_logo: z.boolean(),
model_load_balancing_enabled: z.boolean(),
})
/**
* VersionResponse
*/
export const zVersionResponse = z.object({
can_auto_update: z.boolean(),
features: zVersionFeatures,
release_date: z.string(),
release_notes: z.string(),
version: z.string(),
})
export const zGetVersionQuery = z.object({
current_version: z.string(),
})
/**
* OK
*/
export const zGetVersionResponse = zVersionResponse
+34 -1
View File
@@ -62,6 +62,8 @@ const apiOpenApiDir = path.resolve(currentDir, 'openapi')
const operationMethods = new Set(['delete', 'get', 'patch', 'post', 'put'])
const pydanticDecimalStringPattern = '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'
const codegenSafeDecimalStringPattern = '^(?![-+.]*$)[+-]?0*\\d*\\.?\\d*$'
const fastOpenApiConsoleSpecFilename = 'fastopenapi-console-openapi.json'
const fastOpenApiConsolePathPrefix = '/console/api'
const apiSpecs: ApiSpec[] = [
{ filename: 'console-openapi.json', name: 'console' },
@@ -276,6 +278,33 @@ const normalizeApiSwagger = (document: SwaggerDocument) => {
return document
}
const mergeFastOpenApiConsoleSwagger = (document: SwaggerDocument) => {
const fastOpenApiDocument = readApiSwagger(fastOpenApiConsoleSpecFilename)
const targetPaths = document.paths ??= {}
for (const [routePath, pathItem] of Object.entries(fastOpenApiDocument.paths ?? {})) {
const contractPath = routePath.startsWith(fastOpenApiConsolePathPrefix)
? routePath.slice(fastOpenApiConsolePathPrefix.length) || '/'
: routePath
if (targetPaths[contractPath])
throw new Error(`Duplicate console API path after FastOpenAPI merge: ${contractPath}`)
targetPaths[contractPath] = pathItem
}
const targetSchemas = getDocumentSchemas(document)
const sourceSchemas = getDocumentSchemas(fastOpenApiDocument)
for (const [schemaName, schema] of Object.entries(sourceSchemas)) {
if (targetSchemas[schemaName])
throw new Error(`Duplicate console API schema after FastOpenAPI merge: ${schemaName}`)
targetSchemas[schemaName] = schema
}
return document
}
const topLevelPathSegment = (routePath: string) => {
return routePath.split('/').filter(Boolean)[0] ?? 'root'
}
@@ -435,7 +464,11 @@ const splitConsoleDocument = (document: SwaggerDocument) => {
}
const createApiJobs = (spec: ApiSpec): ApiJob[] => {
const document = normalizeApiSwagger(readApiSwagger(spec.filename))
const document = normalizeApiSwagger(
spec.name === 'console'
? mergeFastOpenApiConsoleSwagger(readApiSwagger(spec.filename))
: readApiSwagger(spec.filename),
)
if (spec.name === 'console')
return splitConsoleDocument(document)
+1 -3
View File
@@ -18,9 +18,7 @@
}
},
"scripts": {
"gen-api-contract": "pnpm gen-api-openapi && pnpm gen-api-contract-from-openapi",
"gen-api-contract-from-openapi": "node -e \"fs.rmSync('generated/api', { recursive: true, force: true })\" && openapi-ts -f openapi-ts.api.config.ts && vp fmt generated/api && eslint --fix generated/api",
"gen-api-openapi": "uv run --project ../../api ../../api/dev/generate_swagger_specs.py --output-dir openapi",
"gen-api-contract": "uv run --project ../../api ../../api/dev/generate_swagger_specs.py --output-dir openapi && uv run --project ../../api ../../api/dev/generate_fastopenapi_specs.py --output-dir openapi && node -e \"fs.rmSync('generated/api', { recursive: true, force: true })\" && openapi-ts -f openapi-ts.api.config.ts && vp fmt generated/api && eslint --fix generated/api",
"gen-enterprise-contract": "openapi-ts -f openapi-ts.enterprise.config.ts",
"type-check": "tsc"
},