Files
Douyin_TikTok_Download_API/web
Evil0ctal 078ae863bd docs: finish the API document, in both languages
Two gaps, both invisible from the code.

**Request bodies were undocumented.** `schemas.py` carried zero `description=`,
so every field a caller has to fill in reached Swagger as a bare name beside an
input box - 85 of them. The knowledge was already written, as `#:` comments,
which readers of the source get and readers of Swagger do not; most of what is
added here is that text moved somewhere it can be seen. The envelope schemas
every response is described by had the same problem in five fields.

**Only part of the document was translatable.** Operations, tags and parameters
went through the catalogue; request-body fields had no lookup at all, so the
Chinese document explained what an endpoint did and then asked for
`skip_existing` in English. Added `openapi.field.<Schema>.<field>`, keyed by
model rather than by field alone because a field name is not global - `url` is a
share link on ParseRequest and a proxy address on ProxyCreate.

Of the 53 parameters in the document, 36 had no catalogue entry, `explain`
among them. All 53 now do. A parameter may also be worded per operation
(`openapi.param.<operation>.<name>`) where its name says nothing about its role:
`identity_id` filters the log on one route and names the identity being retired
on another, and the English source is already two different sentences there.

The envelope localisation had to move after `_type_responses`, which is where
those schemas are added - running it earlier translated every request body and
left DtkResponse and DtkError in English, which is what the new test caught.

`test_openapi_completeness.py` pins both halves: nothing unlabelled in English,
nothing without a CJK character in Chinese. The second is checked by looking for
Chinese rather than by diffing against the English, because the failure being
guarded produces two identical strings.

Also: the About page now carries the author's contact - the issue tracker and an
email - with a line on each saying which to use, since most of what arrives by
email would have been answered faster in the tracker.
2026-09-10 00:33:03 -07:00
..

dtk console

React SPA for the dtk v5 API. Built to static files and served by the api container - no Node runtime in production, no SSR.

Specs this implements: docs/design/07-frontend.md (what the pages are), docs/design/12-design-system.md (what they look like - authoritative for anything visual), docs/design/14-i18n.md (language rules) and docs/design/06-api-auth-mcp.md (the API).

Commands

npm install
npm run dev        # http://localhost:5173, proxies /api to http://127.0.0.1:8000
npm run build      # typecheck, then emit web/dist
npm run preview
npm run typecheck
npm run lint       # eslint + locale parity/ICU/error-code coverage
npm run verify     # all three, in the order CI runs them

DTK_API_TARGET overrides the dev proxy target. VITE_API_BASE_URL is only needed when the console is served from a different origin than the API.

Layout

src/
  main.tsx            theme + i18n bootstrap, root render
  App.tsx             shell, router, setup gate
  lib/                api client, formatting, i18n, theme, tokens of behaviour
  components/         the design-system inventory (import from '@/components')
  hooks/              shared hooks (import from '@/hooks/...')
  locales/{en,zh}/    common | console | errors | setup
  pages/              one file per route
  styles/             tokens.css, base.css, utilities.css

@/ resolves to src/. TypeScript runs strict plus noUncheckedIndexedAccess and verbatimModuleSyntax, so index access is T | undefined and type-only imports need import type.

Non-negotiables

These are enforced by lint or by review; do not work around them.

  1. No colour literals outside styles/tokens.css. local/no-raw-hex-color fails the build on a bare hex in any source file. Use var(--accent), var(--danger), var(--bg-raised). Both themes are complete: anything you add must be readable on #0B0D10 and on #FBFCFD.
  2. No CJK in .ts/.tsx. local/no-cjk-source covers comments, strings and identifiers. Chinese lives in src/locales/zh/*.json only.
  3. Every user-visible string goes through t(), and en/zh key sets must match exactly - npm run lint fails otherwise. Never translate error codes, state enum values, field names, endpoint paths or config keys; translate their display labels instead.
  4. Status is colour + icon + text. Use <StatusBadge kind="identity" value={row.state} /> rather than colouring a cell. business_error renders muted, never red: a deleted video is not a system fault.
  5. Ids and JSON are mono. <CopyableId> for any id, mark columns mono, and <CodeBlock json={...}> for payloads.
  6. Polled values never animate. Pass flashValue to DataTable so a row highlights once when its status changes; do not add transitions on refresh.
  7. Focus rings stay. No outline: none without a visible replacement. Everything must be reachable and operable with the keyboard, and overlays must close on Esc.

Calling the API

import { apiGet, apiPost, apiRequest, isApiError } from '@/lib/api'
import { paths } from '@/lib/endpoints'
import { useApiQuery, useApiMutation } from '@/hooks'
import { POLL } from '@/lib/query'

// Query with polling; errors arrive as ApiError.
const identities = useApiQuery<Identity[]>({
  key: ['identities', filter],
  path: paths.identities.list,
  params: { state: filter },
  poll: POLL.fast,          // 5s. Use POLL.normal / POLL.slow, do not invent one.
})

// Meta (cursor, request_id, duration) when you need it:
const { data, meta } = await apiRequest<RequestLogRow[]>(paths.logs.requests)

The client handles the async-first protocol for you: a 202 {task_id} is polled against /api/v1/tasks/{id} and the promise resolves with the finished result. Pass awaitTask: false to get the raw task id (batch submits do their own orchestration), or use waitForTask / openTaskEvents directly.

It also sends Accept-Language from the current UI language, records the X-RateLimit-* and Retry-After headers (getRateLimit()), and routes a 401 to the login page.

Failures: error.code is the stable enum (ApiErrorPayload is a discriminated union, so switch (error.payload.code) narrows), error.kind separates an API failure from network, timeout, aborted and malformed. Render one with <ErrorState error={query.error} onRetry={query.refetch} /> or toast.apiError(error); both show the code, the localized message, the hint and the request_id.

Formatting

const f = useFormatters()   // bound to the active language
f.compact(12800)            // en: 12.8K   zh: 1.28万  (Intl, not a translation)
f.number(1234)  f.percent(0.982)  f.bytes(1234567)
f.timestamp(row.ts)  f.relative(row.ts)  f.duration(ms)  f.latency(ms)
f.timeZone()                // { iana, short } for chart axes

Missing values render as an em dash. null and 0 are different facts - never coerce one into the other.

Components

Button Input Select Textarea Checkbox Switch Field · StatusBadge ErrorCodeBadge CopyableId MaskedSecret · Card PageHeader MetricTile EmptyState ErrorState Skeleton · DataTable CodeBlock TimeSeriesChart · Modal Drawer ConfirmDialog Toast Menu · Sidebar TopBar ThemeToggle LanguageSwitcher ErrorBoundary.

DataTable gives you sorting, a column picker, a density toggle, a sticky header, skeleton, empty and error states, optional selection, and degrades to cards under 768px. Give it a storageKey so the viewer's column and density choices persist.

TimeSeriesChart is hand-rolled SVG. Colour series from the status palette: success rate var(--success), risk rate var(--danger), volume var(--accent), categorical comparisons var(--series-1..6).

Pages

src/pages/*.tsx are scaffold placeholders that render a PageHeader. Replace the body, keep the console:page.<name>.* keys, and add page-specific keys to locales/{en,zh}/console.json in the same commit. Routes live in App.tsx and navigation entries in lib/nav.ts.

Setup gate

App.tsx reads GET /api/setup/status. While initialized is false, every route redirects to /setup. If that call fails at the transport level the console fails open rather than locking the operator out of the pages that would explain the outage - only a hard network error renders a full-page error.

Responsive

< 768px sidebar becomes a drawer and tables become cards; 768-1280px the sidebar is icons only; >= 1280px the full 240px sidebar with a 1440px content column. The console must stay usable on a phone: that is where the 3am alert lands.