These four locales had translation files but were never wired into i18n
resources or the nezhaLang dropdown, so users could not select them.
Register ja-JP, ro-RO, uk-UA, gl-ES; missing keys fall back to en-US per
the Weblate community-translation workflow.
These four locales had translation files but were never wired into i18n
resources or the nezhaLang dropdown, so users could not select them.
Register ja-JP, ro-RO, uk-UA, gl-ES; missing keys fall back to en-US per
the Weblate community-translation workflow.
Mirror the server HideForGuest flag on services: rename the
enable_show_in_service field to hide_for_guest across the form, schema
and generated types, reuse the existing HideForGuest label, and drop the
now-orphaned ShowInService i18n keys. Services are now visible to guests
by default and hidden only when the box is checked, matching servers.
Mirror the server HideForGuest flag on services: rename the
enable_show_in_service field to hide_for_guest across the form, schema
and generated types, reuse the existing HideForGuest label, and drop the
now-orphaned ShowInService i18n keys. Services are now visible to guests
by default and hidden only when the box is checked, matching servers.
Surface the new nezha:inventory:{read,delete,*} scopes that gate listing
and deleting servers/server-groups, separate from nezha:server:* which now
covers per-server runtime operations. Refine server-scope descriptions to
match the backend split.
Surface the new nezha:inventory:{read,delete,*} scopes that gate listing
and deleting servers/server-groups, separate from nezha:server:* which now
covers per-server runtime operations. Refine server-scope descriptions to
match the backend split.
Two CI-only failures surfaced against a fresh backend DB:
- The revoke test read after.data.find(), but the list endpoint omits data
entirely when the admin has zero tokens, throwing on undefined. Default to [].
- The password-change test's restore POST hit a 403: changing the password
triggers a refresh-token that re-mints the nz-csrf cookie, so the X-CSRF-Token
read just before the request can be stale. A failed restore left the admin on
the rotated password and cascaded into cron/fm/visibility login failures.
Add csrfRequest(), which retries once on 403 after re-reading the cookie, and
use it for both profile mutations.
Verified 8/8 passing across repeated fresh-DB CI-mode runs.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Two CI-only failures surfaced against a fresh backend DB:
- The revoke test read after.data.find(), but the list endpoint omits data
entirely when the admin has zero tokens, throwing on undefined. Default to [].
- The password-change test's restore POST hit a 403: changing the password
triggers a refresh-token that re-mints the nz-csrf cookie, so the X-CSRF-Token
read just before the request can be stale. A failed restore left the admin on
the rotated password and cascaded into cron/fm/visibility login failures.
Add csrfRequest(), which retries once on 403 after re-reading the cookie, and
use it for both profile mutations.
Verified 8/8 passing across repeated fresh-DB CI-mode runs.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The backend CSRF double-submit gate rejects unsafe methods unless
X-CSRF-Token mirrors the signed nz-csrf cookie. page.request bypasses the
SPA JS that does this, so every mutating E2E call got 403, failing the suite.
- Add csrfHeaders(page) helper that mirrors the nz-csrf cookie into the
header, polling until the cookie is readable to avoid the post-login race.
- Apply it to all cookie-authenticated POST/PATCH/DELETE calls (the /mcp
Bearer calls stay header-free since PAT requests are CSRF-exempt).
- loginAs waits for the nz-csrf cookie before returning.
- Fix the create-token dialog submit selector: the button is labelled
'Create API token' (t('CreateApiToken')), not 'Create'.
Verified 8/8 passing across repeated CI-mode runs against a real backend.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The backend CSRF double-submit gate rejects unsafe methods unless
X-CSRF-Token mirrors the signed nz-csrf cookie. page.request bypasses the
SPA JS that does this, so every mutating E2E call got 403, failing the suite.
- Add csrfHeaders(page) helper that mirrors the nz-csrf cookie into the
header, polling until the cookie is readable to avoid the post-login race.
- Apply it to all cookie-authenticated POST/PATCH/DELETE calls (the /mcp
Bearer calls stay header-free since PAT requests are CSRF-exempt).
- loginAs waits for the nz-csrf cookie before returning.
- Fix the create-token dialog submit selector: the button is labelled
'Create API token' (t('CreateApiToken')), not 'Create'.
Verified 8/8 passing across repeated CI-mode runs against a real backend.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
- Add safeExternalHref(): only render http(s) URLs as clickable hrefs,
blocking attacker-controlled template metadata from becoming
javascript:/data: links.
- Refine CSRF header attachment and auth-loading state in the fetcher,
api-tokens client, useAuth, and settings route.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
- Add safeExternalHref(): only render http(s) URLs as clickable hrefs,
blocking attacker-controlled template metadata from becoming
javascript:/data: links.
- Refine CSRF header attachment and auth-loading state in the fetcher,
api-tokens client, useAuth, and settings route.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Add the reserved_hosts setting (input + i18n across all locales) so operators
behind a reverse proxy can declare public dashboard hostnames members must not
register as NAT domains (GHSA-x6fg-52vr-hj4w).
Show the backend error message on NAT create/update failure instead of a
generic "unexpected error", so a reserved-host rejection is explained.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Add the reserved_hosts setting (input + i18n across all locales) so operators
behind a reverse proxy can declare public dashboard hostnames members must not
register as NAT domains (GHSA-x6fg-52vr-hj4w).
Show the backend error message on NAT create/update failure instead of a
generic "unexpected error", so a reserved-host rejection is explained.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Add an API tokens management route to create, list, and revoke PATs, showing
the plaintext token once on creation with scope and server-id selection.
Mirror the nz-csrf cookie into the X-CSRF-Token header on unsafe fetcher
methods (POST/PUT/PATCH/DELETE) for the server-side double-submit check, and
self-heal expired sessions via refresh-token without a recursive fetch loop.
Gate protected routes behind resolved auth state to avoid pre-auth SWR fetches,
and fix the login loading/race so stale probes cannot clobber the session.
Add i18n keys for the new screens across all locales.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Add an API tokens management route to create, list, and revoke PATs, showing
the plaintext token once on creation with scope and server-id selection.
Mirror the nz-csrf cookie into the X-CSRF-Token header on unsafe fetcher
methods (POST/PUT/PATCH/DELETE) for the server-side double-submit check, and
self-heal expired sessions via refresh-token without a recursive fetch loop.
Gate protected routes behind resolved auth state to avoid pre-auth SWR fetches,
and fix the login loading/race so stale probes cannot clobber the session.
Add i18n keys for the new screens across all locales.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The GitHub runner can spend more than the readiness window compiling go run ./cmd/dashboard, leaving dashboard.log empty while the backend has not started yet. Build the dashboard binary during preparation and health-check only the actual server startup. Keep README instructions aligned with CI.
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The GitHub runner can spend more than the readiness window compiling go run ./cmd/dashboard, leaving dashboard.log empty while the backend has not started yet. Build the dashboard binary during preparation and health-check only the actual server startup. Keep README instructions aligned with CI.
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The dashboard embed pattern ignores hidden-only dist directories, so the CI backend stub must create visible index.html files. Keep the README in sync and exclude Playwright specs from Vitest so npm run test remains a unit-test command.
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
The dashboard embed pattern ignores hidden-only dist directories, so the CI backend stub must create visible index.html files. Keep the README in sync and exclude Playwright specs from Vitest so npm run test remains a unit-test command.
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Boot a fresh dashboard from nezhahq/nezha master inside the runner:
- Checkout admin-frontend and the backend repo into two paths.
- Generate the two embed stubs and swag docs so cmd/dashboard builds.
- Inject NZ_JWTSECRETKEY (env-first config story) and start the
backend on 127.0.0.1:8008 with a tmp SQLite db.
- Wait on /api/v1/setting before letting Playwright launch.
- Playwright config brings up Vite dev which proxies /api to 8008.
- Upload playwright-report and dashboard.log on failure for triage.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Boot a fresh dashboard from nezhahq/nezha master inside the runner:
- Checkout admin-frontend and the backend repo into two paths.
- Generate the two embed stubs and swag docs so cmd/dashboard builds.
- Inject NZ_JWTSECRETKEY (env-first config story) and start the
backend on 127.0.0.1:8008 with a tmp SQLite db.
- Wait on /api/v1/setting before letting Playwright launch.
- Playwright config brings up Vite dev which proxies /api to 8008.
- Upload playwright-report and dashboard.log on failure for triage.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Covers the security fixes that landed across both repos:
- auth.spec.ts: login persists nz-jwt cookie and getProfile succeeds;
password change bumps TokenVersion + revokes the old cookie so the
pre-change JWT can no longer auth (regression guard for the
keyId+session backend rewrite).
- cron-csrf.spec.ts: POST /api/v1/cron/:id/manual succeeds while GET
is no longer routable (regression guard for the cron CSRF fix).
- fm-csrf.spec.ts: POST /api/v1/file is reachable while GET is no
longer routable (regression guard for the FM CSRF fix).
- visibility.spec.ts: an anonymous caller cannot see a server-group
that contains zero guest-visible servers (regression guard for the
server-group leak fix).
Fixtures wrap the noisy login + cleanup boilerplate. tsconfig is
scoped to tests/e2e so the suite stays out of the production tsc
project graph.
Playwright config starts the Vite dev server (npm run dev) and
expects a backend reachable at the URL Vite proxies to. CI workflow
follow-up commit wires the backend up.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Covers the security fixes that landed across both repos:
- auth.spec.ts: login persists nz-jwt cookie and getProfile succeeds;
password change bumps TokenVersion + revokes the old cookie so the
pre-change JWT can no longer auth (regression guard for the
keyId+session backend rewrite).
- cron-csrf.spec.ts: POST /api/v1/cron/:id/manual succeeds while GET
is no longer routable (regression guard for the cron CSRF fix).
- fm-csrf.spec.ts: POST /api/v1/file is reachable while GET is no
longer routable (regression guard for the FM CSRF fix).
- visibility.spec.ts: an anonymous caller cannot see a server-group
that contains zero guest-visible servers (regression guard for the
server-group leak fix).
Fixtures wrap the noisy login + cleanup boilerplate. tsconfig is
scoped to tests/e2e so the suite stays out of the production tsc
project graph.
Playwright config starts the Vite dev server (npm run dev) and
expects a backend reachable at the URL Vite proxies to. CI workflow
follow-up commit wires the backend up.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Backend moves /api/v1/file from GET to POST so SameSite=Lax cookies
can no longer expose this state-changing endpoint to cross-site
CSRF. Update createFM() accordingly.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Backend moves /api/v1/file from GET to POST so SameSite=Lax cookies
can no longer expose this state-changing endpoint to cross-site
CSRF. Update createFM() accordingly.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Backend GHSA-8qhj-4f8c-j8qg moves /api/v1/cron/:id/manual from GET to
POST so SameSite=Lax cookies no longer expose this state-changing
endpoint to cross-site CSRF. Update runCron() accordingly.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
Backend GHSA-8qhj-4f8c-j8qg moves /api/v1/cron/:id/manual from GET to
POST so SameSite=Lax cookies no longer expose this state-changing
endpoint to cross-site CSRF. Update runCron() accordingly.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>