Commit Graph
694 Commits
Author SHA1 Message Date
Juan DenisandClaude Fable 5 2f66507a0c feat(ui): restyle the Terminal/Logs page to the flush console look
Log Files + Journal tabs -> single bordered console frame (con-rail
target list with mono eyebrow groups, accent-bg active rows, session-head
viewer bar, log well on --bg-code); Processes tab -> same frame with
sk-dtable-spec sortable table + token gauges; Services tab cards re-
tinted with status stripes. Terminal gets its OWN proc-chip filter chips
(no longer depends on _templates.scss .filter-chip); process/service
statuses -> ds Pills; hashColor/priority palettes retinted to the new
palette. Legacy block kept for shared consumers (JournalControls/
ProcessTable/ServiceCard).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 00:17:54 -04:00
Juan DenisandClaude Fable 5 b38053d448 feat(ui): restyle the Cron Jobs body to the new look
StatCards -> MetricCard KPI strip (jobs/active/service/platform from real
status), job cards -> dense .sk-dtable (clock tile, mono schedule chip +
readable hint, Pill status, all 4 row actions kept incl. run-now), new
list head with search + All/Enabled/Disabled SegControl (client-side
only), modals reskinned with mono eyebrows + --bg-code run output.
Next-run/history/success-rate omitted (backend gap, map S5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 00:17:53 -04:00
Juan Denis 7e272492ef docs: mark FTP/Monitoring/ServerDetail/ServiceDetail+Templates body restyles done 2026-06-10 23:46:10 -04:00
Juan DenisandClaude Fable 5 a16ff1b8f8 feat(ui): restyle ServiceDetail tabs + Templates catalog to the new look
ServiceDetail: header -> ServiceTile gradient avatar + ds Pill + mono
dotsep meta (adds the real domain link); OverviewTab -> MetricCard strip
+ sk-info-row key/values + EnvTag + Gauge bars, deploy statuses -> Pills;
MetricsTab gauges tokenized (no fake history charts - backend gap);
Packages -> sk-dtable; Events -> tone dots + Pills; logs/shell/commands/
gunicorn consoles re-tinted to --bg-code (fixes broken light theme);
settings/git modal polished via SCSS only. Templates: catalog -> tpl-card
grid (icon tile, mono version, clamped desc, tag chips, amber featured
star, hover-reveal Deploy wired to the existing install flow) + cat-chip
category filter; drawer no longer stacks under the install modal. All
tabs, filters, handlers, and deep links kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:45:28 -04:00
Juan DenisandClaude Fable 5 8a61bd67ab feat(ui): restyle the ServerDetail tab bodies to the new look
Header -> demo detail-header (surface gradient + accent wash, mono dotsep
meta, ds Pill status); overview KPI tiles -> spec-card look with thin ds
Gauges (CPU/RAM/Disk retinted to accent-bright/cyan/green convention);
tab strip -> accent underline; data tables + services/packages tables ->
dense sk-dtable spec (scoped fixes for their shadcn-var partials);
docker/cron/cloudflared tabs re-skinned with Pills and mono eyebrows.
Dead blocks removed (kpi-gauge ring, resource-meters, duplicate
server-detail-tabs, notifications-tab). Servers LIST + ops workspace
styles untouched; all tabs, actions, telemetry, and deep links kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:45:16 -04:00
Juan DenisandClaude Fable 5 8f46ac8369 feat(ui): restyle the Monitoring body to the new look
Overview: StatStrip -> MetricCard KPI strip, current metrics -> host-card
grid (icon tile + Pill + thin token-colored Gauges + mono host-mini
footer), active alerts -> alert-row pattern with red-wash firing chips.
Alert Rules / Delivery / History tabs re-skinned in place (Pills, mono
eyebrows, surface tokens); all CRUD, scheduler, and channel config kept.
Synthetic uptime monitors / response-time charts deferred (backend gap,
REDESIGN_MAP S5). Also removes the dead .alert-item block that collided
with _servers.scss.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:45:05 -04:00
Juan DenisandClaude Fable 5 6687c429fa feat(ui): restyle the FTP Server body to the new look
Completes the Files group. StatStrip -> MetricCard KPI strip (status/
active server/users/connections), users + connections tables -> .sk-dtable
with Pills and mono cells, mono eyebrow section headers, surface-token
cards, log viewer on --bg-code. Replaced the broken icon-font <span>s with
lucide icons inside the same buttons. Dead pre-topbar blocks dropped.
All capabilities kept (install flow, user CRUD + password modal,
disconnect, logs, deep-linked tabs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:44:55 -04:00
Juan Denis 819630b02a docs: mark FileManager body restyle done in the redesign map 2026-06-10 23:15:43 -04:00
Juan DenisandClaude Fable 5 f31bc89958 feat(ui): restyle the File Manager body to the flush three-pane console
SCSS-only reskin of the FileManager page to the demo's `.fmgr` look:
- page goes full-bleed flush (no floating cards): toolbar / tree rail /
  listing / status bar joined by 1px soft borders
- left rail: flat groups under mono-uppercase eyebrow headers, demo tree
  rows (accent-bg active, amber folders), dense type filters + disk gauges
- list view: sticky mono-uppercase headers, tighter rows, mono size/
  modified/permissions cells
- grid view: transparent cards with surface-2 hover, accent-bg selection
  (type-tinted thumbs kept)
- status bar + breadcrumbs go mono; bulk bar becomes a flush accent strip
- type palette literals aligned to the redesign tokens (amber/violet/
  green/cyan); context menu + preview drawer re-tinted to surface tokens
- dropped dead blocks from the pre-topbar era (.page-header, .sort-popover,
  .filter-chips-row, .quick-access-*, .analysis-*, .toolbar-select)

All capabilities kept (uploads, bulk ops, type filters, disk mounts,
remote targets, shortcuts) — zero JSX changes. Build verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:08:22 -04:00
Juan DenisandClaude Opus 4.8 b758fda2fd feat(ui): restyle the DNS Zones + SSL bodies to the new look
Finish the Domains trio visually:
- DNS Zones: records table -> dense .sk-dtable; record type rendered as a colored .dns-rtype chip (A/CNAME/MX/TXT/NS/SOA…); content cell uses .sk-cell-mono. Drop unused Badge import.
- SSL Certificates: per-cert status Badge -> Pill (green Valid / amber Expiring). Drop unused Badge import.
Both already themed from Phase 0; these add the redesign's chip/pill language.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:25:09 -04:00
Juan DenisandClaude Opus 4.8 506945c44b feat(ui): restyle the Services list to a KPI strip + dense table
StatStrip -> MetricCard KPI strip (Running/Stopped/Total/Last Deploy with tone icons + subs); per-row status -> Pill; list header -> mono uppercase dense, list card on surface/border-soft tokens. All existing logic kept: search/type/status/sort filters, multi-select + bulk start/stop/restart, repo pill, per-row action buttons, and wordpress/service navigation. Drop now-unused StatStrip/Stat import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:57:55 -04:00
Juan DenisandClaude Opus 4.8 d18d20ce36 feat(ui): restyle the WordPress site list to a dense table
Convert the .wp-site-card grid into a dense .sk-dtable: Site tile + name/port, Environments, Version (mono), Status (Pill green/gray), tag chips, and Open-Site/WP-Admin links. Add a status SegControl (All/Running/Stopped) alongside the existing tag filter; row click still opens the site detail. Omitted SSL/plugins/visits/uptime/server columns (not in the get_sites payload — no list backend) per restyle-first. Drop an unused Badge import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:11:22 -04:00
Juan DenisandClaude Opus 4.8 e2faf9dc6e feat(ui): restyle the Servers list table to the demo's dense look
Visual restyle of the existing .servers-table (no column/capability changes — bulk-select, group, telemetry, and row actions all kept): mono uppercase headers, and thin token-colored telemetry gauges (CPU=accent-bright, RAM=cyan, Disk=green). Region/Load columns omitted (mock-only, no backend field) per restyle-first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:18:28 -04:00
Juan DenisandClaude Opus 4.8 e2968b2197 feat(ui): migrate the WordPress group to the top-bar layout (sidebar now flat)
Final sidebar sub-menu moved to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared WORDPRESS_TABS (WordPress / Pipeline); render PageTopbar on WordPress (sites list) and WordPressProjects (pipeline).
- Remove the wordpress subItems from the sidebar — NO sidebar item has a sub-menu anymore; the rail is fully flat, matching the demo.
- WordPress list: both headers (loading + main) swapped; Import Site + Create Site actions kept.
- Pipeline list header swapped (title 'Pipeline').
- The Pipeline tab is now always visible (was wpInstalled-gated in the sidebar).
Six infra groups total now use the top-bar layout: Domains, Servers, Services, Files, Monitoring, Marketplace, WordPress. Body restyles still pending per group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:07:35 -04:00
Juan DenisandClaude Opus 4.8 7cc27cb82f feat(ui): migrate Monitoring + Marketplace groups to the top-bar layout
Final two sidebar sub-menus moved to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Monitoring group: shared MONITOR_TABS (Monitoring/Status Pages); PageTopbar on both pages (Refresh + Start/Stop + Create-Page actions kept).
- Marketplace group: shared MARKET_TABS (Marketplace/Downloads); PageTopbar replaces the marketplace hero; Downloads header swapped (Import ZIP + Refresh kept).
- Remove the monitoring + marketplace subItems from the sidebar.
Only WordPress (Pipeline) still has a sidebar sub-menu; everything else is now flat + top-bar. Body restyles still pending per group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:35:21 -04:00
Juan DenisandClaude Opus 4.8 faa6355c75 feat(ui): migrate the Files page group to the top-bar layout
Move Files + FTP to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared FILE_TABS (Files / FTP Server); render PageTopbar on both pages.
- Remove the files subItems from the sidebar (route /ftp unchanged, reachable from the tab).
- FileManager: top bar added as the first child of the full-bleed fullscreen browser (already a flex column; body keeps filling) — the toolbar, tree, preview, upload tray and all file ops are untouched.
- FTPServer: .page-header swapped for PageTopbar; the conditional install/test/start/stop/restart actions moved verbatim into the top bar.
- Fix a pre-existing unescaped-apostrophe lint error in FTPServer.
Follow-up: restyle the FileManager + FTP bodies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:26:55 -04:00
Juan DenisandClaude Opus 4.8 c6d44ac502 feat(ui): restyle New Service mode chooser to the demo's method cards
Restyle .new-service-page__mode-card into the demo's centered method-card look (icon chip on top, title, description, corner check on the active card) and tokenize the old teal/indigo active-state literals. GitHub-OAuth repo picker, manifest detection, and the template flow are all preserved. Follow-up: template-catalog grid + Docker-Image/From-Scratch methods.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:14:08 -04:00
Juan DenisandClaude Opus 4.8 4e95c2257f feat(ui): migrate the Services page group to the top-bar layout
Move Services + its sub-pages to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared SERVICE_TABS (Services/New Service/Templates/Deploy Activity); render PageTopbar on all 4 group pages so the tab group persists.
- Remove the services subItems from the sidebar (routes /services/new, /templates, /deployments unchanged, reachable from the tabs).
- Services: header -> PageTopbar (meta shows total/running; New Service action kept).
- NewService: drop the now-redundant breadcrumb; Connections action kept.
- Templates: search box moved into the top-bar actions.
- Deployments: New Service + Live/Paused + Refresh actions kept.
Follow-up: restyle the bodies (NewService method-cards + template catalog, Services dense table, ServiceDetail tabs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 13:07:48 -04:00
Juan DenisandClaude Opus 4.8 8e61ad08d8 feat(ui): migrate the Servers page group to the top-bar layout
Move Servers + its 4 sub-pages to the demo's top-bar structure (REDESIGN_MAP §6 decision 3):
- Add shared SERVER_TABS (Servers/Agent Fleet/Fleet Monitor/Cloud Servers/Config Templates) and render PageTopbar on all 5 pages so the tab group persists.
- Remove the servers subItems from the sidebar (routes unchanged, reachable from the tabs).
- Main Servers page: top bar added ABOVE the full-bleed ops workspace (container -> column); the fleet-health rail, status/group filtering, workbar, bulk actions and pairing are fully preserved — no capability removed.
- Sub-pages (AgentFleet/FleetMonitor/CloudProvision/ServerTemplates): .page-header swapped for PageTopbar, action buttons preserved.
- Fix a pre-existing unescaped-quote lint error in FleetMonitor.
Follow-up: restyle the Servers list body to the demo's dense table + ServerDetail tabs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 04:30:51 -04:00
Juan DenisandClaude Opus 4.8 0eab32096b feat(ui): share the top-bar tab group across Domains/DNS/SSL
Finish the domain page group so the Domains/DNS Zones/SSL tabs persist on all three:
- Extract DOMAIN_TABS to components/domains/domainTabs.jsx; Domains imports it.
- DNSZones + SSLCertificates pages now render the shared <PageTopbar tabs={DOMAIN_TABS}> in place of their old headers (actions preserved).
- Fix a pre-existing unescaped-apostrophe lint error in SSLCertificates.
DNS/SSL page bodies (lists/forms) are not yet restyled — headers only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 04:03:29 -04:00
Juan DenisandClaude Opus 4.8 68695a779c feat(ui): migrate Domains to the demo's top-bar layout
First infra page moved to the demo structure (REDESIGN_MAP §6 decision 3):
- Add shared PageTopbar ds primitive (icon + title + routed sub-nav + actions).
- Domains: top bar with Domains/DNS Zones/SSL tabs (replaces the removed sidebar sub-menu), KPI strip (MetricCard), segmented filter (SegControl), .sk-dtable list, and a detail Drawer with SSL/linked-site/status specs + all actions (visit/verify/SSL/delete). Add-domain + enable-SSL modals preserved.
- Remove the domains subItems from sidebarItems.js (/dns, /ssl routes unchanged, reachable from the top-bar tabs).
- Omit registrar/DNSSEC/nameservers columns (no WHOIS backend yet) per restyle-first.
Follow-up: give DNSZones + SSLCertificates pages the same top bar so the tab group persists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 03:46:46 -04:00
Juan DenisandClaude Opus 4.8 94877ffeca docs: add redesign change map
Per-view change map for the UI redesign: thesis, modularity contract, Phase 0 foundation, shared primitives, per-page deltas, backend gap register, decisions (incl. infra pages adopt the demo's top-bar layout, not sidebar sub-menus), and phased sequence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 03:35:41 -04:00
Juan DenisandClaude Opus 4.8 0b9ee6c61e feat(ui): Phase 1 page restyles (dashboard, settings, databases, docker, marketplace)
- Dashboard: metric tiles -> MetricCard, apps status -> Pill; retint MetricsGraph to the new palette while keeping the recharts chart intact.
- Settings: nav group labels -> mono eyebrow, toggles -> gradient-on, theme-preview swatches repainted.
- Databases: engine status-dot glow, green result count, UTF-8 + Connected status-bar segments.
- Docker: MetricCard KPI strip atop the content area, status filter -> segmented control (kept the rich dx- rail/tabs/inspector).
- Marketplace: mono eyebrows, token card shadow, accent category chips (kept tabs + install-from-source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 03:35:04 -04:00
Juan DenisandClaude Opus 4.8 e8b5299415 refactor(styles): tokenize hardcoded colors to design tokens
Replace accidental old-palette literals (old indigo/green/amber/zinc surfaces+borders, white/black washes) with design tokens so they theme correctly in light mode and track a custom accent. Intentional literals (brand colors, categorical log/node/file-type palettes, theme-preview mockups, white-on-color, scrims) were preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 03:34:49 -04:00
Juan DenisandClaude Opus 4.8 326bc89679 feat(ui): redesign foundation — tokens, self-hosted fonts, primitives, sidebar
Phase 0 of the ServerKit UI redesign (see docs/REDESIGN_MAP.md):
- Remap design tokens to the new infra-console palette behind existing names; add prototype token names (surface ramp, cyan/violet, accent-bright/-bg). Dark/light/system.
- Self-host IBM Plex (woff2 vendored via scripts/fetch-fonts.mjs); no Google Fonts CDN.
- Runtime-derive the full accent ramp in ThemeContext.
- Add shared design-system primitives in components/ds (Pill, EnvTag, SegControl, MetricCard, Gauge, ServiceTile, Sparkline, AreaChart, ScoreGauge, Feed, Drawer).
- Restyle the sidebar (gradient rail, mono section headers, left accent bar) and switch the brand to the demo's gradient-tile logo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 03:34:28 -04:00
github-actions[bot] 9e8513c121 chore: bump version to 1.6.10 [skip ci] 2026-06-07 03:30:10 +00:00
Juan Denis 986909a955 Surface Docker DBs, add base port & migration fixes
Expose and surface containerised databases and introduce managed app base port control. Backend: add endpoint to list databases across all Docker apps, robustly resolve compose container names, include docker DB user when querying, compute migration needs from live heads and self-heal orphaned alembic_version entries by stamping to head, and add the managed_app_base_port system setting. Template service now honours the managed_app_base_port and WordPress template default HTTP port updated to 8300. Frontend: add react-icons brand icons and DatabaseBrands component, show Docker-hosted DBs under their engine nodes with a docker badge, platform-aware console shortcut label, pass DB user to docker table/query API calls, add API client header merge fix, UI for managing base port in settings, enhanced migration history UI (backup/apply/resync flows, confirm dialog, toasts), and various style tweaks and color additions.
2026-06-06 23:29:22 -04:00
github-actions[bot] 3f2fcac205 chore: bump version to 1.6.9 [skip ci] 2026-06-06 03:32:36 +00:00
Juan DenisandClaude Opus 4.8 6f70eb9ad1 feat(workspaces): make python.py grant-aware — every app blueprint now covered (#33)
Re-reading python.py showed every mutating endpoint is independently
@admin_required (runs before the shared get_app_or_404 gate), so that gate only
governs the 4 read GETs (packages/env/status/gunicorn-config). Pointing it at
can_access_app safely opens those to grantees while the admin-only mutations stay
untouched — no per-method split needed.

With this, every app-related blueprint is grant-aware. Also fixes the int-vs-str
identity comparison in the helper.

Verified: a python read-gate test (stranger 403, grantee passes), full suite
132 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:08:44 -04:00
Juan DenisandClaude Opus 4.8 35642187c6 feat(workspaces): extend per-site ACL grants across all app blueprints (#33)
A grant now reaches every app-related blueprint, not just apps.py + WordPress, so
a shared application is fully manageable by its grantees.

- Promoted the access helpers to a shared seam: ResourceGrantService.can_access_app
  (owner/admin/any grant -> reads) and can_edit_app (owner/admin/editor grant ->
  mutations); apps.py now delegates to them.
- Swept 33 ownership checks across 6 blueprints (categorized read vs mutate via a
  parallel mapping workflow): builds.py (12), domains.py (7, incl. SSL + domain
  CRUD), deploy.py (6), private_urls.py (5), templates.py (2), databases.py (1).
  Reads -> any grant; mutates -> editor grant.
- Fixed the same latent int-vs-str ownership bug in all 33 (now user.id).

Deferred: python.py's single get_app_or_404 helper gates read+mutate+delete
together and needs per-method splitting before it's safe to make grant-aware.

Verified: cross-blueprint integration test (grantee reaches builds deployments;
stranger 403), full suite 131 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:04:06 -04:00
Juan DenisandClaude Opus 4.8 2d83440040 feat(workspaces): viewer/editor grant roles + operate-endpoint access (#33)
Completes the chosen access-level ACL design: a grant now lets the user actually
operate the resource, gated by a real role.

- Grants carry a role: editor (view + operate) or viewer (read-only). grant POST
  validates role in {viewer, editor} (default editor); ResourceGrantService gains
  grant_role(); the Sharing UI gains a role selector.
- Two access helpers split the app endpoints: _can_access_app (owner/admin/ANY
  grant) gates reads (get_app, /linked, /logs, /container-logs, /containers,
  /status); _can_edit_app (owner/admin/EDITOR grant) gates mutations (/link,
  /unlink, /environment, PUT, /start, /stop, /restart).
- DELETE stays owner-or-admin (an editor can't delete — tested).
- The same sweep fixed a latent int-vs-str ownership bug across ~14 app endpoints
  (compared app.user_id to the stringified get_jwt_identity(); now user.id).

Verified: 3 more ACL tests (viewer read-only + can't operate, editor operates +
can't delete, role validation/default), full suite 130 passed, lint + build.
Still owner-only: env-vars/databases/domains blueprints (follow-up sweep).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:13:18 -04:00
Juan DenisandClaude Opus 4.8 4dca43b6b0 feat(workspaces): sharing UI for per-site ACL grants (#33)
Completes the per-site ACL feature with UI on top of the backend (d9a5ec7).

- A per-app 'Share' action in the Workspaces Resources modal opens an inline
  sub-view: current grantees with Revoke, plus a grant-access list of users
  (the owner and existing grantees are filtered out).
- Client methods getAppGrants / grantAppAccess / revokeAppAccess.

Verified: frontend lint-clean (no new warnings) + production build. The grant
endpoints are covered by backend tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 12:03:38 -04:00
Juan DenisandClaude Opus 4.8 d9a5ec7e92 feat(workspaces): per-resource access grants — per-site ACL backend (#33)
An owner/admin can share a specific application (transitively its WordPress
site / DB / domains) with a specific user who doesn't own it. Access-level for
now; per-grant view/edit roles are reserved via a role column.

- New ResourceGrant model (Alembic 016, unique per user x resource) +
  ResourceGrantService (grant/revoke/list/user_has_grant/granted_ids).
- Enforcement at the cleanest seams: scope_query gained grant_resource_type so
  granted apps appear in the apps list (own OR granted, then workspace-narrowed);
  get_app and the WordPress _owner_or_admin_app helper became grant-aware, so one
  change opens every WP per-site route to a grantee.
- Grant-management API (GET/POST/DELETE /apps/<id>/grants, owner-or-admin; can't
  grant to the owner; revoke is resource-scoped so you can't revoke another app's
  grant by id). Uses user.id (int), not the stringified get_jwt_identity().

Verified: 4 new ACL tests (visibility, get_app, WP route access, management perms
+ idempotency + revoke), full suite 127 passed, end-to-end alembic upgrade to 016.

Deferred: a Sharing UI (next), and broadening grant-access to the generic app
operate endpoints (start/stop/update stay owner-or-admin; delete stays owner-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:59:34 -04:00
Juan DenisandClaude Opus 4.8 35c7a73d8c feat(workspaces): manage servers in the workspace Resources modal (#33)
Completes the reassignment feature symmetrically — the server reassign endpoint
shipped earlier (e89415f) was API-only; now it has UI.

- getServers gains an allWorkspaces bypass (empty X-Workspace-Id header) and a
  setServerWorkspace client method (mirrors apps).
- The per-workspace modal is now a 'Resources' modal with both Applications and
  Servers sections, each with move-in / move-out.

Verified: frontend lint-clean (no new warnings) + production build. The server
endpoint itself is already covered by backend tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:18:46 -04:00
Juan DenisandClaude Opus 4.8 8ea5f50408 feat(workspaces): scope the WordPress sites hub to the active workspace (#33)
Closes the inconsistency where the workspace switcher filtered apps/servers but
not the WordPress hub.

- WP sites carry no workspace_id of their own, so get_sites(workspace_id=...)
  scopes join-based through the site's parent Application.workspace_id; list_sites
  resolves the active workspace and passes it.
- The hub is global today, so with no workspace context behavior is unchanged
  (additive, narrow-only).
- No frontend change: the WP hub already fetches through the header-injecting
  api.request, so it auto-scopes on the ambient X-Workspace-Id.

Verified: a new API test (global vs workspace-scoped), full suite 123 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:15:15 -04:00
Juan DenisandClaude Opus 4.8 e89415f3e0 feat(workspaces): reassign apps/servers between workspaces (#33)
Completes the organizing loop — existing resources were stuck wherever the
backfill put them (Default); now they can be moved into client workspaces.

- PUT /apps/<id>/workspace (owner-or-admin on the app) and
  PUT /servers/<id>/workspace (developer+), both requiring the TARGET be a
  workspace the caller can access (member or admin). A null target moves the
  resource back to Default.
- UI: an 'Apps' management modal on each Workspaces card (mirrors the Members
  modal) with move-in / move-out. It fetches with allWorkspaces (an empty
  X-Workspace-Id header) so it can see apps in other workspaces to move them in,
  sidestepping the ambient scope.
- Fixed a real bug caught by tests: the app ownership check compared app.user_id
  to the stringified get_jwt_identity() (int != str -> spurious 403); now uses
  user.id.

Verified: 2 new endpoint tests (ownership + target-membership + null->Default +
role gates), full suite 122 passed, frontend lint-clean + production build.
Servers reassignment is API-only for now; the modal is apps-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:32:30 -04:00
Juan DenisandClaude Opus 4.8 567d6059f7 feat(workspaces): workspace-scoped brand color recolors the panel (#33)
Selecting a client workspace now recolors the whole panel to that client's brand
— the visible payoff of the workspace effort.

- A workspace's primary_color (already on the model) is now editable via a color
  picker added to the Workspaces create modal.
- WorkspaceSwitcher mirrors the active workspace's color into a workspace_accent
  localStorage key; ThemeContext applies it via the existing applyAccentToDOM with
  precedence over the user's personal accent (workspace_accent || accentColor),
  recoloring the logo gradient, buttons, and highlights.
- Deterministic (no effect-ordering races): switching reloads and the value is
  read once on mount. Cleared on logout and on a stale selection; reverts to the
  user accent on 'All workspaces'. Default color is the app accent, so a workspace
  is unbranded unless a custom color is chosen.

Verified: frontend lint-clean (no new warnings) + production build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:19:57 -04:00
Juan DenisandClaude Opus 4.8 ddff69020b feat(workspaces): active-workspace selector + ambient X-Workspace-Id header (#33)
Makes the workspace scoping foundation user-visible — the research flagged the missing 'current workspace' context as the core gap #33 had to close.

- WorkspaceSwitcher (sidebar): self-contained selector that reads/writes an
  active_workspace_id in localStorage, fetched from the user's workspaces. Hides
  for single-workspace installs and drops a stale selection on load.
- services/api/client.js: sends the active workspace ambiently as X-Workspace-Id
  on every request (endpoints that don't honor it ignore it); cleared on logout.
- Switching reloads so all lists re-fetch under the new scope.

Backend resolve_workspace_id is lenient (prior commit) so a stale header can
never break the apps/servers lists. Verified: frontend lint-clean + production
build; the header->scoping path is covered by the backend API tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:54:01 -04:00
Juan DenisandClaude Opus 4.8 45aeb507e4 refactor(workspaces): lenient workspace resolution for safe header-driven context (#33)
A stale or forbidden X-Workspace-Id (deleted workspace, removed member) now degrades to no-scope instead of 403/404, so an ambient workspace-context header can never break the apps/servers lists. Safe by construction: falling back to no-scope only ever shows the user their own resources and stamps the default workspace on create — it never grants access to, or creates in, a foreign workspace. Also drops the error-tuple from the four call sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:49:44 -04:00
Juan DenisandClaude Opus 4.8 299a004cdf feat(workspaces): workspace_id scoping foundation + safe backfill (#33)
The cross-cutting backbone the rest of the #33 agency-scale milestone needs,
landed as a contained, narrow-only foundation rather than a 36-route rewrite.

- Nullable workspace_id FK on applications + servers (Alembic 015 off 014) with
  an idempotent raw-SQL backfill: create a Default workspace, attach every
  workspace-less row, and make every existing user a member so nobody loses
  visibility once scoping is activated. Also creates the workspace_id indexes
  that upgraded installs would otherwise lack.
- Centralized scope helper in WorkspaceService: ensure_default_workspace,
  resolve_workspace_id (validates a requested workspace; membership-checked with
  admin bypass; rejects deactivated accounts on the workspace path), and
  scope_query.
- Opt-in + narrow-only: a request filters by workspace only when it carries an
  X-Workspace-Id header / ?workspace_id=. scope_query INTERSECTS the workspace
  filter with the existing per-user ownership filter, so it can only narrow
  visibility, never broaden it — with no context the behavior is byte-identical
  to before (admin->all, owner->own, servers global). This closes the footgun
  where the backfill would otherwise let a non-admin enumerate all resources via
  the Default workspace.
- Wired into apps.py + servers.py (list + create): replaces the duplicated
  ownership branch and stamps new resources with the active-or-default workspace.

Deferred to follow-on slices: broad route adoption, per-site ACL, the
workspace<->User role reconciliation, server-side branding, and a frontend
workspace selector.

Verified: 10 new pytest tests (scope-narrowing, a can't-broaden-access
regression, deactivated-user gate, raw-SQL backfill against real SQLite +
idempotency), full suite 120 passed, a real end-to-end alembic upgrade to 015 on
a fresh DB (columns + indexes), and a 2-lens adversarial review (3 findings, all
fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 03:36:24 -04:00
Juan Denis c652b369d4 Update .gitignore 2026-06-01 03:05:11 -04:00
Juan DenisandClaude Opus 4.8 1926416f27 feat(wordpress): monthly client reports — persisted per-month rollups (#33)
The self-contained reports slice of the #33 agency-scale milestone, built
without the high-blast-radius workspace_id/ACL/branding rework.

- WpReportsService aggregates one calendar month per site: uptime % + a daily
  series + incidents (re-aggregated from #26 HealthCheck samples for the exact
  month), update runs (#29), DatabaseSnapshot backups, and current vulnerability
  posture (#28) + health/disk — each labelled persisted-history vs point-in-time.
- Persisted (WordPressReport, Alembic 014 off 013_ai_assistant) + synchronous
  upsert per (site, YYYY-MM), so point-in-time sources (vulns, health) are
  captured as-of generation while true-historical sources are re-queried per
  month. Client grouping derives from the client-* tag (#20) — no workspace_id
  FK. Model lives in wordpress_site.py + lazy-imported in the service so the
  concurrent AI agent's models/__init__.py is untouched.
- Routes GET /reports, POST /reports/generate (admin), DELETE /reports/<id>;
  client getReports/generateReport/deleteReport; a Reports tab with a recharts
  daily-uptime chart, summary cards, Print->Save-as-PDF (scoped @media print,
  no PDF dep) and JSON export.
- 9 pytest tests; 3-lens adversarial review (4 of 5 findings fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 03:04:03 -04:00
Juan DenisandClaude Opus 4.8 5ad7a4bc3f refactor(ui): consolidate per-page styles into shared cards
Reworks Git, Services, Firewall, FTP, Deployments, Monitoring, and
Marketplace pages onto the shared StatCard/card system and removes their
now-redundant page-specific SCSS (net large reduction in duplicated
styles).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:50:35 -04:00
Juan DenisandClaude Opus 4.8 53f440abb0 refactor(ui): security page + overview adopt shared cards
Moves Security page and its Overview tab onto the shared StatCard/card
styles and trims the now-redundant per-page SCSS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:50:23 -04:00
Juan DenisandClaude Opus 4.8 5338e2930a refactor(ui): expand shared StatCard + card system
Grows the shared card stylesheet and StatCard component so page-level
surfaces can drop their bespoke card SCSS and reuse one vocabulary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:50:16 -04:00
Juan DenisandClaude Opus 4.8 919300498e feat(databases): rebuild Databases page as a full-screen explorer
Replaces the five-tab Databases page with a DataGrip-style, full-bleed
Database Explorer built on the file-manager shell:
- unified source tree for all engines with inline + right-click actions
  (open console, back up, drop, query, copy name)
- tabbed workspace: click a table to open a paginated data-grid tab,
  open SQL consoles per database, and a Backups tab
- toolbar New menu (console / create database / create user, disabled
  when the engine is not installed) and a live status bar
- frontend-only: table browsing runs SELECT ... LIMIT/OFFSET via the
  existing query endpoints; no API changes

Rewrites _databases.scss into the explorer design system (tree, tabs,
results grid, console, context menu, status bar) using existing tokens;
respects prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:41:35 -04:00
Juan DenisandClaude Opus 4.8 caee882844 feat(databases): add Database Explorer building blocks
Reusable pieces behind the new full-screen database UI:
- dbAdapter: unifies MySQL/PostgreSQL/SQLite/Docker behind one
  listTables/getStructure/runQuery interface, with per-dialect identifier
  quoting and a LIMIT/OFFSET browse-query builder
- SourceTree: lazy, status-aware tree (engine -> database -> tables)
- ConsoleTab: SQL console (Ctrl+Enter, read-only/write toggle,
  per-connection history, CSV export)
- TableDataTab: paginated data grid + Structure view
- ResultsGrid: shared sortable result table (sticky header, NULL handling)
- BackupsTab: backups list with engine filter
- modals: create database / user dialogs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:41:24 -04:00
Juan DenisandClaude Opus 4.8 9cab065dc5 feat(ai): add core AI assistant primitive powered by Prompture
AI is a core ServerKit primitive (not a plugin): an Intercom-style bubble + slide-in drawer on every dashboard page, with a tools+page-context assistant mode and a plain simple chat mode, SSE streaming, conversation persistence, and guarded write actions (the model proposes; the user approves).

Plugins EXTEND the assistant rather than rebuilding it: backend via app.plugins_sdk.ai (reg.tool + register_context_provider, discovered from a plugin.json ai block with install/enable/disable hooks); frontend via useServerkitAI() and a contributions.ai category (suggested_prompts, tool_renderers). The marketplace gains an ai category for discovery.

Backend: ai_service (Prompture Conversation + ProviderEnvironment + guardrails), ai_tool_registry singleton, ai_tools_builtin (core.* tools), the /api/v1/ai blueprint (SSE via a worker-thread + queue bridge, single-gevent-worker safe; in-thread confirmation gate), AiConversation/AiMessage/AiPendingAction models + migration 013, encrypted ai_* settings. Frontend: AIContext + components/ai/*, AI settings tab. Powered by Prompture (installed editable; requirements pinned).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:39:05 -04:00
Juan DenisandClaude Opus 4.8 98ffb5096e docs(wordpress): assess + defer the XL/infra-blocked tail (#32/#33/#34)
Document why the final three roadmap tasks are deferred rather than shipped
rushed/blind, each with the concrete Docker-correct path + the specific blocker:

- #32 per-site SFTP: Docker-correct design is a per-site SFTP sidecar (atmoz/sftp
  mounting wordpress_html, injected like #23 redis), but it exposes a
  security-sensitive, unverifiable-from-dev file-access surface + a port/container
  per site. Deferred for a real-host pass.
- #33 agency scale: XL epic (workspace_id FKs + per-site ACL + RBAC rework +
  workspace branding + monthly reports) — cross-cutting, high-blast-radius;
  split into its own milestone. The report data sources (#26/#28/#29) now exist.
- #34 horizontal scaling: XL AND blocked on the same missing per-site
  reverse-proxy/upstream layer as #21, plus a stateless-WP refactor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:30:23 -04:00
Juan DenisandClaude Opus 4.8 6adc6e626c feat(wordpress): emit WP lifecycle events (#35)
Add WordPress lifecycle events to EVENT_CATALOG (wordpress.created / deleted /
backup_completed / updated / update_rolled_back / deployed / deploy_failed,
joining #27's site_down/up) + a reusable EventService.emit_wp(event_type, site,
**extra) helper (best-effort; never breaks the WP op).

Emitted at concrete service points: create_site (wordpress.created), the #29
safe-update (wordpress.updated / update_rolled_back), and git deploy
(wordpress.deployed / deploy_failed + the pre-deploy backup_completed).

The catalog drives the subscription UI and EventSubscription.matches_event
supports a wordpress.* wildcard, so these are immediately subscribable as
outbound webhooks (HMAC + retry) and fire event-triggered workflows via the
existing WorkflowEventBus — the trigger half of the Done-when.

Deferred: WordPress workflow ACTION nodes (needs the workflow node-type
registry + builder UI) and the serverkit wp CLI binary. Verified: py_compile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 02:27:52 -04:00