mirror of
https://github.com/lobehub/lobehub.git
synced 2026-09-20 04:56:13 +08:00
feat/quick-note
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7abba9672f |
✨ feat(openapi): add api-keys, evals, mcp-servers and usage to the v1 API (#18141)
* ✨ feat(openapi): expand v1 REST surface with api-keys, evals, mcp-servers and usage Add new OpenAPI v1 resources (API keys, evals, MCP servers, usage) and harden the existing controllers with shared validators, pagination helpers and public-field projection so responses no longer leak internal columns. Usage reporting goes through a business-server slot so self-hosted deployments fall back to an unavailable summary. * 🐛 fix(openapi): enforce row-owner checks on MCP writes and drop the unused upload pathname The connector model's ownership predicate is workspace-wide, so the REST MCP write paths let any member with only `agent:update:owner` patch, delete or sync a server created by another member. The connector tRPC mutations already guard this with `assertWorkspaceRowManageable`; add the equivalent `assertRowManageable` to the OpenAPI BaseService and apply it before every MCP mutation. Also remove `pathname` from the public upload schema, route docs and spec: it never reached the storage key, and its validator allowed any 1-1024 char string, so wiring it through would have handed clients an unchecked object path. The batch upload schema already omitted it. * 🐛 fix(openapi): drop request fields the v1 run paths never honor `n` never reached ChatStreamPayload and /chat returns a single content string, so accepting it promised multiple choices and delivered one. `caseSelection` is storage-only for internal runs — this endpoint always creates `mode: 'internal'`, which pre-creates topics for every case in the dataset — so a caller asking for a subset would silently run the whole thing. `maxConcurrency` is consumed nowhere. Remove all three from the public schemas and regenerate the spec, matching the `pathname` removal: the contract should only advertise knobs the run path honors. `k`, `maxSteps` and `timeout` stay because the run service does read them. * 🐛 fix(openapi): admin-gate workspace API-key routes `workspaceAuthMiddleware` only enforces the owner/admin role for `apikey` auth, so an OIDC/session-authenticated workspace member holding `api_key:update:owner` could PATCH or DELETE another member's workspace key by id — `ApiKeyModel`'s predicate is workspace-wide, not row-owner. The tRPC side already gates this: `apiKeyProcedure` applies `requireWorkspaceRoleWhenScoped('admin')` to the whole router, so the REST surface was strictly more permissive. Add the equivalent Hono middleware and apply it to every api-keys route so both transports gate identically. * 🐛 fix(openapi): preserve an explicit temperature of 0 on /chat `ChatStreamPayload` was built with `params.temperature || 1`, so a request asking for deterministic output at `temperature: 0` was silently sent at 1 — the schema has accepted `min(0)` since the endpoint was introduced. Pre-existing since #12758 rather than a regression here, but it is the same contract-vs-implementation mismatch this PR has been clearing up, and the fix is `??` instead of `||`. Scoped to the request path: `generateReply` applies the same falsy fallback to an agent's stored config, which is a separate behavioural change for existing agents and is left alone. * 🐛 fix(openapi): require chat:write to queue an eval run An internal eval run pre-creates real chat topics through `TopicModel`, and the QStash workflow keeps writing topic/message state afterwards. The route gated only on `agent:write` + `model:invoke`, so a restricted API key deliberately denied `chat:write` could still persist chat artifacts through `/eval/runs`. `/responses` already enforces the full `agent:write` + `chat:write` + `model:invoke` trio for exactly this reason; match it here. * 🐛 fix(openapi): correct file response schemas and regenerate the SDK The spec fell back to the bare resource schema for any one-segment path, so `POST /files`, `GET /files/{id}`, `POST /files/batches` and `POST /files/queries` all advertised `File` while the controllers actually return wrapped payloads (`{ file, parsed? }`, `{ successful, failed, summary }`, `{ files, failed, ... }`). Generated clients would have mistyped every one of them. Describe the four explicitly ahead of the fallback. `packages/sdk/src/generated` was also stale — its drift test was already failing locally, since the SDK is not in the PR CI whitelist. Regenerated it from the corrected spec, so the new api-keys / eval / mcp-servers / usage resources are callable and the file operations carry the right types. * 🐛 fix(openapi): keep MCP server routes to base-scope connectors `isPublicMcpServer` ignored `agentId`, but `queryPublic()` (the list path) filters `agent_id IS NULL` while `findPublicById` does not. So the id routes — GET/PATCH/DELETE and POST /:id/sync — could reach agent-scoped connectors the list route cannot even surface. Deleting one that way skipped the connector router's agent-plugin unpin, which exists specifically so a delete never leaves the owning agent with a dangling pinned tool. Require `agentId === null` in the predicate so the whole group is consistently base-scoped; agent-scoped connectors stay managed through the agent profile. * 🐛 fix(openapi): stop typing named sub-operations as the group resource The success-schema fallback treated any single-segment path as a resource operation, so `GET /messages/count` was advertised as `Message` (it returns `{ count }`) and `GET /users/me` as `User` (a caller without `user:read` deliberately gets only `{ id }`). Generated SDK callers were typed against the wrong shapes. Narrow the fallback to the two real resource operations — the collection root and `{id}` — and describe `messages/count`, `messages/replies` and `users/me` explicitly. An undescribed sub-operation now degrades to a permissive object rather than a confidently wrong resource type, which is the failure mode that produced both this and the earlier `/files` mistyping. SDK regenerated. |
||
|
|
3ea7afd5d8 |
✨ feat(openapi): generate openapi.yml from routes via hono-openapi (#17944)
* ✨ feat(openapi): generate openapi.yml from routes via hono-openapi - add src/common/validator.ts: drop-in zValidator built on hono-openapi so every request schema registers into the spec while keeping the exact @hono/zod-validator 400 response shape - describe the four validator-less routes (users/me, agent-groups list, file upload, batch upload incl. multipart bodies) and /health - add scripts/generate-openapi.ts: runtime spec generation with route-vs-spec parity check, --check mode for CI, placeholder responses - commit the generated openapi.yml (79 operations / 38 paths) and guard it with scripts/generate-openapi.test.ts - ignore the generated file in eslint/prettier so formatters don't fight the generator output Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ✨ feat(openapi): serve API docs at /api/v1/docs via Scalar - extract spec assembly into src/spec.ts, shared by the generator script and the new runtime endpoint so both always emit the same document - GET /api/v1/openapi.json: build spec from live routes on first request, cached in memory - GET /api/v1/docs: Scalar interactive reference reading that endpoint - exempt the two docs routes from the generator parity check (real endpoints, deliberately not part of the described API surface) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 💄 style(openapi): match docs site theme to the app's neutral monochrome style - add src/docs-theme.ts: Scalar CSS variable overrides — black accent on light, white accent on true-black dark, antd system font stack - theme: 'none' so the runtime bundle doesn't inject its default theme stylesheet after our customCss (which silently overrode every variable) - reuse the app favicon Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🌐 feat(openapi): localize docs UI following the app's language preference Resolve the Scalar UI locale per request with the same priority the main app uses (?hl query > LOBE_LOCALE cookie > Accept-Language), mapped to Scalar's built-in translations (zh-CN, de, fr, es, ru, ar; zh-* all map to zh-CN). English stays the default for unsupported locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |