diff --git a/.gitignore b/.gitignore index ea0e4157..bc4ff251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,15 @@ -node_modules +# Dependencies +node_modules/ +.pnpm-store/ + +# Nuxt build output +.nuxt/ +.output/ +.nitro/ +.cache/ +dist/ +# TypeScript tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo @@ -13,92 +23,27 @@ tsconfig.build.tsbuildinfo /logs *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm +# Test coverage +coverage/ # Optional eslint cache .eslintcache -# Optional REPL history -.node_repl_history - # Output of 'npm pack' *.tgz -# Yarn Integrity file -.yarn-integrity - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# Nuxt generate -dist - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless - # IDE / Editor .idea - -# Service worker -sw.* +*.swp # macOS .DS_Store -# Vim swap files -*.swp +# Nx .nx -.output -# Claude +# Claude and agent tooling .claude/ .playwright-mcp/ -docs/ \ No newline at end of file +.superpowers/ +docs/ diff --git a/README.md b/README.md index b4cef1cc..7ae4d6bb 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,89 @@ website, the administrators have access to an overview of the submitted projects the data at the data center, as well as have full control over the individual containers running on their systems and manage which projects/analyses have access to the various datasets. -## Required Environment Variables +It is a [Nuxt 4](https://nuxt.com) single-page application built with [PrimeVue 4](https://primevue.org), +Tailwind CSS 4 and Pinia. Authentication is handled by [@sidebase/nuxt-auth](https://auth.sidebase.io) (Auth.js) through +the server route `server/routes/flame/api/auth/[...].ts`. -This frontend requires the following environment variables to be set. The SSR framework used for this project (Nuxt) -in addition to the OIDC library (nuxt-oidc-auth) requires the keycloak-related environment variables to be precisely -named and explicitly defined in order to overwrite the default configuration during runtime -(i.e. when deployed in a container). +## Development -```dotenv -NUXT_PUBLIC_BASE_URL="http://localhost:3000" # URL of the website -NUXT_PUBLIC_HUB_ADAPTER_URL="http://urlForHubAdapterApi.de" # URL for hub adapter API -NUXT_PUBLIC_ORIGIN=$NUXT_PUBLIC_BASE_URL/api/auth # NO QUOTES! Relative path to sign page (you likely should not modify this) +Requires Node.js 24 and pnpm (version pinned in `package.json` via `packageManager`; `corepack enable` will pick it up). + +```bash +pnpm install # also runs `nuxt prepare` +pnpm dev # http://localhost:3000, reads .env (see Environment Variables) +``` -NUXT_IDP_CLIENT_ID=node-ui # Client ID defined in the IDP provided in NUXT_PUBLIC_IDP_ISSUER -NUXT_IDP_CLIENT_SECRET=xxx -NUXT_PUBLIC_IDP_PROVIDER="keycloak" # Can be either 'keycloak' or 'authentik' -NUXT_PUBLIC_IDP_ISSUER="http://my-keycloak.com/realms/flame" +| Command | Purpose | +|------------------|-------------------------------------------------------------------------------| +| `pnpm dev` | Start the dev server with hot reload | +| `pnpm build` | Production build into `.output/` (runs type checking) | +| `pnpm prod` | Serve the production build (`node .output/server/index.mjs`) | +| `pnpm test` | Run the Vitest suite in watch mode (`pnpm test --run` for a single pass) | +| `pnpm coverage` | Run the tests with coverage | +| `pnpm lint` | Lint with ESLint (`pnpm lint:fix` to apply automatic fixes) | +| `pnpm typecheck` | Type check the app, server and tests | +| `pnpm build-api` | Regenerate `app/services/Api.ts` from `app/services/hub_adapter_swagger.json` | -# If not using the same keycloak instance as the other FLAME Node services for user authentication, then this needs to be set -NUXT_PUBLIC_INTERNAL_KEYCLOAK_URL="" +### Project layout -NUXT_AUTH_SECRET=xxx # Unique key for encrypting JWT ``` +app/ + assets/ global CSS (main.css is the entry) and the PrimeVue theme preset + components/ UI components, auto-imported by file name + composables/ use* functions that rely on Vue/Nuxt context + layouts/ page shell (header, footer) + pages/ file-based routes + plugins/ Nuxt plugins (hub adapter $fetch client, node settings) + services/ generated hub adapter API types (do not edit Api.ts by hand) + stores/ Pinia stores + types/ shared app types + utils/ pure helper functions +server/ Nitro routes: auth handler and health endpoints +shared/types/ type augmentations shared by app and server +test/ Vitest specs mirroring app/, plus MSW mocks in test/mockapi +``` + +## Environment Variables -| EnvVar | Description | Default | Required | -|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------|--------------------------------|:--------:| -| NUXT_PUBLIC_BASE_URL | URL of the website | | x | -| NUXT_PUBLIC_HUB_ADAPTER_URL | URL for hub adapter API | | x | -| NUXT_PUBLIC_ORIGIN | Relative path to sign page (you likely should not modify this) | $NUXT_PUBLIC_BASE_URL/api/auth | | -| NUXT_IDP_CLIENT_SECRET | Client secret defined in the IDP provided in NUXT_PUBLIC_IDP_ISSUER | | x | -| NUXT_PUBLIC_IDP_PROVIDER | Can be either 'keycloak', 'auth0', 'authentik', 'onelogin', 'okta', or 'zitadel' | keycloak | | -| NUXT_PUBLIC_IDP_ISSUER | URI for the IDP used to identify itself using OpenID Connect (OIDC) protocol | | x | -| NUXT_PUBLIC_INTERNAL_KEYCLOAK_URL | If not using the same keycloak instance as the other FLAME Node services for user authentication, then this needs to be set | | | -| NUXT_AUTH_SECRET | Unique key for encrypting JWT, see [Setting a Secret below](#setting-a-secret) | | x | +Variables prefixed with `NUXT_` override the Nuxt runtime config, so all of them can be changed at container start +without rebuilding the image. The auth server route reads them directly from the environment. + +```dotenv +NUXT_PUBLIC_BASE_URL="http://localhost:3000" +NUXT_PUBLIC_ORIGIN="http://localhost:3000/flame/api/auth" +NUXT_PUBLIC_HUB_ADAPTER_URL="http://localhost:5000" + +NUXT_PUBLIC_IDP_PROVIDER="hub" +NUXT_PUBLIC_IDP_ISSUER="https://auth.example.org" +NUXT_IDP_CLIENT_ID="node-ui" +NUXT_IDP_CLIENT_SECRET="xxx" + +NUXT_AUTH_SECRET="xxx" # see "Setting a Secret" below +``` + +### Required + +| Variable | Description | +|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `NUXT_PUBLIC_BASE_URL` | Public URL of this website. Default: `http://localhost:3000` | +| `NUXT_PUBLIC_ORIGIN` | Full public URL of the auth endpoint, i.e. `$NUXT_PUBLIC_BASE_URL/flame/api/auth`. Default: `http://localhost:3000/flame/api/auth` | +| `NUXT_PUBLIC_HUB_ADAPTER_URL` | URL of the hub adapter API. Used by the browser and by the auth route. Default: `http://localhost:5000` | +| `NUXT_PUBLIC_IDP_PROVIDER` | Identity provider type: `hub`, `keycloak`, `authentik`, `onelogin`, `okta` or `zitadel`. Default: `hub` | +| `NUXT_PUBLIC_IDP_ISSUER` | OIDC issuer URL of the identity provider, e.g. `https://keycloak.example.org/realms/flame`. Default: `https://auth.hub.privateaim.net/realms/master` | +| `NUXT_IDP_CLIENT_SECRET` | Client secret of the OIDC client registered at the identity provider | +| `NUXT_AUTH_SECRET` | Secret used to sign and encrypt the session JWT, see [Setting a Secret](#setting-a-secret) | + +### Optional + +| Variable | Description | Default | +|-----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| `NUXT_IDP_CLIENT_ID` | Client ID of the OIDC client registered at the identity provider | `node-ui` | +| `NUXT_PUBLIC_INTERNAL_KEYCLOAK_URL` | Internal (cluster) URL of the identity provider used for server-side token, userinfo and JWKS requests when it differs from the public issuer. Not used with the `hub` provider | value of `NUXT_PUBLIC_IDP_ISSUER` | +| `NUXT_IDP_HEALTH_TIMEOUT_MS` | Timeout for the identity provider reachability check at `/flame/api/health`. Keep it below 3500, the Auth.js request timeout | `3000` | +| `NUXT_PUBLIC_VICTORIA_LOGS_URL` | URL of a VictoriaLogs instance. When set, a "VictoriaLogs" link appears in the user menu | | +| `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` | Outbound proxy for server-side requests to the identity provider and hub adapter | | +| `NUXT_HOST`, `NUXT_PORT` | Address and port the server listens on. The Docker images set `0.0.0.0` and `3000` | | ## Setting a Secret @@ -46,4 +96,13 @@ sign/encrypt cookies, and to generate cryptographic keys. To quickly generate su ```bash openssl rand -base64 32 -``` \ No newline at end of file +``` + +## Docker + +`prod.Dockerfile` builds the production image used by CI: + +```bash +docker build -f prod.Dockerfile -t node-ui . +docker run --env-file .env -p 3000:3000 node-ui +``` diff --git a/app/app.vue b/app/app.vue index e8e6c7f8..5274587f 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,10 +1,6 @@ - - diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 054c0332..f150e382 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -1,4 +1,8 @@ @import "tailwindcss"; +@import "./elements.css"; +@import "./table.css"; +@import "./preferences.css"; +@import "./toast.css"; html { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, @@ -23,7 +27,3 @@ html { gap: 1rem; margin-bottom: 2rem; } - -.page-description { - -} \ No newline at end of file diff --git a/app/components/analysis/AnalysesTable.vue b/app/components/analysis/AnalysesTable.vue index 477096e9..7be09f41 100644 --- a/app/components/analysis/AnalysesTable.vue +++ b/app/components/analysis/AnalysesTable.vue @@ -1,403 +1,46 @@ - - - - diff --git a/app/components/data-stores/DetailedDataStoreTable.vue b/app/components/data-stores/DetailedDataStoreTable.vue index c7f7bafb..b7eec94d 100644 --- a/app/components/data-stores/DetailedDataStoreTable.vue +++ b/app/components/data-stores/DetailedDataStoreTable.vue @@ -1,35 +1,14 @@ diff --git a/app/components/data-stores/create/DataStoreProjectInitializer.vue b/app/components/data-stores/create/DataStoreProjectInitializer.vue index 1eaa97bf..3785a2d6 100644 --- a/app/components/data-stores/create/DataStoreProjectInitializer.vue +++ b/app/components/data-stores/create/DataStoreProjectInitializer.vue @@ -1,148 +1,69 @@ diff --git a/app/pages/analyses/[id].vue b/app/pages/analyses/[id].vue index 5e5966b3..7abc6ca0 100644 --- a/app/pages/analyses/[id].vue +++ b/app/pages/analyses/[id].vue @@ -1,9 +1,9 @@ - - diff --git a/app/pages/analyses/index.vue b/app/pages/analyses/index.vue index a7a512ca..3f7ee341 100644 --- a/app/pages/analyses/index.vue +++ b/app/pages/analyses/index.vue @@ -1,9 +1,19 @@ - - - - diff --git a/app/pages/data-stores/create.vue b/app/pages/data-stores/create.vue index 27579019..de1028b0 100644 --- a/app/pages/data-stores/create.vue +++ b/app/pages/data-stores/create.vue @@ -1,7 +1,45 @@ + + diff --git a/app/pages/data-stores/index.vue b/app/pages/data-stores/index.vue index bfe4a9be..90dd70e0 100644 --- a/app/pages/data-stores/index.vue +++ b/app/pages/data-stores/index.vue @@ -1,9 +1,47 @@ - + diff --git a/app/pages/error/403.vue b/app/pages/error/403.vue index f2c76187..4c353934 100644 --- a/app/pages/error/403.vue +++ b/app/pages/error/403.vue @@ -1,7 +1,4 @@ - - - diff --git a/app/pages/index.vue b/app/pages/index.vue index 95ed149d..14d79f6a 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,6 +1,4 @@ - - - diff --git a/app/pages/uptime.vue b/app/pages/uptime.vue index 92cf21ff..c4dea47e 100644 --- a/app/pages/uptime.vue +++ b/app/pages/uptime.vue @@ -1,16 +1,11 @@