nexxo
f06f00c428
chore(config): documented env placeholders + SSH keys from file paths
...
- .env.example gains a full, commented CluPilot operations block (Stripe, Hetzner
DNS, Traefik, WireGuard hub, SSH identity, monitoring, prod mail) with a note
that everything is optional for local dev
- config/provisioning.php: CLUPILOT_SSH_{PUBLIC,PRIVATE}_KEY_PATH read the key
from a file (a multi-line PEM cannot live in .env), falling back to inline vars
- documents that Uptime Kuma's REST API is read-only (monitor CRUD is Socket.IO),
so it needs a bridge rather than being drop-in for the generic REST client
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:37:25 +02:00
nexxo
32fa1028cc
fix(portal): cloud status labels for all instance states; scale storage curve into the quota
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:16:16 +02:00
nexxo
d05932105d
feat(portal): bind the cloud card to the real instance (plan/seats/domain/quota) so the maintenance badge matches it
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:14:42 +02:00
nexxo
ac3b3a4e11
fix(portal): scope the per-instance maintenance badge to that instance's host
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:12:33 +02:00
nexxo
a321d963d7
fix(portal): no silent no-ops without a customer; maintenance visible per instance; maintenance form redesign
...
- ResolvesCustomer trait replaces 5 duplicated customer() lookups; portal actions
now SAY why nothing happened (operator accounts have no Customer) instead of
returning silently — the actual cause of 'dead buttons', '0/0 seats' and
'settings not saving' when signed in as admin
- portal layout: explicit notice for a login without a linked customer
- /cloud: per-instance maintenance badge + window details; seat note 'owner = seat 1'
- maintenance form: sectioned card, hints, placeholder, styled datetime fields,
grouped host picker with per-datacenter select-all + selection counter
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:10:44 +02:00
nexxo
0b2d762b88
fix(auth): scope registration throttle to its own route (per-IP), not all Fortify endpoints
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:41:30 +02:00
nexxo
35a86c413c
fix(auth): throttle Fortify endpoints (registration had no limiter — signup-spam guard)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:38:44 +02:00
nexxo
406f753311
fix(admin): remove is_admin self-heal — RBAC is the only console boundary (no revocation bypass)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:35:14 +02:00
nexxo
22cbe9ac39
fix(auth/admin): atomic user+customer signup; Rule::in for country validation (comma-safe)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:33:09 +02:00
nexxo
6ca1e3fba5
fix(auth): create + link a Customer on public signup so the portal works pre-purchase
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:31:08 +02:00
nexxo
39a1f708a1
fix(auth): reject signup with an existing customer email (account-claim guard)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:29:35 +02:00
nexxo
507ab485a0
fix(admin): derive legacy datacenter location allowlist from DB, not client property
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:27:41 +02:00
nexxo
3a794b05dd
fix(admin): promote only role-less legacy admins; allow editing datacenters with legacy free-form location
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:26:09 +02:00
nexxo
fd7cba7403
fix(admin): authorize datacenter modal mounts; stale threshold uses step maxDuration
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:23:44 +02:00
nexxo
2b73a895c9
fix(admin): don't flag scheduled-backoff runs as stale; edit-datacenter country validation from config
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:21:22 +02:00
nexxo
8304f2e7dc
fix(admin): self-heal legacy is_admin into Owner (no RBAC bypass); validate datacenter country server-side
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:19:24 +02:00
nexxo
88957f8f57
feat(auth): enterprise split login + signup (Fortify registration) via frontend-design
...
- login/register redesigned as a split brand-panel + clean form (Apple-ish
enterprise look), cross-linked; brand panel uses accent gradient + tokens
- Fortify registration enabled; /register full-page Livewire + RegisterTest
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:16:27 +02:00
nexxo
be99f413f7
feat(admin): provisioning liveness — per-run progress bar, last-activity, stale warning
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:11:17 +02:00
nexxo
b50f97568f
fix(admin): no admin lockout (is_admin fallback); centered modal width; datacenter edit modal + country dropdown
...
- isOperator()/EnsureAdmin/broadcast fall back to is_admin so a legacy admin is
never locked out by a stale permission cache
- published modal: centered max-w-lg card (dynamic modalWidth class was purged
by Tailwind → full-width)
- datacenter edit moved to a modal (no row-height jump); location is now a
country dropdown (config/countries.php) instead of free text
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:09:17 +02:00
nexxo
87a81f5f40
fix(admin): read-only send-time guard (never drop mail on retry); document exactly-once residual for real-mail outbox
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:40:44 +02:00
nexxo
e09965f918
fix(admin): in-flight claim (claimed_at) for exactly-once maintenance send; scope permission rollback
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:38:44 +02:00
nexxo
28221549d1
fix(admin): defer maintenance sent_at to real delivery; read-only send-time dedup (no lost mail)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:35:52 +02:00
nexxo
0cc5be5479
fix(admin): atomic send-time delivery claim so backlog duplicates ship exactly once
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:34:02 +02:00
nexxo
5b85806605
fix(admin): catch racing ledger insert and re-fetch (idempotent under concurrency)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:31:43 +02:00
nexxo
72731112f4
fix(admin): atomically claim maintenance notification retries under a row lock
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:30:17 +02:00
nexxo
8bb93137a3
refactor(admin): extract MaintenanceNotifier; atomic provisioning retry; catch-up cancellation for race-delivered announcements
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:28:02 +02:00
nexxo
ae1f78f534
fix(admin): shown-once temp password for invited staff; suppress announcements for cancelled windows at send time
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:23:21 +02:00
nexxo
718e8568c1
fix(admin): require host on any maintenance save; cancel-notify only delivered announcements
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:19:44 +02:00
nexxo
62b4d85ef7
fix(admin): release notification claim on dispatch failure so retries aren't blocked
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:17:44 +02:00
nexxo
ab5ba2b169
fix(admin): dedup pending maintenance notifications (updated_at claim + staleness threshold)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:16:15 +02:00
nexxo
493b81aadf
fix(admin): confirm maintenance delivery via MessageSent (retryable until sent); guard resend/cancel on derived state; fix flaky non-unique host wg_ip
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:14:01 +02:00
nexxo
52fa7a34a2
feat(admin): email affected customers when maintenance is cancelled (ledger-guarded)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:07:21 +02:00
nexxo
6593dae946
fix(admin): retry path for missed maintenance emails; lock Owner role to serialize last-owner guard
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:04:31 +02:00
nexxo
bc2e95ba18
fix(admin): block role escalation of non-staff; validate maintenance host ids atomically; rollback ledger on dispatch failure
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:01:45 +02:00
nexxo
e5c74c6bdd
fix(admin): reject customer email on operator account update; do not claim mail delivery on dispatch
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:58:24 +02:00
nexxo
d68d8e1f25
fix(rbac/mail): migrate legacy is_admin users to Owner in-migration; send maintenance mail in customer locale
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:56:22 +02:00
nexxo
eff8c08258
feat(admin): maintenance windows — schedule once, assign many hosts, notify
...
- maintenance_windows + host pivot + notification ledger; derived state (never
stored); affected-customer + banner queries live off instances
- admin /maintenance: create draft/publish, multi-host select, impact counts,
cancel; capability-gated (maintenance.manage)
- publish emails affected customers once (queued Mailable, ledger-idempotent)
- customer portal maintenance banner (upcoming <=72h + active) merged per window
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:53:50 +02:00
nexxo
c5d60340b7
feat(admin): staff RBAC (spatie) + admin settings page
...
- 5 operator roles (Owner/Admin/Support/Billing/Read-only) seeded via migration
with a capability catalogue; app checks capabilities via Gate, never role names
- every mutating admin action authorizes server-side (hosts/datacenters/customers/
impersonate/provisioning); is_admin reads migrated to console.view / isOperator()
- admin /settings: own account + Owner-only staff invite/role/revoke with
last-owner, self-role and customer-collision guards (transactional)
- sidebar 'zum Kundenportal' link replaced with Settings
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:47:42 +02:00
nexxo
4336c3bb3f
feat(brand): CluPilot logo mark + SVG favicon across layouts
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:31:33 +02:00
nexxo
08f61b43f5
fix(ui): modal above sidebar (z-index); datacenter edit buttons stay right; toast slides from bottom; provisioning retry action
...
- modal wrapper z-10 -> z-[70] (was behind the z-40 sidebar); toasts slide up/down
- datacenter inline-edit colspan fix so save/cancel stay right-aligned
- admin provisioning: retry a failed run (table + panel), mirrors host-detail retry
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:29:37 +02:00
nexxo
99081284a4
fix(portal): delete old logo only after the branding update commits
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:04:27 +02:00
nexxo
f6efa4f200
fix(portal): allow closure after failed provisioning; serialize last-owner guard
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:02:37 +02:00
nexxo
8bcb5ec268
fix(admin): enforce hosts.datacenter FK (restrictOnDelete) — no deactivation side-effect
...
The DB now refuses to orphan a host; datacenter delete pre-checks for a friendly
message and catches the constraint as the race backstop.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:00:38 +02:00
nexxo
249efa0553
fix: serialize datacenter delete (lock+deactivate+recheck); store logo before deleting old
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:58:07 +02:00
nexxo
01383e7e5e
fix(portal): race-safe owner-seat initialization (firstOrCreate + catch)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:55:09 +02:00
nexxo
3bf0dfb548
fix: seat limit + suspend guard per Codex; harden factory email uniqueness
...
- seat limit follows the active/cancelling package, not a newer inactive record
- suspend/reactivate toggle refuses closed accounts (terminal); closed shown in list
- factory emails use a large unique numeric space (safeEmail pool could collide
late in a full suite run)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:53:15 +02:00
nexxo
cbbb523f9c
fix(portal): cancel the active instance explicitly, not just the newest record
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:48:57 +02:00
nexxo
c011a11b48
fix(portal): atomic seat-limit (row lock) + billing-day-anchored cancellation date
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:47:04 +02:00
nexxo
074b0c041b
fix(portal): enforce customer lifecycle per Codex review
...
- EnsureCustomerActive middleware: suspended/closed customers lose portal
access (admins + active impersonation exempt)
- cancellation: reject non-active instances; service-end anchored on the
subscription start date, not calendar month-end
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:45:03 +02:00
nexxo
3b288486cd
feat(portal): seats management — invite/role/revoke against plan limit
...
- seats table + model; Users page manages team members (owner auto-created)
- invite respects plan seat allowance + dedupe; role change + revoke guarded
so the last owner can never be removed or demoted
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:40:18 +02:00