Commit Graph
397 Commits
Author SHA1 Message Date
Juan DenisandClaude Opus 4.8 5836294c4d feat(cloudflare): R2 / KV / D1 developer platform (Phase 6)
Add a Storage tab listing and managing Cloudflare's account-scoped storage
primitives — R2 buckets, KV namespaces, and D1 databases. Management only
(list/create/delete); the object/key/row data planes are out of scope.

- CloudflareClient: r2/buckets, storage/kv/namespaces, d1/database list+create
  +delete.
- CloudflareService: combined list_storage (each product fetched independently so
  a missing token scope degrades to a per-product error, not a dead tab),
  validated create/delete for each. R2 bucket names validated S3-style.
- /api/v1/cloudflare/zones/:id/storage* routes (read any user; writes admin).
- StoragePanel (three generic create/list/delete sections; R2 notes its
  S3-compatibility for future backup targeting); api methods; SCSS.

No new tables — wiring an R2 bucket as an S3 backup backend (minting scoped R2
keys) is a deliberate follow-up, noted in the UI.

Tests: aggregate listing + per-product error degradation, R2 name validation,
create/delete, KV/D1 required-field guards (6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:39:12 -04:00
Juan DenisandClaude Opus 4.8 2f1999018d feat(cloudflare): Cloudflare Tunnels (Phase 5)
Add a Tunnels tab for creating and managing Cloudflare Tunnels (cloudflared) —
exposing a local/private service through Cloudflare's edge with no public IP.
Clearly distinct from ServerKit's WireGuard remote-access tunnels (always
labelled "Cloudflare Tunnel" in the UI). Account-scoped; account resolved from
the zone.

- CloudflareTunnel model: records the connector token (encrypted at rest) and
  owning connection per (account, tunnel_id).
- CloudflareClient: cfd_tunnel create (config_src=cloudflare) / list
  (is_deleted=false) / delete / token / connections / configurations get+put.
- CloudflareService: create (returns + stores the one-time token, yields the
  cloudflared install command), list (managed-flagged), delete, fetch install,
  and public-hostname routing — sets the ingress config (always re-appends the
  required http_status:404 catch-all) and best-effort upserts the proxied CNAME
  hostname→<id>.cfargotunnel.com via the shared ownership-guarded DNS path.
- /api/v1/cloudflare/zones/:id/tunnels* routes (read any user; writes admin).
- TunnelsPanel (create form + one-time install box w/ copy, tunnel list with
  per-tunnel install reveal and public-hostname add/remove); api methods; SCSS.

Tests: list is_deleted param, create config_src, name required, token stored
encrypted + install string, managed-flagging, delete prunes local row, ingress
built with catch-all last (7).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:34:32 -04:00
Juan DenisandClaude Opus 4.8 02774f741e feat(cloudflare): Workers edge hosting (Phase 4)
Add a Workers tab to the Cloudflare Zone Settings page for deploying edge
scripts and managing Worker routes. Workers are account-scoped, so the owning
account is resolved from the zone — the whole feature reuses the existing
Cloudflare DNS connection (no new credential).

- CloudflareWorker model: records the source ServerKit uploaded per (account,
  name) so a worker can be tracked and re-deployed (created via create_all).
- CloudflareClient: zone→account lookup, list/delete scripts, and a stable
  multipart PUT /scripts/{name} module upload (the still-beta resource-oriented
  Workers API is deliberately avoided per the roadmap's fallback note), plus
  zone Worker routes list/add/delete.
- CloudflareService: list (live scripts flagged when ServerKit-managed + routes),
  deploy (validated lowercase name, records source, optional route attach),
  delete, route add/delete.
- /api/v1/cloudflare/zones/:id/workers* routes (read any user; writes admin).
- WorkersPanel component (deploy form with a starter script, worker list, routes
  list); api methods; SCSS.

Tests: multipart upload shape, name validation, deploy records source, list
managed-flagging, delete prunes the local row (5).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:27:36 -04:00
Juan DenisandClaude Opus 4.8 5ec5be4b31 feat(cloudflare): WAF custom rules (Phase 3)
Add a WAF tab to the Cloudflare Zone Settings page for managing custom firewall
rules (the http_request_firewall_custom phase entry-point ruleset).

- CloudflareClient: ruleset list/get + phase-entrypoint create (PUT) + per-rule
  POST/PATCH/DELETE.
- CloudflareService: list rules (empty when no custom ruleset yet, distinguished
  from an auth error by listing rulesets first), add rule (creates the ruleset on
  first use), update/delete, and three one-click presets — lock WordPress admin
  to an IP, block common exploit paths, challenge suspicious bots. The IP in the
  lock preset is validated with ipaddress so it can't inject expression syntax;
  rule actions are constrained to a safe terminal subset.
- /api/v1/cloudflare/zones/:id/waf/* routes (list any user; add/preset/update/
  delete admin).
- WafPanel component (preset picker + custom-rule form + active-rules list with
  enable toggle and delete); api methods; SCSS.

Tests: ruleset discovery, empty vs auth-error, create-on-first-rule vs append,
action validation, preset IP validation + safe expression, update/delete (10).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:20:26 -04:00
Juan DenisandClaude Opus 4.8 6bc0ac36e5 feat(cloudflare): cache purge (Phase 2)
Add Cloudflare cache management to the Zone Settings page. Purge everything (with
a confirm) or purge specific URLs (one per line, capped at 30 to match Free/Pro
limits); hosts/prefixes/tags are accepted by the API for Enterprise plans and
their plan errors are surfaced verbatim.

- CloudflareClient.purge_cache(zone_id, payload).
- CloudflareService.purge_cache: validates intent (raises on empty), caps files
  at 30, normalizes the payload.
- POST /api/v1/cloudflare/zones/:id/purge-cache (admin).
- Actions tab gains a "Purge cache" card (purge-everything button + URL list);
  api method; SCSS.

Tests: client payload, purge-everything, 30-file cap, empty-raises, provider
error surfaced (5 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:12:20 -04:00
Juan DenisandClaude Opus 4.8 125b1e4553 feat(cloudflare): zone settings page (Phase 1)
Add a Cloudflare Zone Settings surface on top of the existing Cloudflare DNS
connection — SSL/TLS, Speed, Caching and Security toggles plus a one-click
"recommended hardening" preset. Reuses the connected DNSProviderConfig
credential and shared CloudflareClient, so auth/encryption-at-rest are not
re-implemented.

Backend:
- CloudflareClient: generic v4 request() helper + zone-settings get/update
  (envelope normalized to {success, error?, result?}).
- CloudflareService: resolve zone+credential by ServerKit DNS zone id, curated
  setting groups (UI metadata), get/update setting, apply recommended preset
  with per-setting reporting.
- /api/v1/cloudflare blueprint: settings GET / GET-one / PATCH + apply-preset
  (reads any user, writes admin; mutations captured by the audit fallback).

Frontend:
- CloudflareZoneSettings page (tabbed SSL/TLS · Speed · Caching · Security ·
  Actions) with declarative toggle/select/HSTS controls and optimistic save.
- Route /cloudflare/zones/:zoneId, "Cloudflare" link from Cloudflare-managed
  DNS zones, api module, SCSS.

Tests: client envelope/zone-settings + service resolution guards, indexing,
and preset reporting (11 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:08:38 -04:00
Juan DenisandClaude Opus 4.8 7b9ae4f934 feat(backups): protection panel UI + global cost view
Shared ProtectionPanel (status, schedule, history) rendered for WordPress (Backups tab + Settings) and Services (Settings → Backups), plus a calendar view, detail/restore drawers, and storage-cost columns/settings/KPIs on the global Backups page.

- components/backups: ProtectionPanel, ProtectionStatusCard, ScheduleCard, BackupHistoryList, BackupCalendar, BackupDetailDrawer, RestoreDrawer, format helpers
- api/backupProtection: generic policy/runs methods over targetType
- WordPressDetail + service-detail SettingsTab render the shared panel
- Backups page: cost rates settings, Site/Cost columns, cost KPI cards

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:53:34 -04:00
Juan DenisandClaude Opus 4.8 9de7cc65db feat(notifications): route config notices into the notification center; top banner = urgent only
The "common misconfiguration" notices stacked every hint as a banner at the top of
the dashboard while the notification bell sat empty. Now:

- SystemNotices (top banner) shows only urgent notices (error/critical, or an
  explicit urgent flag), at most one — the dashboard stays clear.
- The live notices fold into the notification center (bell + /notifications) via the
  shared NotificationsContext: they show with their Fix action, bump the unread
  badge, and can be dismissed. Dismissal syncs across both surfaces (shared
  localStorage).

New util utils/dismissedNotices.js (shared dismiss state + notice->item mapper).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:40:12 -04:00
Juan DenisandClaude Opus 4.8 a03bd0c6dc feat(dns): wildcard vs per-site subdomain DNS toggle in Site Settings (Phase 3 #9 UI)
Surfaces sites_dns_mode in the Managed Sites Domain & HTTPS card: a select to choose
Wildcard (point *.base once, every site instant) or Per-site (each new site gets its
own auto-created, ownership-tracked A record). Reads dns_mode from the sites-https
status and saves via updateSystemSetting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:00:50 -04:00
Juan DenisandClaude Opus 4.8 da404bd8cf fix(toast): accept a sonner options object, not just a numeric duration
ToastContext wrapped the 2nd arg as { duration }, so the ~30 call sites that pass a
full sonner options object (toast.info(msg, { duration: 4000 })) got it
double-wrapped ({ duration: { duration } }) and their custom durations were silently
dropped. Normalize at the wrapper: a number becomes { duration }, an object passes
through. Backward-compatible with the few numeric callers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:00:50 -04:00
Juan Denis edf6260dee fix(build-release): make release builder work on Windows/Git Bash
- Replace rsync with tar-based copy to avoid missing rsync on Windows/Git Bash
- Exclude backend/venv, backend/.venv, backend/.venv-wsl, and broken WSL symlinks
- Exclude ./backups, backend/dev-data/backups, backend/instance/backups, and scripts/test/output
- Exclude scratch images (PNG/JPEG/JPG), logs, and tmp files from the tarball
- Pack with tar --transform so /opt/serverkit prefix is created without cross-device moves
- Reduce release tarball from 13 GB back to ~6 MB
2026-06-22 16:52:34 -04:00
Juan DenisandClaude Opus 4.8 37707e7b5d feat(dns): "Changes to your Cloudflare" UI — activity feed + zone mirror (Phase 2 frontend)
#7 Surface the Phase 2 backend:
- DnsActivity: a per-connection "Recent changes" feed (action, record, result
  badge, error) from GET /dns/changes, expandable on each DNS connection in
  Settings -> Connections.
- DNS Zones page: a "ServerKit records / In your provider" toggle; the provider
  view (GET /dns/<id>/mirror) lists every live record badged ServerKit vs External,
  external records read-only/muted ("ServerKit never modifies external records").
- api: getDnsChanges() + getZoneMirror().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:40:26 -04:00
Juan DenisandClaude Opus 4.8 1127243d1d feat(dns): unify Cloudflare credentials behind one connection + shared client
Phase 0 of the DNS roadmap.

#2 Shared Cloudflare client (app/services/dns/): DnsCredential + DnsRecordSpec +
CloudflareClient. Both DNSProviderService and DNSZoneService delegate to it, so
auth (scoped token / global key), the CAA structured-data wire format, MX/SRV
priority parsing, and idempotent upsert live in one place. Fixes dup-on-recreate,
silent update/delete no-op, and missing proxied/priority on the provider path.

#1 Single credential source: new FK dns_zones.dns_provider_config_id (migration
025) + DNSZoneService._resolve_credential (linked -> auto-discover-by-domain ->
legacy inline-token). create_zone accepts a connection id; link_legacy_zones()
converges inline tokens onto encrypted connections at boot. The /dns create-zone
modal now picks a connection instead of pasting a token. DDNS rides it for free.

Tests: test_dns_cloudflare_client.py, test_dns_credential_resolution.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:12:31 -04:00
Juan Denis 0f4e5821da feat: add dismissible system notices for common misconfigurations
- Add GET /api/v1/system/notices endpoint (admin-only)
- Detect missing canonical domain, sites base domain, server public IP, and wildcard HTTPS
- Add SystemNotices React component with dismiss persistence in localStorage
- Render notices inside DashboardLayout so they appear on every page
- Add scoped SCSS for warning/info/error notice banners
2026-06-22 14:42:09 -04:00
Juan Denis 652d903ae7 Normalize emails, add panel_origin & domain-aware URLs
Make email handling case-insensitive (queries + model normalization) to prevent duplicate accounts. Add SiteDomainService.panel_origin() helper and use it across services to build public URLs (Gitea, WordPress, Roundcube) and Django ALLOWED_HOSTS. Add optional custom-domain support when creating WordPress sites and propagate domain to Roundcube install. Improve environment domain fallback to prefer panel/base domains over .localhost. Small Dockerfile healthcheck tweak (use 127.0.0.1). Update install/update/build scripts to always (re)create virtualenv locally for release installs. Remove unused frontend CreateSiteModal and expose domain field in WordPress page.
2026-06-22 14:37:44 -04:00
Juan Denis 0e29978e7b Truncate large telemetry payloads & UI robustness fixes
Telemetry: ensure sanitized payloads bigger than the 32KiB threshold are replaced with a {_truncated: True} marker before storage, and add a unit test covering that behavior.

Backend API: adjust imports in secrets_webhooks to use WorkspaceService and include User model.

Frontend: make Sidebar resilient to malformed active_workspace JSON in localStorage (try/catch), and ensure socketService.disconnect() is called when user becomes unauthenticated in NotificationsProvider to clear notifications and unread count.

Files: backend/services/telemetry_service.py, backend/tests/test_telemetry.py, backend/app/api/secrets_webhooks.py, frontend/src/components/Sidebar.jsx, frontend/src/contexts/NotificationsContext.jsx
2026-06-22 01:45:15 -04:00
Juan Denis 10ca0b3970 Refactor sidebar, workspace list, and Git settings UI
Rename service settings 'repository' → 'git' (routes, labels, and links) and update App route comment. Reorder sidebar footer so user mini and notification bell share a single row and add accessible user-mini button. Adjust notification panel positioning, add show animation, and responsive layout tweaks. Replace Workspaces card grid with a dense table view: add search, status segment control, bulk select checkboxes, and selection state. Minor SCSS tweaks for sidebar button sizing and WordPress action width. These changes improve discoverability and bulk management UX.
2026-06-22 01:35:36 -04:00
Juan Denis bb0bd7ba1d Workspace and queue bus improvements
- Add workspace detail routes, tabs, and page title handling
- Add workspace switcher select component and updated workspace styles
- Add queue detail page and protect system queue groups from REST mutations
- Update sidebar layout styles and queue operations UI
2026-06-22 01:16:00 -04:00
Juan Denis 9b7f69e654 Normalize service detail page with WordPress layout and add Domain & SSL
- Rename service Environment tab to Env Vars and settings Environment to Environment Type
- Add Domain & SSL section to service settings with attach domain + Let's Encrypt flow
- Surface primary domain from domains array in service subtitle
- Add prominent repo connection pill to WordPress detail header
- Apply WordPress list layout to Services page (drop KPIs, tabs+search toolbar)
2026-06-22 01:15:53 -04:00
Juan DenisandClaude Opus 4.8 70f8697586 feat(jobs): admin Jobs UI page over the unified job API (Phase 9)
Add /jobs — the single pane to observe and manage all background work, built
on the /api/v1/jobs API. Mirrors the DeliveryLog/Telemetry admin pages.

- Jobs table polls every 5s: status Pills (succeeded/running/pending/failed/
  cancelled), kind, owner, attempts, age. Status/kind filters. Per-row Cancel
  (pending/running) and Retry (failed/cancelled) with toast feedback. Row
  expands to a detail view (payload/result/error/correlation/timing) via getJob.
- Scheduled-jobs table: name, kind, schedule (cron or "every Nh"), next/last run,
  with Run-now and Pause/Enable.
- Stat cards (total/running/pending/succeeded/failed) from /jobs/stats.
- Wiring: services/api/jobs.js module bound into ApiService; route + page title
  in App.jsx; "Jobs" item in the sidebar "system" group; _jobs.scss imported
  in main.scss. Admin-gated client-side via useAuth().isAdmin.

Lint clean (0 errors) and production build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:28:41 -04:00
Juan Denis 3d3975fa3a feat(telemetry): unified system event stream with correlation
Add SystemEvent model, TelemetryService, /api/v1 telemetry endpoints, and
a /telemetry UI page. Instrument audit, notification, webhook, backup,
deployment, and monitoring paths. Add correlation_id to Notification,
EventDelivery, and DeploymentJob so related events can be grouped.

- 18 backend tests added and passing
- Frontend production build clean
2026-06-21 20:19:57 -04:00
Juan DenisandClaude Opus 4.8 a33b2f7f38 feat(frontend): notification center, delivery log, email providers
- NotificationsContext + header bell/dropdown (sidebar + mobile top bar)
  with a live unread badge over Socket.IO and a /notifications history page.
- Admin /admin/notifications delivery log (stat cards, status/channel
  filters, retry) and an email-provider management UI (add/test/
  set-default/delete) for SMTP/SendGrid/Postmark/SES/Mailgun.
- api/notifications.js methods, socket 'notification' forwarding, a timeAgo
  util, and themed sk-notif* / sk-dlog / sk-eprov styles.

Builds clean (vite) and lints clean (eslint exit 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:57:18 -04:00
Juan Denis cbf64dfbaa feat(workspaces): enforce server quotas and add nav permissions UI
- Enforce max_servers quota on server creation and workspace moves
- Add my_role/my_effective_role to single workspace GET response
- Add Navigation Permissions editor to Workspace Settings
- Store full active workspace object for nav/brand settings
2026-06-21 18:44:08 -04:00
Juan Denis 45f754abc0 feat(workspaces): improve workspace switching and secrets/webhooks UI
- Update workspace API and service for workspace context handling
- Refine Sidebar and WorkspaceSwitcher components
- Update sidebar navigation items for workspace-aware routing
- Enhance SecretsWebhooks page and API client integration
2026-06-21 18:32:25 -04:00
Juan DenisandClaude Opus 4.8 a5455c4655 feat(frontend): lean default sidebar + split Settings into account/admin
Sidebar — leaner default + setup wiring:
- Add a "Recommended" preset (Full minus Marketplace, Queue Bus, and
  Secrets & Webhooks) and make it the default for unconfigured users, so
  fresh installs aren't maximalist. The three demoted items stay fully
  routable and one click away via the "Full" view or Customize Sidebar.
- Factor the three into a shared ADVANCED_ITEM_IDS list so every curated
  preset hides them consistently.
- Wire the Setup wizard's use-case answer to an initial preset
  (presetForUseCases): a single WordPress intent -> Web Hosting, a single
  DevOps intent -> Docker/DevOps, anything mixed/general -> Recommended.

Settings — split the 17-item rail:
- Add a two-way "My Account" / "Admin" segmented switch (admin-only) so
  personal preferences aren't interleaved with server-wide system
  controls. The active group is derived from the tab, so deep links
  (e.g. /settings/users) open the right group. About moves into My
  Account; the dev/local-dev tools consolidate under Admin.

Verified: eslint (0 errors) and vite build both pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 03:11:12 -04:00
Juan Denis b15d12e1af Add per-server Remote Access tab and UI tweaks
Expose Remote Access as a per-server tab and adapt the fleet page for server-scoped views. RemoteAccess now accepts serverId, filters/labels tunnels involving that server, renders inline action button when embedded, disables private-server select for scoped views, and links nodes to per-server Remote Access pages from fleet view. Added tab to ServerDetail when wireguard is supported and updated styles for responsive intro, node links, and a “this server” tag. Small refactors: useMemo/useCallback optimizations and cleaner topbar action handling.
2026-06-21 02:47:36 -04:00
Juan Denis 4dc71d5774 fix(frontend): hide single-target picker, clean up terminal routing and errors
- TargetPicker now renders a static label when only one target option is available,
  removing the pointless/disabled dropdown on single-server setups.
- Change Terminal tab route from /terminal/terminal to /terminal/shell and add
  a redirect for the old URL.
- Terminal shell tab only lists agents advertising terminal capability, preventing
  'Agent not connected' errors on local/WSL dev installs.
- Add per-server creation guard in RemoteTerminal to avoid duplicate session
  attempts on remounts or rapid server switches.
2026-06-21 02:25:31 -04:00
Juan Denis 32e6def0e6 Add ClamAV start action and UI posture fixes
Add a POST /security/clamav/start endpoint and SecurityService.start_clamav() to enable+start the ClamAV daemon (tries clamav-daemon and clamd). Add unit tests that mock ServiceControl to verify success/failure and auth. Wire a startClamAV API call into the frontend and extend the Security Overview: richer posture UI, one-click fixes (install, start service, init integrity, update defs), KPIs, icons, and accessibility improvements. Update related styles and file-manager minor styles. This improves operator workflows by allowing a one-click "Start service" posture fix and provides immediate UI feedback when fixes run.
2026-06-21 01:54:51 -04:00
Juan Denis c4e8235746 style(lint): fix pre-existing ESLint errors across frontend 2026-06-21 01:32:01 -04:00
Juan Denis 58b6b831c1 refactor: migrate remaining tables/forms and clean Terminal inline styles 2026-06-21 01:31:57 -04:00
Juan Denis e9083da189 refactor: migrate pages to shared primitives and add lint guardrails
- Standardize toast usage in AgentFleet and FleetMonitor (toast.success/error/info)
- Replace inline full-page spinners with PageLoader in CloudProvision, DNSZones, DynamicDns, Marketplace, ServerTemplates, StatusPages
- Migrate Servers and Domains tables to DataTable
- Migrate DynamicDns create form to FormField
- Adopt SearchFilterBar in Applications
- Adopt CopyButton in DynamicDns token callout
- Replace raw buttons with Button in Servers, Terminal, Docker
- Move Domains error-banner close button inline styles to SCSS class
- New ErrorState, SearchFilterBar, CopyButton utility components
- Add ESLint rules warning on inline styles, raw <button>, and legacy .card divs
2026-06-21 01:10:35 -04:00
Juan Denis 7f50e741cd feat: add shared primitives and extract reusable overflow logic
- New useOverflowItems hook for tab overflow menus
- Refactor ui/tabs and ds/PageTopbar to share overflow logic
- New PageLoader component for full-page loading states
- New DataTable component with sorting, empty/loading states, custom rows
- New ResourceCard / ResourceList for servers/apps/domains/containers
- New FormField / FormRow wrappers for consistent forms
- New IconButton wrapper for accessible icon-only actions
- Add supporting styles in _design-system.scss and _forms.scss
2026-06-21 01:10:23 -04:00
Juan Denis b96626f96f cleanup: remove orphaned components and unused shadcn primitives
- Delete unused app components: QueryRunner, AuditLogTab, Toast, UptimeGraph, BaseNode
- Delete unused shadcn primitives: accordion, avatar, progress, scroll-area, separator, tooltip
- Normalize AuthContext.jsx line endings to LF
- Remove no-op badgeVariants() shim from ui/badge
2026-06-21 01:10:12 -04:00
Juan DenisandClaude Opus 4.8 1c519a24a2 feat(services): URL-route detail tabs and adopt the shared repo-connect form
Bring the Service detail page to parity with the WordPress detail page:

- Routes: add services/:id/:tab and services/:id/:tab/:section so the active
  tab and the Settings sub-section live in the URL (shareable, survive refresh).
  ServiceDetail drives its tab via useTabParam; SettingsTab reads/writes the
  section via useParams + navigate.
- Repository: the Settings -> Repository section now renders the shared
  RepoConnectForm (the same component the WordPress Git settings use) instead
  of the bespoke GitConnectModal — provider picker (GitHub/GitLab/Bitbucket/
  Gitea) with URL fallback and a connected summary + Disconnect. Existing
  deploy scripts are preserved on connect. The top 'connect a repository' pill
  now navigates to .../settings/repository, and GitConnectModal is removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:42:03 -04:00
Juan DenisandClaude Opus 4.8 429e6ef344 fix(settings): make settings sub-nav items fill the column width
A flex <button> keeps its shrink-to-fit intrinsic width, so each settings
nav item's box was sized to its label (e.g. the active 'Git' box only as wide
as the word) instead of the 200px column. Add width:100% (box-sizing is
border-box) so every item's box is the same full width, text still left-aligned.
Applies to both the WordPress (.wp-settings__navitem) and Service
(.svc-settings__navitem) detail settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:27:26 -04:00
Juan DenisandClaude Opus 4.8 03504c6517 feat(connections): GitLab/Gitea/Bitbucket repo pickers + path selector
Adds source-connection support for browsing repositories on GitLab, Gitea, and
Bitbucket (new *RepoPicker components) plus a PathSelector, wired through
RepoConnectForm / GitProviders / ConnectionsHub / providerCatalog and the
sourceConnections + files APIs, backed by additions to the source_connections
API and service.

Bundled from in-progress work in a concurrent session at the user's request.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:20:10 -04:00
Juan DenisandClaude Opus 4.8 e46c0d685e redesign(services): WordPress-style grouped Settings sub-nav
Restructure the service Settings tab from a flat stack into a grouped left
sub-nav + content pane (.svc-settings grid), mirroring the WordPress detail
page: General -> Environment, Connections -> Repository, Advanced -> Danger
Zone, each with an icon. Section is local state; the existing panels are
unchanged, only reorganized.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:19:20 -04:00
Juan DenisandClaude Opus 4.8 6fb0454ac6 fix(services): center the Events tab empty-state icon
The empty-state used text-align:center, but svgs are display:block (Tailwind
preflight) so the clock glyph left-aligned while the text centered. Flex-center
the container so the icon and text share the same axis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:17:30 -04:00
Juan DenisandClaude Opus 4.8 a69d67afcb feat(services): add tab icons and pin Settings to the far right
Give each Service detail tab a lucide glyph (TAB_ICONS) and pin the Settings
tab to the right of the strip via app-detail-tab--end, matching the WordPress
detail tab bar. The strip wraps (flex-wrap + overflow-x:visible) so the
right-pinned Settings tab can never get pushed off-screen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:09:16 -04:00
Juan DenisandClaude Opus 4.8 1079c3b98d redesign(services): migrate Service detail to the sk-topbar layout
Reuse the WordPress detail page's shell so /services/:id matches /wordpress/🆔
a full-bleed PageTopbar (.sk-topbar) with the breadcrumb in the title slot and
the Deploy/Restart/more actions on the right, above a centered .app-detail-body
holding a flat .app-detail-header (ServiceTile + name + status Pill + type
badge + mono subtitle), the repo-connection pill, the versions panel, the
shared .app-detail-tabs underline strip, and .app-detail-content.

Drops the bespoke .svc-detail__{breadcrumb,header,title-row,tabs,content}
chrome in favour of the shared app-detail-* base classes, and runs the page
full-bleed via .main-content:has(.svc-detail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:08:34 -04:00
Juan Denis f249fd49f0 Add topbar tab groups with overflow + backups/security tabs
Replace per-page Tabs with a shared PageTopbar tab group that supports overflow and a "More" popover. Adds BACKUP_TABS and SECURITY_TABS components and updates Backups, Security and Git pages to render sections via the topbar tabs (useTabParam routing). Implements responsive/overflow measurement logic in PageTopbar (TopbarTabs) and related styles; removes old page-local tab styles and shadcn Tabs usage. Improves header UX so active tab stays visible when space is limited.
2026-06-20 23:32:27 -04:00
Juan Denis 5e54a691f5 redesign(git): migrate Git page to Domains-style layout
- Rewrite Git.jsx with KPI strip, listhead + SegControl tabs, resource
  tables/cards, and detail drawers matching the Domains page.
- Apply Domains layout styles via .domains-page and add git-specific atoms
  for repo detail tabs, mini tab switcher, and quick-action rows.
- Preserve all existing logic: install, repo browser, webhooks, deployments,
  rollback/redeploy, start/stop/uninstall.
2026-06-20 23:17:23 -04:00
Juan Denis c045bcc0a9 feat(queue-operations): Servers-style layout and explicit send-message flow
- Redesign QueueOperations to match the Servers page layout:
  left overview/status rail, main workbar + command bar + queue table
- Replace tabbed UI with a single queues table filtered by group/status/search
- Add per-queue Send and Messages actions; send is now a clear modal
  pre-filled with the destination group and queue
- Fix confusing send flow that made it look like messages couldn't be sent
- Message detail and create group/queue modals preserved
- Frontend production build passes; backend tests pass
2026-06-20 22:54:08 -04:00
Juan Denis 440a215b5e feat(queue-bus): auto-generate slugs and resolve duplicates
- Add global app.utils.slug helper (slugify + unique_slug)
- create_group/create_queue now accept name without slug
- Duplicate slugs get sequential suffixes (test -> test-1)
- Update frontend modals to remove slug fields
- Adjust tests for auto-unique behavior
2026-06-20 22:38:38 -04:00
Juan DenisandClaude Opus 4.8 3f50db7a34 feat(git): make the provider chips real, selectable buttons
The repo-connect provider strip was a static "explainer" — clicking GitLab/
Bitbucket/Gitea did nothing, and the GitHub picker showed regardless, which
read as confusing. Now (when a host form passes onSelect) the chips are radio
buttons that drive the connect method: GitHub shows the one-click picker;
every other provider shows the paste-a-URL field with that host's placeholder
(gitlab.com / bitbucket.org / gitea / scp-style ssh). A recognized URL still
auto-selects its provider. GIT_PROVIDERS gained `oauth` + `placeholder`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:37:56 -04:00
Juan Denis 942342fac2 refactor(queue-bus): clarify Operations UI layout
- Overview: group cards are fully clickable and navigate to Messages
- Overview: add 'system' badge for system-owned groups
- Groups: add per-row 'Create Queue' action and clearer 'View Queues' label
- Messages: remove out-of-place 'Create Queue' button; keep Send Message only
- Create Queue modal: include group selector
2026-06-20 22:31:17 -04:00
Juan Denis 9b4618b24b feat(queue-bus): introduce ServerKit Queue Bus under Operations
- Add pluggable queue bus with SQLAlchemy backend (Redis-ready interface)
- Models: QueueGroup, Queue, QueueMessage with retries, visibility timeout,
  delayed delivery, priority ordering, and dead-letter handling
- REST API at /api/v1/queue for groups, queues, messages, and stats
- Plugin SDK: from app.plugins_sdk import queue
- Operations UI page with group/queue/message management and send-message form
- Migrate webhook deliveries from raw threading.Thread to the queue bus
- Add webhook consumer with retry/dead-letter support
- Migration 022_queue_bus and backend tests
2026-06-20 22:25:06 -04:00
Juan DenisandClaude Opus 4.8 fb96db72d4 feat(git): one-click "Connect with GitHub" repo picker
Add a shared GithubRepoPicker that lists the user's repositories from the
GitHub source-connection OAuth that already powers /services/new (no new
backend), and render it above the paste-a-URL fields in RepoConnectForm.
Picking a repo fills the clone URL + default branch, so linking a repo to a
WordPress site — and any future RepoConnectForm surface — becomes "authorize
once, pick a repo" instead of pasting a URL. Falls back gracefully to the URL
form when GitHub isn't configured/connected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:22:29 -04:00
Juan Denis f7e51cc570 Allow committed screenshots and fix ServiceDetail effect
Update .gitignore to allow committed screenshot assets under docs/screenshots/ (these are generated mock-data README images and should be tracked). Fix a bug in frontend/src/pages/ServiceDetail.jsx: avoid referencing isUpload from outer scope (temporal dead zone) by deriving it inside the useEffect, and replace the effect dependency with service?.source so the effect runs correctly when the service source changes. This prevents a render-time crash and ensures versions are loaded for uploaded services.
2026-06-20 22:01:27 -04:00
Juan DenisandClaude Opus 4.8 505ff0f2cd feat(git): shared RepoConnectForm + clickable commits; center icon tiles
- Extract the repo-connect form into components/git/RepoConnectForm.jsx
  (provider strip + URL/branch/auto-deploy + optional tracked paths +
  connected state) — the canonical reusable surface. components/wordpress/
  GitConnectForm.jsx is now a thin WordPress-flavored wrapper over it, so
  WordPress renders identically and other pages can drop the shared form in.
- Make the WordPress commit log clickable: each SHA links to the commit on
  the remote (GitHub/GitLab /commit, Bitbucket /commits; https + scp-ssh).
- Fix off-center icon tiles on /services/new (grid + place-items + block svg
  instead of inline-flex).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:58:28 -04:00