Skip to content

v0.2.0 — English first, TypeScript throughout

Choose a tag to compare

@robeshell robeshell released this 27 Sep 14:15
4ef8543

English first and TypeScript throughout: the specs, docs and tools speak English, the admin frontend is TypeScript end to end, and the OpenAPI doc is kept in sync with the backend by a check instead of by hand.

Highlights

  • English first. AGENTS.md, CLAUDE.md, docs/, the skills, the MCP tool descriptions and every developer tool (pnpm verify, pnpm scaffold, pnpm openapi:generate, the setup wizard) are in English; the docs site defaults to English (Chinese at /zh/, Japanese at /ja/); English is the fallback UI / API language.
  • TypeScript frontend. apps/web (source, tests and configs) is TypeScript with the same strict settings as the API, checked by pnpm typecheck and the verify gate; pnpm scaffold generates typed TSX pages and TS API files.
  • API types from the OpenAPI doc. The frontend's API types are generated from docs/apifox-full.openapi.json, and a new body-sync rule keeps every documented request body in line with the backend's Zod declarations.

Added

  • routeBody(schema, 'create' | 'patch' | 'array') (@/common/validation): a route declares its JSON body once — .route goes into the route options (Fastify route config, so nothing runs before the login and permission checks), .parse(request) validates after the permission check. All 79 body-reading routes, the backend template and pnpm scaffold use it; test/conventions.test.ts rejects a direct parseBody / parsePatch / parseArrayBody in a routes.ts.
  • body-sync OpenAPI rule (apps/api/scripts/lib/openapi-body-sync.ts): compares each documented JSON request body with the route's declaration — nullability, requiredness, types and enums per property, nested objects and array items included — wherever the doc rules run (pnpm openapi:generate --strict, verify's openapi_sync, test/openapi-doc.test.ts). Export fields[] / file_type enums are the contract by design; other intentional differences live in BODY_SYNC_ALLOWLIST with a reason each, and a stale entry fails.
  • Generated frontend API types: apps/web/src/shared/api/openapi.d.ts (by apps/web/scripts/api-types.mjs) with ApiItem / ApiResponse / ApiQuery / ApiBody helpers in @/shared/api/types; pnpm openapi:generate regenerates it and test/api-types.test.ts fails when it is stale.
  • Traffic flow in the component gallery (/component-center/dataviz/traffic-flow, permission cc_dataviz_traffic_flow): a Sankey diagram of visits (source → landing page → outcome) and a conversion funnel, replacing the map heatmap. Migration 0001_traffic_flow_menu renames the existing menu in place, so role grants carry over.
  • docs/shadcn-changes.md: every project change to an upstream shadcn / AI Elements component, to re-apply after re-adding one.
  • Component layer boundaries enforced by a test: shadcn primitives import only primitives; shared components get data through props instead of calling the API or reading app context.

Changed

  • Frontend in TypeScript (plan and details in docs/roadmap.md "TypeScript frontend"):
    • apps/web/tsconfig.json is strict (same options as the API: noUncheckedIndexedAccess, verbatimModuleSyntax); src is TypeScript only (test/typescript-only.test.ts), and tests and the Vite / Vitest configs are checked by tsconfig.test.json. pnpm lint lints the web app too.
    • Everything is typed: shadcn primitives (components.json tsx: true), shared components (DataTable<Row> with typed columns, FormFields / FormDialog over react-hook-form, generic trees and selects, Chart over echarts' EChartsOption), contexts (useAuth() / useTagsView() return non-null values), hooks (useCrudList<Row>), every module API file and every page.
    • pnpm scaffold writes api/<name>.ts typed from the module's OpenAPI entries and a list page index.tsx with FormValues and DataTableColumn<Row>[], so a form that doesn't match the documented body fails tsc; it regenerates openapi.d.ts itself. docs/templates/frontend/ is TSX.
    • Idiomatic cleanup: casts and non-null assertions replaced by narrowing, type guards and precise generics (the few left are commented); naming and export conventions documented in AGENTS.md "TypeScript".
  • English first:
    • Developer specs, skills and MCP tool descriptions in English; UI copy is still Chinese source text as the i18n key (t('中文原文')) with English and Japanese translations.
    • English is the fallback: the UI uses it when neither a saved choice nor the browser language matches; the API answers in English when a request has no supported Accept-Language (curl, API-token clients). Page title and <html lang> are English.
    • Tool output in English (pnpm verify, pnpm scaffold incl. spec validation and docs/spec.schema.json hints, pnpm openapi:generate, pnpm seed:rbac, setup / seed scripts, scripts/setup.sh). Generated code keeps Chinese UI copy as the i18n key, and generated OpenAPI text stays Chinese.
    • Docs site: English at the root (/guide/…), Chinese at /zh/, Japanese at /ja/; old /en/… links redirect.
  • Repository root tidied: the Docker setup wizard is bash scripts/setup.sh (entry point scripts/docker-entrypoint.sh); README.zh-CN.md; .env.production.example; llms.txt served at /llms.txt; one shared AGENTS.md (+ CLAUDE.md) instead of per-tool rule copies.
  • Faster first page opens: ECharts registered on demand (chart pages load about 40% less code); sign-in, reset-password and profile pages lazy-loaded (first download about a fifth smaller); page code prefetched on menu hover / focus and, for light pages, while the browser is idle (skipped in data-saver mode).
  • Scaffolded modules document export ids / fields / file_type as nullable, and only required fields without a default are non-null and listed in required.

Fixed

  • OpenAPI request side matches the backend: 332 differences between documented request bodies and the Zod field.* declarations fixed (nullable fields with their defaults, '' = "all" filters, export_mode: all, validated enums on announcements / menus, required webhook fields, descriptions that promised lenient parsing where the API returns 400).
  • Dashboard network tiles always showed 0.00 (they read fields the API doesn't return); they now show cumulative traffic since boot with a readable unit.
  • Card list: a card with no is_active value showed the switch off but was saved as enabled; edits sent id / timestamps along with the form.
  • Heatmap dates were a day off before 08:00 in UTC+8; times on the dashboard, perf monitor and WebSocket pages ignored the UI language; the code editor reported success for languages it can't format; the AI SQL schema sheet showed "no tables" after a failed load; the prompt studio kept untrimmed values after saving; kanban sent board_code on update; untranslated list-page section titles and image-upload hint; Three.js pages didn't resize with their container; smaller fixes in DataTable keys, zero page size, the particle canvas and the code highlighter.
  • Tags view: even spacing between tabs (the close button on inactive tabs no longer leaves an invisible gap).

Upgrading from 0.1.0

  • Run pnpm db:migrate (migration 0001_traffic_flow_menu) and pnpm seed:rbac -- --incremental.
  • Frontend pages must be index.tsx: page routing no longer finds index.jsx, and apps/web/src accepts no .js / .jsx files. Convert custom pages and components to TypeScript (see AGENTS.md "TypeScript" and docs/templates/frontend/).
  • Backend routes read JSON bodies through routeBody(...) instead of calling parseBody / parsePatch / parseArrayBody directly (enforced by test/conventions.test.ts), and pnpm openapi:generate -- --strict now also checks request bodies against the Zod declarations.
  • API clients that send no Accept-Language now get English messages; send Accept-Language: zh-CN for Chinese.
  • Docs site links: English pages moved from /en/… to the root (old links redirect), Chinese pages from the root to /zh/….
  • Moved at the repo root: setup.sh → scripts/setup.sh, docker-entrypoint.sh → scripts/docker-entrypoint.sh, README_CN.md → README.zh-CN.md, the production env reference .env.example → .env.production.example (local development still uses apps/api/.env.example).