Repository navigation
build(dashboard): vendor cozystack-ui into the monorepo - #2963
Conversation
|
Important Review skippedToo many files! This PR contains 199 files, which is 49 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (199)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request consolidates the Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request integrates the Cozystack dashboard UI source code directly into the repository under packages/system/dashboard/images/console and updates the Makefile to build from this in-tree context. The review feedback identifies several critical issues: missing enabled gates on useK8sList queries in DynamicOptionsWidget and ExternalIpsPage that can cause 403 errors on initial load, a potential runtime crash in ResourceQuotasField when parsing numeric quota values, and a potential crash in SecretsTab when attempting to copy to the clipboard in non-secure HTTP contexts.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const { data: optionList, isLoading } = useK8sList<OptionObject>({ | ||
| apiGroup: "core.cozystack.io", | ||
| apiVersion: "v1alpha1", | ||
| plural: "options", | ||
| namespace: tenantNamespace ?? undefined, | ||
| }) |
There was a problem hiding this comment.
The useK8sList query is missing the enabled: !!tenantNamespace option. When tenantNamespace is null (e.g., during initial load), it will trigger a cluster-wide query to /apis/core.cozystack.io/v1alpha1/options, which will fail with a 403 Forbidden error for non-admin users. Gating the query with enabled: !!tenantNamespace prevents this unnecessary and failing request.
| const { data: optionList, isLoading } = useK8sList<OptionObject>({ | |
| apiGroup: "core.cozystack.io", | |
| apiVersion: "v1alpha1", | |
| plural: "options", | |
| namespace: tenantNamespace ?? undefined, | |
| }) | |
| const { data: optionList, isLoading } = useK8sList<OptionObject>( | |
| { | |
| apiGroup: "core.cozystack.io", | |
| apiVersion: "v1alpha1", | |
| plural: "options", | |
| namespace: tenantNamespace ?? undefined, | |
| }, | |
| { enabled: !!tenantNamespace }, | |
| ) |
There was a problem hiding this comment.
Good catch — gated the query with enabled: !!tenantNamespace so it no longer issues a cluster-wide request to the core.cozystack.io options endpoint while the tenant namespace is still resolving. Fixed in d4b0b52.
| */ | ||
| export function ExternalIpsPage() { | ||
| const { tenantNamespace } = useTenantContext() | ||
| const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>( | ||
| { | ||
| apiGroup: "", | ||
| apiVersion: "v1", | ||
| plural: "services", | ||
| namespace: tenantNamespace ?? undefined, |
There was a problem hiding this comment.
The useK8sList query is missing the enabled: !!tenantNamespace option. When tenantNamespace is null, it will trigger a cluster-wide query to /api/v1/services, which will fail with a 403 Forbidden error for non-admin users. Gating the query with enabled: !!tenantNamespace prevents this unnecessary and failing request.
| */ | |
| export function ExternalIpsPage() { | |
| const { tenantNamespace } = useTenantContext() | |
| const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>( | |
| { | |
| apiGroup: "", | |
| apiVersion: "v1", | |
| plural: "services", | |
| namespace: tenantNamespace ?? undefined, | |
| const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>( | |
| { | |
| apiGroup: "", | |
| apiVersion: "v1", | |
| plural: "services", | |
| namespace: tenantNamespace ?? undefined, | |
| }, | |
| { fieldSelector: "spec.type=LoadBalancer", enabled: !!tenantNamespace }, | |
| ) |
There was a problem hiding this comment.
Agreed — added enabled: !!tenantNamespace to the services query so it won't hit /api/v1/services cluster-wide before the tenant namespace is known. Fixed in fbbcc2e.
| function parseSize(val: string, units: readonly string[]): { num: string; unit: string } { | ||
| // Match longest suffix first to avoid partial matches | ||
| const sorted = [...units].sort((a, b) => b.length - a.length) | ||
| for (const u of sorted) { | ||
| if (val.endsWith(u)) return { num: val.slice(0, -u.length), unit: u } | ||
| } | ||
| // Strip any trailing alphabetic chars so we don't corrupt data on save | ||
| const numericPart = val.replace(/[A-Za-z]+$/, "") | ||
| return { num: numericPart || val, unit: units[0] ?? "" } | ||
| } |
There was a problem hiding this comment.
parseSize expects val to be a string and calls val.endsWith(u). However, if val is a number (e.g., cpu: 10 in user-authored YAML), it will throw TypeError: val.endsWith is not a function and crash the page when loading the edit form. Coercing val to a string first prevents this runtime crash.
| function parseSize(val: string, units: readonly string[]): { num: string; unit: string } { | |
| // Match longest suffix first to avoid partial matches | |
| const sorted = [...units].sort((a, b) => b.length - a.length) | |
| for (const u of sorted) { | |
| if (val.endsWith(u)) return { num: val.slice(0, -u.length), unit: u } | |
| } | |
| // Strip any trailing alphabetic chars so we don't corrupt data on save | |
| const numericPart = val.replace(/[A-Za-z]+$/, "") | |
| return { num: numericPart || val, unit: units[0] ?? "" } | |
| } | |
| function parseSize(val: string | number, units: readonly string[]): { num: string; unit: string } { | |
| const valStr = String(val) | |
| // Match longest suffix first to avoid partial matches | |
| const sorted = [...units].sort((a, b) => b.length - a.length) | |
| for (const u of sorted) { | |
| if (valStr.endsWith(u)) return { num: valStr.slice(0, -u.length), unit: u } | |
| } | |
| // Strip any trailing alphabetic chars so we don't corrupt data on save | |
| const numericPart = valStr.replace(/[A-Za-z]+$/, "") | |
| return { num: numericPart || valStr, unit: units[0] ?? "" } | |
| } |
There was a problem hiding this comment.
Right — parseSize now coerces val to a string first, so a numeric quota from user-authored YAML (e.g. cpu: 10) no longer throws val.endsWith is not a function. Fixed in 83a8479.
| <button | ||
| type="button" | ||
| disabled={!revealed} | ||
| onClick={() => navigator.clipboard.writeText(fullValue)} | ||
| className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30" | ||
| title="Copy" | ||
| > | ||
| <Copy className="size-3.5" /> | ||
| </button> |
There was a problem hiding this comment.
navigator.clipboard.writeText is called without checking if navigator.clipboard is defined. In non-secure contexts (HTTP), navigator.clipboard is undefined, and clicking the copy button will crash the app with a TypeError. We should guard the button's disabled state and the click handler to prevent this crash.
| <button | |
| type="button" | |
| disabled={!revealed} | |
| onClick={() => navigator.clipboard.writeText(fullValue)} | |
| className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30" | |
| title="Copy" | |
| > | |
| <Copy className="size-3.5" /> | |
| </button> | |
| <button | |
| type="button" | |
| disabled={!revealed || !navigator.clipboard} | |
| onClick={() => navigator.clipboard?.writeText(fullValue)} | |
| className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30" | |
| title="Copy" | |
| > | |
| <Copy className="size-3.5" /> | |
| </button> |
There was a problem hiding this comment.
Added a guard: the copy button is now disabled and the handler is a no-op when navigator.clipboard is unavailable (plain-HTTP contexts), so it no longer throws. Fixed in ea0c0ab.
Move the console SPA source into the monorepo at packages/system/dashboard/images/console so the UI versions and ships in lockstep with the platform, removing the cross-repo digest-bump PRs and the version drift between the two repositories. Snapshot of cozystack/cozystack-ui main at 8ef45a4; full history remains in that repository (to be archived). The Makefile now builds the console image from this in-tree context instead of cloning the external repo at build time: dropped clone-console/clean-console and the CONSOLE_REPO/CONSOLE_BRANCH variables; the built digest is still pinned into values.yaml by update-values-console. .helmignore already excludes images/*/* so the source never enters the Helm chart. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Mirrors the former cozystack-ui test workflow: pnpm typecheck + unit tests, gated to changes under packages/system/dashboard/images/console. pnpm/action-setup reads the pnpm version from package_json_file (the console workspace manifest) since the monorepo root has no package.json. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on DynamicOptionsWidget.tsx: gate the useK8sList options query with enabled: !!tenantNamespace so it does not fire a cluster-wide request to core.cozystack.io options (403 for non-admin tenants) while the tenant namespace is still resolving. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on ExternalIpsPage.tsx: add enabled: !!tenantNamespace to the services useK8sList so it does not issue a cluster-wide /api/v1/services request (403 for non-admin tenants) before the tenant namespace is known. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on ResourceQuotasField.tsx: parseSize coerces val to a string first so a numeric quota from user-authored YAML (e.g. cpu: 10) no longer crashes the edit form with "val.endsWith is not a function". Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on SecretsTab.tsx: guard the copy button on navigator.clipboard so it is disabled and a no-op when the API is unavailable (plain-HTTP contexts), instead of throwing a TypeError. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
1c1f0b7 to
ea0c0ab
Compare
|
Re: the force-push — this branch originally vendored the UI with |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — the relocation is complete and coherent: the build, CI, and Helm packaging all wire to the in-tree source, no secrets or build artifacts are committed, and the shipped image/digest are unchanged. The findings below are pre-existing upstream UI behaviors carried in verbatim; none block the move.
Business context: moves the cozystack-ui console SPA into the monorepo under packages/system/dashboard/images/console/ so the UI ships and versions in lockstep with the platform, replacing the build-time git clone of the external repo.
Vendoring-correctness checklist (all pass):
- Import is complete and coherent —
Containerfile,pnpm-workspace.yaml,pnpm-lock.yaml, and all four workspace packages (apps/console,packages/{ui,types,k8s-client}) are present, and the build filter@cozystack/consoleresolves toapps/console/package.json. Evidence:packages/system/dashboard/images/console/Containerfile,.../apps/console/package.json:2. - Build is rewired to the in-tree context and drops the clone —
docker buildx build images/console --file images/console/Containerfile, the OCI source label is repointed to the monorepo, and the digest is still pinned byupdate-values-consolein the same commit. Evidence:packages/system/dashboard/Makefile(build-console/update-values-console). - Source is excluded from the Helm chart —
.helmignorealready carriesimages/*/*. Evidence:packages/system/dashboard/.helmignore:3. - No secrets/credentials/
.env/private keys committed — a full-diff scan returns only credential-masking UI logic and test fixtures. Evidence:git diff origin/main...HEAD(no real secret material;images/console/.gitignoreexcludesnode_modules,dist,.env). - License is intact — the code is now first-party under the repo-wide Apache-2.0
LICENSE. Evidence:LICENSE:1. - CI builds/validates from the vendored source — new path-gated
ui-test.yamlrunspnpm typecheck+pnpm test, and the image continues to build viamake build. Evidence:.github/workflows/ui-test.yaml. - The four earlier review findings are addressed with surgical, single-file commits. Evidence: commits
d4b0b527b,fbbcc2ee8,83a847916,ea0c0ab1a.
Blockers: none.
Non-blocking (pre-existing upstream behavior carried in verbatim — fine to land the move now and fix in follow-ups, now that the code is trackable in-tree):
-
Watch never reconnects when the resource version is unchanged. The watch error callback only calls
invalidateQueries; if the re-list returns the sameresourceVersion, the effect dependencies do not change and the watch is never re-established, leaving views permanently stale after a transient proxy/network drop. Evidence:packages/system/dashboard/images/console/packages/k8s-client/src/hooks.ts:88-113. -
Detail page renders an infinite spinner on a failed GET.
if (isLoading || !instance || !ad)precedes theif (error)branch, so a 404/403/5xx leavesinstanceundefined and renders the spinner forever; the error branch is unreachable. Same class as the loading-state issues already fixed in this PR. Evidence:packages/system/dashboard/images/console/apps/console/src/routes/detail/ApplicationDetailPage.tsx:72. -
Application list renders an infinite spinner for an unknown
plural.if (defsLoading || (!ad && plural))stays true for any unrecognized URL after definitions finish loading, so the "Unknown application type" branch is unreachable. Evidence:packages/system/dashboard/images/console/apps/console/src/routes/ApplicationListPage.tsx:29. -
The image build does not use the pinned pnpm.
package.jsonsetspackageManager: [email protected]and CI honors it, but theContainerfilerunscorepack prepare pnpm@latest --activate, so the container build can drift to a newer pnpm major.--frozen-lockfileguards the lockfile, but pinning the builder to the same version would make the image build reproducible. Evidence:packages/system/dashboard/images/console/Containerfile:9vs.../package.json:5. -
No
.gitattributesmarks the imported tree. This is acceptable and not a blocker: the code is now first-party (the plan archives the external repo), the repo has no.gitattributesconvention, the existing in-treeimages/token-proxy/precedent is unmarked, andpnpm-lock.yamlis auto-detected as generated by Linguist. Markingimages/console/**linguist-vendoredwould also hide it from future review diffs, which is undesirable for first-party source. Optionally mark onlypnpm-lock.yamllinguist-generatedif you want the one-time import to collapse in the diff view. Evidence: no.gitattributesin repo;packages/system/dashboard/images/token-proxy/unmarked.
The console UI now lives in cozystack/cozystack under packages/system/dashboard/images/console (cozystack/cozystack#2963). Tracked in cozystack/cozystack#3097.
The console UI now lives in cozystack/cozystack under packages/system/dashboard/images/console (cozystack/cozystack#2963). Tracked in cozystack/cozystack#3097. Signed-off-by: Myasnikov Daniil <[email protected]>
…t vendor doc link (#3099) ## What this PR does Two post-vendor cleanups from the #2963 review: - **Pin the console image builder to pnpm 11.1.2.** The `Containerfile` ran `corepack prepare pnpm@latest`, so the container build could drift to a newer pnpm major than the `[email protected]` pinned in `package.json` and used by CI. `--frozen-lockfile` guards the lockfile, but pinning the builder makes the image build reproducible and consistent with CI. - **Repoint a stale doc link.** `docs/storage-immutability.md` referenced the now-vendored `cozystack/cozystack-ui#6`; it now points at the in-tree console path. ## Verification - No source change; `pnpm typecheck` + `pnpm test` unaffected. - Builder pnpm version now matches `package.json`'s `packageManager` field. ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Clarified the storage immutability documentation by updating the UI reference to an in-repo path. * **Chores** * Standardized the build environment to use a specific pnpm version, helping keep installs and builds consistent. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…finite spinner (#3098) ## What this PR does Two console pages ordered their loading guard ahead of the terminal state, making the latter unreachable. Both are pre-existing upstream behaviors carried in by the cozystack-ui vendoring (#2963) and flagged as non-blocking findings in that PR's review. - **`ApplicationDetailPage`** checked `isLoading || !instance || !ad` before `if (error)`. On a failed GET (403/404/5xx) `isLoading` is false and `instance` stays undefined, so the page rendered the loading spinner forever and never reached the "not found" branch. The error branch now runs first. - **`ApplicationListPage`**'s `defsLoading || (!ad && plural)` guard stayed true for any unrecognized `plural` once definitions had loaded, so the "Unknown application type." branch was unreachable. The guard now only spins while definitions are loading. ## Verification - Added co-located regression tests for the failed-GET (detail) and unknown-plural (list) paths. - `pnpm typecheck` clean; full console suite green (`pnpm test`: 317 passed, +4 new). ```release-note fix(dashboard): the console now shows a not-found message on a failed application GET and an unknown-type message for an unrecognized resource, instead of spinning forever ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved loading and error handling on application pages so users see the correct message at the right time. * “Unknown application type” now appears only after application definitions finish loading, instead of being masked by a loading state. * Application detail pages now show a “not found” message when the requested app can’t be loaded, rather than continuing to display a spinner. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…lently When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s. Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet. Co-authored-by: IvanHunters <[email protected]> Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
In the four backup create pages (Backup, BackupJob, BackupPlan, BackupRestoreJob), run the RJSF SchemaForm.validate() call right after the name check and before the page-level required-field alerts. The submit button lives outside RJSF and bypasses its validation, so each page triggers validate() explicitly; it previously ran only after the manual alert checks, so a schema-required field left empty exited via an alert without RJSF ever rendering its inline errors. Validating first makes inline errors show consistently; the page-level alerts then cover only page-only requirements. Re-applied from cozystack/cozystack-ui#26 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Scope is the validation-ordering change only — the original PR's VMDisk/StorageClass parts were obsoleted by the DynamicOptionsWidget refactor (#30) already in the vendored snapshot. Includes the original per-page tests. Co-authored-by: Aleksei Sviridkin <[email protected]> Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
The cozystack-ui SPA was vendored into the monorepo (#2963) and now lives under packages/system/dashboard. Map its likely PR title scopes to the existing area/dashboard so UI PRs no longer fall through to area/uncategorized. Assisted-By: Claude <[email protected]> Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
…3117) ## What this PR does The cozystack-ui SPA was vendored into the monorepo in #2963 and now lives under `packages/system/dashboard`. PRs touching the UI are likely to use `cozystack-ui` or `console` as their Conventional Commits scope, neither of which the PR auto-labeler recognizes today — so they fall through to `area/uncategorized`. This maps both scopes to the existing `area/dashboard` (already described as "dashboard / UI"). No new label is introduced; `.github/labels.yml` is unchanged. ### Release note ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Expanded automatic PR labeling to add the `area/dashboard` label when a PR title scope is `cozystack-ui` or `console`. * Existing label rules and parsing behavior remain unchanged for all other scopes. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…finite spinner Two console pages ordered their loading guard ahead of the terminal state, making the latter unreachable: - ApplicationDetailPage checked `isLoading || !instance || !ad` before `if (error)`. On a failed GET, isLoading is false and instance stays undefined, so the page spun forever and never showed the not-found message. The error branch now runs first. - ApplicationListPage's `defsLoading || (!ad && plural)` guard stayed true for any unrecognized plural once definitions had loaded, so 'Unknown application type.' was unreachable. The guard now only spins while definitions are loading. Both are pre-existing upstream behaviors carried in by the cozystack-ui vendoring (cozystack#2963). Adds co-located regression tests for the failed-GET and unknown-plural paths. Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
…t vendor doc link The console Containerfile ran `corepack prepare pnpm@latest`, so the image build could drift to a newer pnpm major than the `[email protected]` pinned in package.json and used by CI. `--frozen-lockfile` guards the lockfile, but pinning the builder makes the image build reproducible and consistent with CI. Also repoints docs/storage-immutability.md off the now-vendored cozystack-ui#6 link to the in-tree console path (follow-up from cozystack#2963). Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
…lently When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s. Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet. Co-authored-by: IvanHunters <[email protected]> Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
In the four backup create pages (Backup, BackupJob, BackupPlan, BackupRestoreJob), run the RJSF SchemaForm.validate() call right after the name check and before the page-level required-field alerts. The submit button lives outside RJSF and bypasses its validation, so each page triggers validate() explicitly; it previously ran only after the manual alert checks, so a schema-required field left empty exited via an alert without RJSF ever rendering its inline errors. Validating first makes inline errors show consistently; the page-level alerts then cover only page-only requirements. Re-applied from cozystack/cozystack-ui#26 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Scope is the validation-ordering change only — the original PR's VMDisk/StorageClass parts were obsoleted by the DynamicOptionsWidget refactor (#30) already in the vendored snapshot. Includes the original per-page tests. Co-authored-by: Aleksei Sviridkin <[email protected]> Assisted-By: Claude <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
#3116) ## What this PR does Re-applies cozystack/cozystack-ui#26 (by @lexfrei) onto the in-tree console, after the UI was vendored into the monorepo (#2963) and the standalone repo is being archived. Part of the cozystack-ui retirement tracked in #3097. In the four backup create pages (Backup, BackupJob, BackupPlan, BackupRestoreJob), the RJSF `SchemaForm.validate()` call now runs right after the name check and before the page-level required-field alerts. The submit button lives outside RJSF and bypasses its validation, so each page triggers `validate()` explicitly; it previously ran only after the manual alert checks, so a schema-required field left empty exited via an alert without RJSF ever rendering its inline errors. Validating first makes inline errors show consistently; the page-level alerts then cover only page-only requirements (application/backup reference, strategy, timestamp). Scope is the validation-ordering change only — the original PR's VMDisk/StorageClass parts were obsoleted by the `DynamicOptionsWidget` refactor (#30) already present in the vendored snapshot. ## Verification - Includes the original per-page regression tests. - `pnpm typecheck` clean; full console suite green (317 passed). Original PR: cozystack/cozystack-ui#26 — credited via `Co-authored-by`. ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved submission validation across backup creation, backup job, backup plan, and backup restore job pages. * Inline required-field errors now appear before page-level alerts. * Invalid submissions are prevented from sending requests or triggering unnecessary alerts. * **Tests** * Expanded coverage for validation order and submission blocking. * Improved form test scenarios for invalid and missing data. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…lently When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s. Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet. Co-authored-by: IvanHunters <[email protected]> Assisted-by: LLM Signed-off-by: Myasnikov Daniil <[email protected]>
…lently When the kc-access cookie expires, k8s API calls return 401 and the SPA stays on a stale page until the user clears the cache by hand. K8sClient now treats 401 as an auth boundary and makes one top-level navigation into the oauth2-proxy sign-in flow, passing the current location as rd so the user comes back to the same page. The handler can be replaced through K8sClientConfig.onUnauthorized and does nothing outside the browser. The unauthorizedHandled latch keeps concurrent 401s from redirecting more than once. Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963), since the source repo is being archived. Related: #2788. Co-authored-by: IvanHunters <[email protected]> Assisted-by: LLM Signed-off-by: Myasnikov Daniil <[email protected]>
…3115) ## What this PR does When the dashboard session expires, the console now sends the user back to sign-in instead of sitting on a stale page. This re-applies cozystack/cozystack-ui#34 by @IvanHunters onto the in-tree console, since the UI was vendored into the monorepo (#2963) and the standalone repo is being archived. It is part of the cozystack-ui retirement tracked in #3097. When the `kc-access` cookie expires, k8s API calls return 401. `K8sClient` now treats a 401 as an auth boundary and makes one top-level navigation to `/oauth2/sign_in?rd=<current-url>`. That path is the sign-in entry both gatekeeper modes serve. With OIDC enabled, oauth2-proxy goes straight to the provider (`--skip-provider-button`) and returns the user to `rd`. Without OIDC, token-proxy renders its token form there, but it does not honour `rd` yet, so the user lands on `/` after sign-in. The handler can be replaced through `K8sClientConfig.onUnauthorized` and does nothing outside the browser. A latch keeps concurrent 401s from redirecting more than once, and any 2xx from either fetch site re-arms it, so a handler that re-authenticates in place still sees the next expiry. Related: #2788. ### Verification `client.unauthorized.test.ts` covers the hook on the request and watch paths, the latch across concurrent 401s, the re-arm after a success, and the default handler's target and relative `rd`. `pnpm typecheck` and the console test suite pass. Original PR: cozystack/cozystack-ui#34, credited via `Co-authored-by`. ### Screenshots Not a UI change. ### Downstream repositories - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: ### Release note ```release-note fix(dashboard): on a 401 (expired session) the console now sends the user to sign in again instead of silently showing a stale page. With OIDC enabled the user returns to the page they were on; without OIDC they land on the start page after entering a token. ```
What this PR does
Moves the
cozystack-uiconsole SPA into this monorepo so the UI versions and ships in lockstep with the platform — removing the cross-repo digest-bump PRs and the version drift between the two repositories.The source is vendored at
packages/system/dashboard/images/console/viagit subtree, preserving full upstream history. This mirrors the existing in-treeimages/token-proxy/pattern, and.helmignorealready excludesimages/*/*so the source never enters the Helm chart.Changes
cozystack-uimainmerged underpackages/system/dashboard/images/console/(history preserved as a merge parent).packages/system/dashboard/Makefilebuilds the console image from the in-tree context instead of cloning the external repo at build time. Droppedclone-console/clean-consoleand theCONSOLE_REPO/CONSOLE_BRANCHvariables. The built digest is still pinned intovalues.yamlbyupdate-values-console— now in the same commit as the source change rather than a separate follow-up PR..github/workflows/ui-test.yamlruns pnpm typecheck + unit tests, path-gated toimages/console/**. The container image continues to build viamake build.Verification
pnpm install --frozen-lockfile→pnpm typecheckclean → production build producesdist/.helm templaterenders; console + token-proxy image references unchanged.Follow-ups (after merge)
cozystack-uirepository and redirect contributions here.Screenshots
N/A — no UI behavior change. This relocates the UI source and rewires the build; the shipped image and digest are unchanged.
Release note
🤖 Generated with Claude Code