Repository navigation
Commit 1345322
authored
feat: serve the web UI from sqb server and ship it in the wheels (#1329)
* feat(ui): add the orchestration UI prototype as a SvelteKit app
Port the prototype pages, components and styles into ui/ as a static single-page app, with neutral example owners and data.
* fix(ui): resolve the prototype's type-check errors
Wire the lineage hover handlers to the current event names, declare the run gantt element refs as state and add Node types for the generated tsconfig.
* feat(ui): add a typed API client layer with a mock implementation
Pages and the shell read through an ApiClient interface shaped like the server API, with changed-signal subscriptions for live refetches. A mock client provides neutral example data, a coalescing warehouse sync and per-target trigger state.
* feat(ui): add the orchestration shell with location and target switchers
The sidebar follows the new navigation with capability-gated admin items and live counts. The top bar shows location / target, connection state, snapshot age and warehouse sync freshness with a coalescing refresh, above a platform-alert banner. Pages without content yet render a not-built state.
* feat(ui): read prototype pages through the API client
Overview, Runs, Jobs, Triggers, Checks and Connections load from the client and refetch on changed signals. Run statuses use the orchestration vocabulary, trigger state shows the code default, and gated actions are disabled without the matching capability.
* feat(ui): add platform and admin API types and mock client
* feat(ui): gate the warehouse refresh behind warehouse.sync
* feat(ui): build the Alerts page with platform health and event history
* feat(ui): build Locations and rework Connections into warehouse and providers
* feat(ui): build the Maintenance page from synced migration and janitor state
* feat(ui): build Admin pages and read-only Settings
* feat(ui): rebuild the runs list, queue, timeline and run detail
Runs list with the design's statuses and kinds, trigger and tick links,
requested-by, node counts and nullable cost, with group parents expanding
to their child runs. A Queue tab shows why each queued run waits (write
claims, full concurrency groups, priority, upstream jobs) and its overlap
outcome. The timeline projects schedule and calendar ticks only and marks
covered and skipped ticks.
Run detail keeps the Gantt, status rail and tabs, and now shows nodes with
skipped-because-of across jobs (kept in the graph), Recall events and
scheduler facts, output, per-statement SQL, liveness, cancel progress,
provenance and invocations. Cancel, Rerun and Rerun from failure are
capability-gated.
The mock client gains an operate store that derives node outcomes, events
and run status from run records, and a small scheduler loop so claims,
cancels and launches progress like they do on the server.
* feat(ui): rebuild the run panel from the CLI command table
The panel's verbs and flags are generated from the CLI's command table by
scripts/extract-command-table.mjs (npm run gen:command-table; --check fails
when the generated file is stale, for CI). Selectors autocomplete from the
snapshot's nodes. Before launch the server check shows the resolved
selection, the write set, run.adhoc coverage (refused as a whole, listing
out-of-scope nodes), whether run.rewrite_history is needed, and the claims
the run would queue behind. The target is locked to the target switcher,
and launching opens the queued run.
* feat(ui): show jobs and job groups with their code declarations
One list for jobs and groups with a kind filter and no create button.
Detail shows the read-only declaration and its source file, the resolved
nodes, settings, overlap, on_failure, concurrency groups with current
holders, priority, targets and owner; for groups, the members in their
lineage-derived order with after edges and critical nodes. Attached
triggers can be paused or enabled (an audited state fact), recent runs are
listed, and Launch takes an optional cursor window or full refresh gated
by run.rewrite_history.
* feat(ui): show triggers and sensors with tick logs and evaluations
The list covers cron and calendar schedules, built-in job, node and
platform-event sensors and custom @sensors, with running or paused state
against the code default, reset to code default, and bulk pause or enable
recorded as audited facts with an optional reason.
Detail shows the tick log (requested, skipped, covered by run X, failed,
deferred, late and catch-up), next due times, the calendar's stored
horizon with its low-horizon warning, and for sensors the condition,
evaluations (requested, completed, failed, timed_out, abandoned), the
cursor with a reset gated by sensor.reset_cursor, run requests by run key,
a code-changed-since-last-evaluation notice and the override history.
* fix(ui): explain queued group children waiting for their parent run
A child run whose group run hasn't been claimed now says so, instead of
reading as waiting for an upstream job. The README documents regenerating
the run panel's command-table extract.
* feat(ui): split run kind into what runs and why it was requested
Runs now carry kind (job, group, adhoc) and origin (manual, schedule,
sensor, rerun). Group children are job runs that inherit the parent's
origin. The run list gains an origin filter and labels show both.
* refactor(ui): drop the deferred tick outcome
Tick outcomes are requested, skipped, covered and failed. The tick that
could not be evaluated while the code location was unavailable is now a
failed tick.
* fix(ui): point the overdue tick alert at an overdue schedule
The tick_overdue event named activity_microbatch, which ticks normally.
A new hourly segments_hourly schedule has its last tick 72 minutes ago
and no evaluation since, so its next due time is in the past and the
triggers list shows it overdue with a watchdog warning. Platform
fixtures that still named the removed nightly_build job now point at
existing jobs and nodes.
* feat(ui): disable users and create service accounts
Users admin gains Disable (revokes the account's sessions, confirmed
first) and New service account (name and description; it can then be
issued API tokens). Both need user.manage and are audited by the mock.
The mock refuses disabling yourself and removing your own last admin
assignment, recording the refusal as a denied audit entry, and the roles
page flags that assignment with a stronger warning.
* refactor(ui): delete the superseded connection list API
The reworked Connections page reads the connections overview, so the
unused listConnections endpoint, the old Connection type and its
fixtures go. Mock principals are imported from principals.ts directly.
* feat(ui): re-enable disabled accounts
* feat(ui): rebuild the overview on a BUILD data layer
Add the BUILD block to the API client (overview, catalog nodes, column
lineage, checks, evidence and the snapshot change feed) with wire types in
types/build.ts and a mock that derives build state from the run history,
builds outside the scheduler and the change feed.
The overview now shows typed attention items (failed and partial runs,
stale nodes split into changed and behind with a gated Rebuild, late
sources, platform events, unresponsive runs, blocked queue entries, low
calendar horizons and pending role approvals), metrics with partial runs
counted separately and 24-hour cost against a rolling baseline where the
adapter reports cost, build status by group, and recent snapshots with
their git author.
* feat(ui): show latest check results with history and audited evidence
Checks list the latest audit result per check with severity, run scope
phase, evaluation mode, outcome and violations or measured value, plus a
strip of recent outcomes. Each check has a page with its history, the
executed SQL (view.sql) and stored evidence rows (view.evidence, audited).
Run checks opens the run panel on sqb audit over the visible checks' nodes.
* feat(ui): back lineage and catalog with build state from the API
Lineage and Catalog now read catalog nodes from the client instead of
inline prototype data. Last outcome and stale reasons (changed, changed
upstream, behind) are shown separately, build age is labelled Last built,
and drift and SQL compare against the definitions stored with each
fingerprint in this target; builds outside the scheduler show their
fingerprint run and time.
Nodes carry an owner list that can be filtered. The catalog page gains
Drift, Runs, Checks, Warehouse (object, declared against actual columns,
row statistics and microbatch coverage) and History (renames, former
names and old-name views) tabs. Graph selections and the toolbar filters
feed the run panel, and the large-graph canvas is kept.
* feat(ui): show the snapshot change feed per deploy
Changes lists each published snapshot with its generation, git revision,
author and publisher: nodes added, removed, changed or renamed with their
column changes, jobs, triggers and sensors, roles with the nodes they now
cover, and environment fingerprint changes. Entries link to the affected
nodes and to the runs that followed, and show per-run cost changes by job
where the adapter reports cost.
* feat(ui): replace Alerts with a Health page and drop notification channels
Alerting is done by sensors, and where their messages go is an ordinary
provider used by the sensor's code, so the separate channel concept is gone:
no Channels panels, types, client method or mock data. A chat_webhook
provider shows the pattern, with the platform-event sensor as its user.
The Alerts page becomes Health under Platform. It shows the scheduler
lease, server replicas, the heartbeat and the read-only server-side
platform-event sink, followed by the platform event history. Each event
links to the trigger, job, run or location it affects and to the sensors
that reacted to it, with how each handled it. The banner, overview
attention items and nav badge now point at /health.
* feat(ui): redesign the runs list with status tabs, URL filters and bulk actions
* feat(ui): redesign run detail with header actions, failures and a filtering timeline
* feat(ui): fold server health and problem history into Locations
* feat(ui): give providers their own pages apart from warehouse connections
* feat(ui): split people from service accounts and drop personal API tokens
* feat(ui): rewrite page copy in plain language
* fix(ui): match check wording to how audits run
* fix(ui): use the shared checkbox style on the runs list
* feat(ui): deploy each location against exactly one target
A location is now one project deployed against one target, so staging and
dev become their own locations. The sidebar keeps a single location
switcher that reads "name · target · adapter", the target switcher and the
?target= deep link are gone, and the data layer scopes everything by
location only.
The location page drops the Targets, Warehouse sync and processes tables
and the side rail: the header shows the deployed version, a one-line
process summary (listing processes only when none or several run), the
warehouse connection and a collapsible Details; access tokens move into
the page.
* feat(ui): report warehouse sync failures as location problems
Adds the warehouse_sync_failed platform event kind, labelled "Warehouse
sync failing", so a sync that can't read one of SQLBuild's tables shows
up in the location's Problems and the banner instead of a per-table sync
table. The staging example location demonstrates it.
* fix(ui): show open location problems by default and hide tokens without access
* feat(ui): model custom roles as a base role plus additions
Roles declare the built-in role they extend and their additions. Assignments carry their source (manual, SSO group, policy file or built-in), denials carry a decision ID, and approvals carry the definition diff and who gains what. Adds a shared permission catalog with a plain name and risk for every action, used by the Requires hints.
* feat(ui): explain access with the chain, nearest miss and denial IDs
One access checker for Settings, person pages and a Check access page. It accepts a denial ID from a permission error, shows the assignment, role, grant and scope behind a yes, and for a no the nearest miss and whether a pending change would allow it.
* feat(ui): make People the default admin page with per-person access
Users becomes People: role per location with source chips and filters for role, location, source and risky actions. Each person's page lists their roles (derived ones locked), an effective-access grid whose marks open the reason, recent activity and disable or enable. Giving a role is limited to roles you hold.
* feat(ui): list service accounts in a table with an account page
The same shape as People plus owner, token count and last use. Each account's page has its tokens, roles, effective access and recent activity.
* feat(ui): split Roles into roles, SSO mappings and approvals
Roles show base plus additions, where they are defined and who has them; each role's page explains its additions with risk tags, what its selectors match today and their growth, and can compare with the base role. SSO mappings are read-only. Approvals show who gains what, the definition diff and narrowing already applied, need a comment, and block approving your own commit.
* feat(ui): show plain action names and denial IDs in the audit log
Actions read in plain words with the code on hover. Denied rows show their copyable decision ID and a Check this link to the access checker.
* fix(ui): check approval refusals as the role permission behind them
* fix(ui): label config-assigned roles as server config and the default role plainly
* feat(ui): match check, maintenance, coverage and sync reads to the control API
A check's SQL and execution error come from a separate audited view.sql read, loaded on request, because driver errors quote SQL. Evidence cells can be booleans and columns are the rows' sorted keys. A move discovered from the warehouse has no origin model, so pages show its origin relation. Microbatch coverage bounds can be integer or timestamp text, the frontier can be missing, and the panel says coverage is tracked only for concurrent microbatch models, with known gaps meaning holes between completed partitions. The warehouse sync runs on the location's schedule, so the shell shows when it last synced and the on-demand refresh and its permission are gone.
* build(ui): sync the lockfile with package.json
npm ci refused the lockfile because two optional build dependencies were missing from it. Regenerated with npm install; the locked @lucide/svelte stays at 1.17.0, which type-checks cleanly.
* feat(ui): drop role approvals and service accounts
The control server has neither: roles defined in code apply when their snapshot is published, and people sign in with single sign-on, a password or sqb login. The approvals tab, its banner, attention item, nav badge and change-feed tag go, along with the service-account screens, API tokens, the token.manage permission and the service-account principal kind. Mock CI and reporting actors become ordinary people.
* wip: person bootstrap read and person-authorized change feed
Checkpoint, not ready for review. The grant decision is now a pure function
shared by authorize_person (which still audits denials) and an unaudited
capability read. GET /api/control/bootstrap lists the locations a person can
view; the person change feed needs View on every node of the target, rechecks
the session and grants before each notice, and drops per-run notices.
Validated: targeted orchestration tests on SQLite and PostgreSQL, clippy,
fensu, and the TypeScript client check and test. No UI changes yet.
* fix: name no nodes in refused listings
A refused location listing named the nodes the person could not view and
read differently for a location that does not exist. Listing refusals now
carry one fixed message with the decision ID, identical for unassigned and
unknown locations. The audit event keeps the missing nodes.
* wip: live shell UI on the control API
Checkpoint, not ready for review. The UI talks to the server unless ?mock or
VITE_SQB_MOCK=1 selects the mock: password sign-in with return to the
requested page, sign-out, the bootstrap's locations in the switcher, the
selected location's change feed with live, reconnecting and offline states,
and navigation entries hidden when the server has no endpoint for them.
Validated: svelte-check, and a browser walk-through against sqb server with a
SQLite store: login rejection and success, location switch, server outage and
recovery, sign-out, and a person with no roles. No UI unit tests yet.
* fix: name no nodes in authorization denials
Every refusal from person authorization now shows fixed text per action
and its decision ID; the audit event keeps the missing nodes. Unknown
jobs and triggers are refused only after the caller is authorized for
the target, so an unknown object reads the same as a forbidden one.
* test(ui): unit-test the change feed, navigation gating and sign-in redirect
The sign-in redirect now resolves next against a fixed origin, so
backslash and control-character variants of //host are refused too.
* ci: type-check and unit-test the UI when it changes
* ci: run the UI job when the verify workflow changes
* fix(ui): stop retrying a refused change feed and dot-segment redirects
Before a retry the feed now asks the feed URL itself and stops on 401 or
403. The bootstrap lists a location when any node is viewable, so a
partial viewer retried the refused feed forever, auditing each refusal.
The sign-in redirect also refuses paths that resolve to //host.
* ci: run the UI job when the generated control client changes
* wip: serve the built UI from sqb server behind a ui feature
Checkpoint, not ready for review. With the ui cargo feature, a build script
embeds ui/build (or SQLBUILD_UI_DIST) and sqb server answers GET and HEAD
outside /api and /auth with the matching file, or the index for client
routes. Hashed assets are immutable; the page carries a hash-based CSP meta
policy and the server adds frame-ancestors. The theme script moved to a
static file so no inline script needs a hash.
Validated: a fixture-table contract test of routing, caching and CSP
headers, with a mutation check; clippy with all features; fensu; svelte-check
and vitest; and a real sqb server built with the feature behind HTTPS, where a
deep link redirected to sign-in, sign-in returned to the page, and the shell
rendered its empty state. Not yet: wheel builds, a populated location, make
check-ci.
* wip: harden UI path handling and ship the UI in the wheels
Checkpoint, not ready for review. A UI path containing a double slash, a
backslash, a dot or dot-dot segment, or a percent-encoded dot, slash or
backslash is refused instead of reaching an asset or the shell. Every wheel
job builds ui/ with Node and builds sqb with the ui feature; the linux-x86_64
smoke starts the packaged sqb server and requires the shell and an asset.
Changes under ui/ now select the wheel builds.
Validated: a raw-request contract test of 22 path cases (dot segments, encoded
traversal, slashes, backslashes, /api versus /apix, query strings and a
fragment) with a mutation check; clippy with all features; fensu; the smoke
commands against a locally built wheel, which fail on a wheel built without
the feature; the scope classifier on a ui-only change. Not yet: make check-ci,
CI wheel builds on every platform, review.
* build: fail wheel builds that would ship sqb server without its UI
Wheel jobs set SQLBUILD_UI_REQUIRED, which turns the missing-UI build warning
into a build failure on every platform, not only the one the smoke step
covers. Local builds keep the warning. The theme script now follows the page's
CSP meta tag so the policy governs it.
* fix(ui): explain a server without person sign-in instead of reporting it unreachable
A server started without password, OIDC or trusted-proxy sign-in has no
bootstrap endpoint, so the shell reported a 404 as an unreachable server with
a Retry button that cannot help. It now says the server has no person sign-in
configured and links to the server documentation.1 parent cf07c40 commit 1345322
21 files changed
Lines changed: 695 additions & 23 deletions
File tree
- .github/workflows
- crates
- sqlbuild-cli
- sqlbuild-orchestration
- src/api
- _helpers/resources
- main
- tests/api
- ui
- src
- lib
- state
- routes
- static
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
126 | 138 | | |
127 | 139 | | |
128 | 140 | | |
| |||
142 | 154 | | |
143 | 155 | | |
144 | 156 | | |
| 157 | + | |
| 158 | + | |
145 | 159 | | |
146 | 160 | | |
147 | 161 | | |
148 | | - | |
| 162 | + | |
149 | 163 | | |
150 | 164 | | |
151 | 165 | | |
| |||
213 | 227 | | |
214 | 228 | | |
215 | 229 | | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
216 | 243 | | |
217 | 244 | | |
218 | 245 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
151 | | - | |
| 151 | + | |
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
157 | | - | |
| 157 | + | |
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
| |||
1144 | 1144 | | |
1145 | 1145 | | |
1146 | 1146 | | |
| 1147 | + | |
| 1148 | + | |
1147 | 1149 | | |
1148 | 1150 | | |
1149 | 1151 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
10 | 13 | | |
11 | 14 | | |
12 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
| 81 | + | |
80 | 82 | | |
81 | 83 | | |
82 | 84 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
418 | 418 | | |
419 | 419 | | |
420 | 420 | | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
0 commit comments