Otche frontend

Canonical React 19 / TypeScript / Vite source for the Otche Windows/Defender observation UI. nginx serves the built assets and proxies same-origin /api/ to the backend. It contains no PVE credentials, database credentials, sample files or demo verdicts.

Repository ownership

This repository owns src/, frontend dependencies and lockfile, Vite configuration, nginx and the frontend Docker image. The Go API/worker/watchdog and unsigned Windows runner are owned by otche-backend; Compose and complete installation instructions are owned by otche-deploy. Clone all three as siblings, not inside one another. Backend processes share one Go module; they are not copied here.

Build and development

Node.js 22 and npm are required for the local build; Docker needs no host Node installation.

npm ci
npm run build
docker build -t otche-web:local .

dist/, dependencies, .env overrides and build metadata are generated/ignored. There are no external static assets or submodules required by the build. Keep package-lock.json tracked.

Run the permanent Chromium browser suite with npx playwright install chromium followed by npm test. Playwright starts an isolated Vite server on loopback port 4173 and exercises the routed application with deterministic HTTP fixtures; no running backend, administrator credentials, or private state is needed. Coverage includes key creation/scopes/expiry, one-time token handling, failed responses, revocation, limits, mobile layout, and the read-only documentation catalog. e2e/fixtures/openapi.json is a verbatim snapshot of the backend's embedded internal/api/openapi.json; refresh it when that public contract changes. Traces and reports use the ignored test-results/ and playwright-report/ directories.

For local development, start the real API with a dedicated local database, PUBLIC_ORIGIN=http://127.0.0.1:5173 and ALLOW_INSECURE_HTTP=true, then:

npm ci
npm run dev

Vite listens on loopback port 5173 and proxies /api to http://127.0.0.1:8080. Override that target with OTCHE_API_PROXY if needed; retain the exact browser origin at the API. Production nginx talks to Compose service api:8080; use the sibling deploy repository for the actual app, not a standalone frontend container with no API.

See the backend API contract. Session cookies and CSRF checks are enforced by the API. Missing configuration/qualification is displayed as a blocker, never converted into fake successful analysis. Profile labels render the complete manually edited name verbatim, including any build/version text the operator chooses; no build is automatically derived or appended. Historical Runs display their stored name snapshot. Actual report OS/build telemetry remains separate and unchanged. Grub archives are authenticated private downloads, not public static assets.

On mobile, the navigation drawer moves focus into the visible menu, contains Tab/Shift+Tab, and makes the workspace inert while open. Escape closes it and restores the opener; resizing to desktop clears the modal state. Keyboard focus waits for actual layout visibility rather than a fixed animation delay.

Report file collection is shown only in the selected attempt's Grub panel: requested paths, collection results and its ZIP download. Internal telemetry JSON, logs, manifests and video segments are not listed as user file downloads. Video remains in the dedicated player; diagnostic artifacts remain stored and protected by the existing API.

The report journal follows the selected machine attempt, using the same event scope as its video timeline. Switching a machine or historical attempt replaces the journal; job-wide and other-attempt events are not mixed into it.

API keys and integration reference

The authenticated navigation includes API-ключи (/api-keys) and Документация API (/docs). The read-only documentation page uses the public OpenAPI 3.1 contract at /api/v1/openapi.json; it does not execute requests. API-key management always uses the existing browser session and CSRF-protected same-origin client, never a Bearer token or administrator endpoints.

Key creation requires a name (trimmed, 1–80 characters, no control characters), an integer lifetime of 1–365 days (UI default: 90), and explicit scopes. The six available scopes are profiles:read, jobs:read, jobs:write, uploads:write, uploads:read, and artifacts:read; only the first two are selected initially. Write scopes never imply read scopes. Keys access only their owner's data, including keys created by administrators, and cannot access administration, authentication, or key-management endpoints. At most 20 unrevoked, unexpired keys may coexist.

The complete token appears only in the successful creation panel. It remains only in component memory, is never stored in browser storage or URLs, and is copied to the clipboard only by explicit user action. Dismissing the panel, leaving the page, or reloading discards it; save it in a secure secret store first. The registry shows only prefixes, permissions, dates, and active/expired/revoked states. Last use is approximate to a minute. Revocation requires confirmation and cannot be undone.

Mutations refresh the registry without clearing a newly issued token on a list-refresh failure. Failed creation is never automatically retried: an interrupted response may mean the server created a key whose token was not received. Refresh the registry, revoke that inaccessible key if present, then create a replacement. Polling the metadata list does not resend creation or revocation requests.

Visual system

The complete interface uses a Windows 95/98-inspired light design: patterned teal desktop, silver application surfaces, navy window title bars, raised/sunken square controls, system typography and real-data monospace counters. src/tokens.css owns palette/font/bevel tokens; src/styles.css is the single component/responsive stylesheet, not a theme override layered over the former dark UI. No remote fonts, decorative dependencies or simulated system controls are required.

Login, explorer-style navigation, dashboard, job registry, five-section analysis configuration, machine properties, selected-attempt reports and administration share the same panel primitives. The login marquee and heading accent are decorative only; reduced-motion disables their animation. Critical findings, readiness and errors never animate or infer success from color. Desktop/tablet/mobile layouts retain readable text, native form controls, dotted focus indicators, and internally scrollable data tables. Grub's multi-control editor uses a labelled fieldset rather than labelling its add button as the entire group.

Machine cards share aligned heading/revision/attempt rows; the selected machine and attempt ID have separate labelled areas. Defender events use retro event windows with explicit action failures (including 1118/1119), not generic success alerts. Before/after snapshots render Russian configuration labels and known enum meanings; inverted Disable* flags are interpreted as configuration only, missing/unknown values remain explicit, and raw snapshots are collapsed. ASR IDs/actions stay paired with incomplete-list warnings. No configuration label implies that a file is safe.

The Windows selector offers select-all-eligible and clear actions using the same admission predicate as individual choices (enabled, published, qualified, plus online availability when required). Busy/loading states prevent unsafe bulk actions. Loading uses a segmented indeterminate bar without invented percentages; short stepped window-entry effects and genuine progress indicators honor reduced-motion.

S
Description
Otche React/TypeScript frontend
Readme
594 KiB
Languages
TypeScript 81.8%
CSS 17.6%
JavaScript 0.3%
HTML 0.2%