From 1ee305c97a1e1f6a88bda1da937e5592d3775a60 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Thu, 14 May 2026 16:30:02 -0700 Subject: [PATCH 01/11] feat(client): scaffold React 19 + Tailwind 4 + @mieweb/ui SPA Phase 1 of manager UI rewrite. Creates create-a-container/client/ with: - Vite 6 + React 19 + TypeScript - Tailwind CSS 4 via @tailwindcss/vite - @mieweb/ui 0.6.1 with BlueHive brand CSS - React Router 7 (data routers) with full route tree placeholder - TanStack Query 5, react-hook-form + zod, lucide-react Adds client:* scripts to create-a-container/package.json. --- create-a-container/client/.gitignore | 8 + create-a-container/client/README.md | 23 + create-a-container/client/index.html | 14 + create-a-container/client/package-lock.json | 2694 +++++++++++++++++ create-a-container/client/package.json | 34 + .../client/src/app/AppLayout.tsx | 14 + .../client/src/app/AuthLayout.tsx | 11 + .../client/src/app/PlaceholderPage.tsx | 12 + create-a-container/client/src/app/router.tsx | 51 + create-a-container/client/src/main.tsx | 31 + .../client/src/styles/index.css | 11 + create-a-container/client/src/vite-env.d.ts | 1 + create-a-container/client/tsconfig.app.json | 32 + create-a-container/client/tsconfig.json | 7 + create-a-container/client/tsconfig.node.json | 22 + create-a-container/client/vite.config.ts | 31 + create-a-container/package.json | 10 +- 17 files changed, 3004 insertions(+), 2 deletions(-) create mode 100644 create-a-container/client/.gitignore create mode 100644 create-a-container/client/README.md create mode 100644 create-a-container/client/index.html create mode 100644 create-a-container/client/package-lock.json create mode 100644 create-a-container/client/package.json create mode 100644 create-a-container/client/src/app/AppLayout.tsx create mode 100644 create-a-container/client/src/app/AuthLayout.tsx create mode 100644 create-a-container/client/src/app/PlaceholderPage.tsx create mode 100644 create-a-container/client/src/app/router.tsx create mode 100644 create-a-container/client/src/main.tsx create mode 100644 create-a-container/client/src/styles/index.css create mode 100644 create-a-container/client/src/vite-env.d.ts create mode 100644 create-a-container/client/tsconfig.app.json create mode 100644 create-a-container/client/tsconfig.json create mode 100644 create-a-container/client/tsconfig.node.json create mode 100644 create-a-container/client/vite.config.ts diff --git a/create-a-container/client/.gitignore b/create-a-container/client/.gitignore new file mode 100644 index 00000000..f523d209 --- /dev/null +++ b/create-a-container/client/.gitignore @@ -0,0 +1,8 @@ +node_modules +dist +dist-ssr +*.local +.vite +*.log +.DS_Store +*.tsbuildinfo diff --git a/create-a-container/client/README.md b/create-a-container/client/README.md new file mode 100644 index 00000000..6b672e75 --- /dev/null +++ b/create-a-container/client/README.md @@ -0,0 +1,23 @@ +# Manager Client + +Vite + React 19 + TypeScript SPA for the Create-a-Container manager. Styles via Tailwind 4 and `@mieweb/ui` (BlueHive brand). + +## Develop + +```bash +npm install +npm run dev # http://localhost:5173 (proxies /api, /login, /logout, /nginx-conf, /dnsmasq to Express) +``` + +Express must be running on `http://localhost:3000` (or set `VITE_API_TARGET`). + +## Build + +```bash +npm run build # outputs to dist/, served by Express in production +``` + +## Layout + +- `src/app/` — router, layouts, shell +- `src/styles/` — Tailwind + `@mieweb/ui` brand entry diff --git a/create-a-container/client/index.html b/create-a-container/client/index.html new file mode 100644 index 00000000..31fb436c --- /dev/null +++ b/create-a-container/client/index.html @@ -0,0 +1,14 @@ + + + + + + + + MIE Container Manager + + +
+ + + diff --git a/create-a-container/client/package-lock.json b/create-a-container/client/package-lock.json new file mode 100644 index 00000000..1170e8f9 --- /dev/null +++ b/create-a-container/client/package-lock.json @@ -0,0 +1,2694 @@ +{ + "name": "@mieweb/create-a-container-client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@mieweb/create-a-container-client", + "version": "0.0.0", + "dependencies": { + "@hookform/resolvers": "^3.9.1", + "@mieweb/ui": "latest", + "@tanstack/react-query": "^5.62.0", + "lucide-react": "^0.460.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-hook-form": "^7.54.0", + "react-router": "^7.1.0", + "zod": "^3.24.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "@types/node": "^22.10.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "tailwindcss": "^4.0.0", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mieweb/ui": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@mieweb/ui/-/ui-0.6.1.tgz", + "integrity": "sha512-M1zHSukvCs9fTXTa/fZfHKueWOhOv8oomq0Mic9QhXcVMkN6i+6/JQdboEEJRtQYUhxGwNoTFbYIzMpbODvoXg==", + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@swc/helpers": "^0.5.19", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "google-libphonenumber": "^3.2.44", + "lucide-react": "^0.562.0", + "luxon": "^3.7.2", + "tailwind-merge": "^2.6.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@esheet/builder": ">=0.0.2", + "@esheet/renderer": ">=0.0.2", + "@ozwell/react": ">=0.1.0", + "ag-grid-community": ">=32.0.0", + "ag-grid-react": ">=32.0.0", + "datavis-ace": "=4.0.0-PRE.2", + "react": ">=18.0.0", + "react-dom": ">=18.0.0", + "wavesurfer.js": ">=7.0.0" + }, + "peerDependenciesMeta": { + "@esheet/builder": { + "optional": true + }, + "@esheet/renderer": { + "optional": true + }, + "@ozwell/react": { + "optional": true + }, + "ag-grid-community": { + "optional": true + }, + "ag-grid-react": { + "optional": true + }, + "datavis-ace": { + "optional": true + }, + "react": { + "optional": false + }, + "react-dom": { + "optional": false + }, + "wavesurfer.js": { + "optional": true + } + } + }, + "node_modules/@mieweb/ui/node_modules/lucide-react": { + "version": "0.562.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", + "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/helpers": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", + "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.100.10", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.10.tgz", + "integrity": "sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.100.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.10.tgz", + "integrity": "sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.100.10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", + "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.356", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.356.tgz", + "integrity": "sha512-9NgFd7m5t5MCJ5rUSjJITUXAH9mEGlrlofnMf4YEr+pz6JlP7cWmTAH+JFmbPnaSW8koVTkuW7pacORWAnA5Yw==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.21.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.3.tgz", + "integrity": "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/google-libphonenumber": { + "version": "3.2.44", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.44.tgz", + "integrity": "sha512-9p2TghluF2LTChFMLWsDRD5N78SZDsILdUk4gyqYxBXluCyxoPiOq+Fqt7DKM+LUd33+OgRkdrc+cPR93AypCQ==", + "license": "(MIT AND Apache-2.0)", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.460.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz", + "integrity": "sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/react-hook-form": { + "version": "7.75.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.75.0.tgz", + "integrity": "sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz", + "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", + "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/create-a-container/client/package.json b/create-a-container/client/package.json new file mode 100644 index 00000000..9dd8cd9c --- /dev/null +++ b/create-a-container/client/package.json @@ -0,0 +1,34 @@ +{ + "name": "@mieweb/create-a-container-client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "lint": "tsc -b --noEmit", + "type-check": "tsc -b --noEmit" + }, + "dependencies": { + "@hookform/resolvers": "^3.9.1", + "@mieweb/ui": "latest", + "@tanstack/react-query": "^5.62.0", + "lucide-react": "^0.460.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-hook-form": "^7.54.0", + "react-router": "^7.1.0", + "zod": "^3.24.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "@types/node": "^22.10.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "tailwindcss": "^4.0.0", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } +} diff --git a/create-a-container/client/src/app/AppLayout.tsx b/create-a-container/client/src/app/AppLayout.tsx new file mode 100644 index 00000000..4d2c5129 --- /dev/null +++ b/create-a-container/client/src/app/AppLayout.tsx @@ -0,0 +1,14 @@ +import { Outlet } from 'react-router'; + +export function AppLayout() { + return ( +
+
+

MIE Container Manager

+
+
+ +
+
+ ); +} diff --git a/create-a-container/client/src/app/AuthLayout.tsx b/create-a-container/client/src/app/AuthLayout.tsx new file mode 100644 index 00000000..9f336a4f --- /dev/null +++ b/create-a-container/client/src/app/AuthLayout.tsx @@ -0,0 +1,11 @@ +import { Outlet } from 'react-router'; + +export function AuthLayout() { + return ( +
+
+ +
+
+ ); +} diff --git a/create-a-container/client/src/app/PlaceholderPage.tsx b/create-a-container/client/src/app/PlaceholderPage.tsx new file mode 100644 index 00000000..4935aac8 --- /dev/null +++ b/create-a-container/client/src/app/PlaceholderPage.tsx @@ -0,0 +1,12 @@ +import { useLocation } from 'react-router'; + +export function PlaceholderPage({ title }: { title: string }) { + const { pathname } = useLocation(); + return ( +
+

{title}

+

Route: {pathname}

+

Phase 1 scaffolding placeholder. Implementation lands in Phase 4.

+
+ ); +} diff --git a/create-a-container/client/src/app/router.tsx b/create-a-container/client/src/app/router.tsx new file mode 100644 index 00000000..cd809caa --- /dev/null +++ b/create-a-container/client/src/app/router.tsx @@ -0,0 +1,51 @@ +import { createBrowserRouter, Navigate } from 'react-router'; +import { AppLayout } from './AppLayout'; +import { AuthLayout } from './AuthLayout'; +import { PlaceholderPage } from './PlaceholderPage'; + +export const router = createBrowserRouter([ + { + element: , + children: [ + { path: '/login', element: }, + { path: '/register', element: }, + { path: '/register/success', element: }, + { path: '/verify/:token', element: }, + { path: '/reset-password', element: }, + { path: '/reset-password/:token', element: }, + ], + }, + { + element: , + children: [ + { index: true, element: }, + { path: '/sites', element: }, + { path: '/sites/new', element: }, + { path: '/sites/:id/edit', element: }, + { path: '/sites/:siteId/containers', element: }, + { path: '/sites/:siteId/containers/new', element: }, + { path: '/sites/:siteId/containers/:id/edit', element: }, + { path: '/sites/:siteId/nodes', element: }, + { path: '/sites/:siteId/nodes/new', element: }, + { path: '/sites/:siteId/nodes/import', element: }, + { path: '/sites/:siteId/nodes/:id/edit', element: }, + { path: '/external-domains', element: }, + { path: '/external-domains/new', element: }, + { path: '/external-domains/:id/edit', element: }, + { path: '/jobs/:id', element: }, + { path: '/users', element: }, + { path: '/users/new', element: }, + { path: '/users/invite', element: }, + { path: '/users/:uid/edit', element: }, + { path: '/groups', element: }, + { path: '/groups/new', element: }, + { path: '/groups/:id/edit', element: }, + { path: '/apikeys', element: }, + { path: '/apikeys/new', element: }, + { path: '/apikeys/created', element: }, + { path: '/apikeys/:id', element: }, + { path: '/settings', element: }, + { path: '*', element: }, + ], + }, +]); diff --git a/create-a-container/client/src/main.tsx b/create-a-container/client/src/main.tsx new file mode 100644 index 00000000..0c453e2d --- /dev/null +++ b/create-a-container/client/src/main.tsx @@ -0,0 +1,31 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { RouterProvider } from 'react-router'; +import { router } from './app/router'; +import './styles/index.css'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: (count, err) => { + const status = (err as { status?: number } | null)?.status; + if (status === 401 || status === 403 || status === 404) return false; + return count < 2; + }, + staleTime: 30_000, + refetchOnWindowFocus: false, + }, + }, +}); + +const rootEl = document.getElementById('root'); +if (!rootEl) throw new Error('Missing #root element'); + +createRoot(rootEl).render( + + + + + , +); diff --git a/create-a-container/client/src/styles/index.css b/create-a-container/client/src/styles/index.css new file mode 100644 index 00000000..b0736301 --- /dev/null +++ b/create-a-container/client/src/styles/index.css @@ -0,0 +1,11 @@ +@import 'tailwindcss'; +@import '@mieweb/ui/styles.css'; +@import '@mieweb/ui/brands/bluehive.css'; + +@custom-variant dark (&:where(.dark, .dark *)); + +html, +body, +#root { + height: 100%; +} diff --git a/create-a-container/client/src/vite-env.d.ts b/create-a-container/client/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/create-a-container/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/create-a-container/client/tsconfig.app.json b/create-a-container/client/tsconfig.app.json new file mode 100644 index 00000000..5230fdab --- /dev/null +++ b/create-a-container/client/tsconfig.app.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + + "resolveJsonModule": true, + "isolatedModules": true + }, + "include": ["src"] +} diff --git a/create-a-container/client/tsconfig.json b/create-a-container/client/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/create-a-container/client/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/create-a-container/client/tsconfig.node.json b/create-a-container/client/tsconfig.node.json new file mode 100644 index 00000000..653ea2aa --- /dev/null +++ b/create-a-container/client/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/create-a-container/client/vite.config.ts b/create-a-container/client/vite.config.ts new file mode 100644 index 00000000..baafd22a --- /dev/null +++ b/create-a-container/client/vite.config.ts @@ -0,0 +1,31 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import tailwindcss from '@tailwindcss/vite'; +import path from 'node:path'; + +const EXPRESS_TARGET = process.env.VITE_API_TARGET || 'http://localhost:3000'; + +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + port: 5173, + strictPort: true, + proxy: { + '/api': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + '/login': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + '/logout': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + '/nginx-conf': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + '/dnsmasq': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + }, + }, + build: { + outDir: 'dist', + emptyOutDir: true, + sourcemap: true, + }, +}); diff --git a/create-a-container/package.json b/create-a-container/package.json index a6368eda..01ef0026 100644 --- a/create-a-container/package.json +++ b/create-a-container/package.json @@ -1,13 +1,19 @@ { "nodemonConfig": { "ignore": [ - "certs" + "certs", + "client" ] }, "scripts": { "dev": "nodemon server.js", "db:migrate": "sequelize db:migrate && sequelize db:seed:all", - "job-runner": "node job-runner.js" + "job-runner": "node job-runner.js", + "client:install": "npm --prefix client install", + "client:dev": "npm --prefix client run dev", + "client:build": "npm --prefix client run build", + "client:preview": "npm --prefix client run preview", + "client:type-check": "npm --prefix client run type-check" }, "dependencies": { "argon2": "^0.44.0", From 7ea6d257f3092790d4d5c352c59faaa1fdcb2d27 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 09:33:29 -0700 Subject: [PATCH 02/11] feat(api): add /api/v1 JSON API + CSRF + OpenAPI spec - New middlewares/api.js: apiAuth (session OR Bearer API key), apiAdmin, asyncHandler, ApiError, jsonErrorHandler, csrfGuard. - CSRF: csrf-csrf double-submit, exempts Bearer requests. - Routers under /api/v1: auth (login w/ 2FA push, logout, register, password reset), sites, sites/:id/containers (CRUD + metadata), sites/:id/nodes (CRUD + Proxmox import + storages), external-domains, groups, users (+invite), apikeys, settings, jobs (incl. SSE stream). - openapi.v1.yaml exposed via /api/v1/openapi.{json,yaml}. - Mounted in server.js before legacy EJS routes. - Legacy /apikeys, /sites, /jobs etc. remain functional. --- create-a-container/middlewares/api.js | 154 +++++ create-a-container/openapi.v1.yaml | 449 ++++++++++++++ create-a-container/package-lock.json | 30 + create-a-container/package.json | 2 + create-a-container/routers/api/v1/apikeys.js | 89 +++ create-a-container/routers/api/v1/auth.js | 354 +++++++++++ .../routers/api/v1/containers.js | 580 ++++++++++++++++++ .../routers/api/v1/external-domains.js | 102 +++ create-a-container/routers/api/v1/groups.js | 96 +++ create-a-container/routers/api/v1/index.js | 68 ++ create-a-container/routers/api/v1/jobs.js | 118 ++++ create-a-container/routers/api/v1/nodes.js | 266 ++++++++ create-a-container/routers/api/v1/settings.js | 83 +++ create-a-container/routers/api/v1/sites.js | 111 ++++ create-a-container/routers/api/v1/users.js | 159 +++++ create-a-container/server.js | 7 +- 16 files changed, 2667 insertions(+), 1 deletion(-) create mode 100644 create-a-container/middlewares/api.js create mode 100644 create-a-container/openapi.v1.yaml create mode 100644 create-a-container/routers/api/v1/apikeys.js create mode 100644 create-a-container/routers/api/v1/auth.js create mode 100644 create-a-container/routers/api/v1/containers.js create mode 100644 create-a-container/routers/api/v1/external-domains.js create mode 100644 create-a-container/routers/api/v1/groups.js create mode 100644 create-a-container/routers/api/v1/index.js create mode 100644 create-a-container/routers/api/v1/jobs.js create mode 100644 create-a-container/routers/api/v1/nodes.js create mode 100644 create-a-container/routers/api/v1/settings.js create mode 100644 create-a-container/routers/api/v1/sites.js create mode 100644 create-a-container/routers/api/v1/users.js diff --git a/create-a-container/middlewares/api.js b/create-a-container/middlewares/api.js new file mode 100644 index 00000000..2d69c6aa --- /dev/null +++ b/create-a-container/middlewares/api.js @@ -0,0 +1,154 @@ +/** + * API v1 middleware — JSON-only auth, CSRF, error handling, request helpers. + * + * Response shape contract: + * success: { data: , meta?: { ... } } + * error: { error: { code: string, message: string, fields?: { [name]: string } } } + */ + +const { doubleCsrf } = require('csrf-csrf'); + +// --- CSRF (double-submit cookie) ---------------------------------------------------------- +// Token lives in a cookie + must echo in X-CSRF-Token (or _csrf in body). +// Skipped for Bearer-token API key requests (those are already cryptographically auth'd). +const csrfSecret = () => process.env.CSRF_SECRET || process.env.SESSION_SECRET || 'dev-csrf-secret-change-me'; + +const { + doubleCsrfProtection, + generateCsrfToken, + invalidCsrfTokenError, +} = doubleCsrf({ + getSecret: csrfSecret, + getSessionIdentifier: (req) => (req.session && req.session.id) || req.ip || 'anonymous', + cookieName: '__Host-csrf.token', + cookieOptions: { + sameSite: 'lax', + path: '/', + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + }, + size: 32, + getCsrfTokenFromRequest: (req) => + req.headers['x-csrf-token'] || (req.body && req.body._csrf), +}); + +// CSRF guard: enforce on state-changing methods, exempt Bearer-auth requests. +function csrfGuard(req, res, next) { + if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') { + return next(); + } + const auth = req.get('Authorization') || ''; + if (auth.startsWith('Bearer ')) return next(); + return doubleCsrfProtection(req, res, next); +} + +// --- Auth --------------------------------------------------------------------------------- +async function apiAuth(req, res, next) { + if (req.session && req.session.user) return next(); + + const authHeader = req.get('Authorization'); + if (authHeader && authHeader.startsWith('Bearer ')) { + const apiKey = authHeader.substring(7); + if (apiKey) { + const { ApiKey, User } = require('../models'); + const { extractKeyPrefix } = require('../utils/apikey'); + const keyPrefix = extractKeyPrefix(apiKey); + const apiKeys = await ApiKey.findAll({ + where: { keyPrefix }, + include: [{ model: User, as: 'user', include: [{ association: 'groups' }] }], + }); + for (const stored of apiKeys) { + if (await stored.validateKey(apiKey)) { + req.user = stored.user; + req.apiKey = stored; + req.isAdmin = stored.user.groups?.some((g) => g.isAdmin) || false; + req.session = req.session || {}; + req.session.user = stored.user.uid; + req.session.isAdmin = req.isAdmin; + stored.recordUsage().catch((err) => + console.error('Failed to record API key usage:', err), + ); + return next(); + } + } + } + } + + return res.status(401).json({ error: { code: 'unauthorized', message: 'Authentication required' } }); +} + +function apiAdmin(req, res, next) { + if (req.session && req.session.isAdmin) return next(); + return res.status(403).json({ error: { code: 'forbidden', message: 'Admin access required' } }); +} + +// --- Helpers ------------------------------------------------------------------------------ +function asyncHandler(fn) { + return (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); +} + +function ok(res, data, meta) { + return res.json(meta ? { data, meta } : { data }); +} + +function created(res, data) { + return res.status(201).json({ data }); +} + +function noContent(res) { + return res.status(204).end(); +} + +function fail(res, status, code, message, fields) { + return res.status(status).json({ error: { code, message, ...(fields ? { fields } : {}) } }); +} + +// Final JSON error handler — mount at the end of the /api/v1 stack. +function jsonErrorHandler(err, req, res, _next) { + if (err && err === invalidCsrfTokenError) { + return fail(res, 403, 'csrf_invalid', 'Invalid CSRF token'); + } + if (err && err.name === 'SequelizeValidationError') { + const fields = {}; + for (const ve of err.errors || []) { + if (ve.path) fields[ve.path] = ve.message; + } + return fail(res, 422, 'validation_failed', 'Validation failed', fields); + } + if (err && err.name === 'SequelizeUniqueConstraintError') { + const fields = {}; + for (const ve of err.errors || []) { + if (ve.path) fields[ve.path] = `${ve.path} already exists`; + } + return fail(res, 409, 'conflict', 'Resource already exists', fields); + } + // Plain Error with .status — let route handlers throw with shape { status, code, message, fields? } + if (err && err.status && Number.isInteger(err.status)) { + return fail(res, err.status, err.code || 'error', err.message || 'Request failed', err.fields); + } + console.error('[api/v1] Unhandled error:', err); + return fail(res, 500, 'internal_error', 'Internal server error'); +} + +class ApiError extends Error { + constructor(status, code, message, fields) { + super(message); + this.status = status; + this.code = code; + if (fields) this.fields = fields; + } +} + +module.exports = { + apiAuth, + apiAdmin, + csrfGuard, + generateCsrfToken, + asyncHandler, + ok, + created, + noContent, + fail, + jsonErrorHandler, + ApiError, +}; diff --git a/create-a-container/openapi.v1.yaml b/create-a-container/openapi.v1.yaml new file mode 100644 index 00000000..f871f1b2 --- /dev/null +++ b/create-a-container/openapi.v1.yaml @@ -0,0 +1,449 @@ +openapi: 3.0.3 +info: + title: Create-a-Container API v1 + description: | + JSON-only REST API for the React SPA and external automation. + Co-exists with the legacy `/` (HTML) and `/apikeys` (mixed) routes during the React migration. + + ## Authentication + + Two mechanisms are supported: + + - **Session cookie** — established by `POST /api/v1/auth/login`. Requires a CSRF token (see below) on every state-changing request. + - **API key** — `Authorization: Bearer `. CSRF is skipped for Bearer-authenticated requests. + + ## CSRF (session auth only) + + 1. `GET /api/v1/csrf-token` → returns `{ "data": { "csrfToken": "..." } }` and sets `__Host-csrf.token` cookie. + 2. Send the token in the `X-CSRF-Token` header on every `POST`/`PUT`/`PATCH`/`DELETE`. + + ## Response envelope + + Success: `{ "data": , "meta"?: {...} }`. + Error: `{ "error": { "code": "...", "message": "...", "fields"?: {...} } }`. + version: 1.0.0 +servers: + - url: /api/v1 + +tags: + - name: Auth + - name: Sites + - name: Containers + - name: Nodes + - name: Jobs + - name: External Domains + - name: Users + - name: Groups + - name: API Keys + - name: Settings + - name: Meta + +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + SessionCookie: + type: apiKey + in: cookie + name: connect.sid + headers: + XCSRFToken: + description: CSRF token returned by GET /csrf-token. Required for state-changing requests under session auth. + schema: { type: string } + schemas: + Error: + type: object + required: [error] + properties: + error: + type: object + required: [code, message] + properties: + code: { type: string } + message: { type: string } + fields: + type: object + additionalProperties: { type: string } + DataWrap: + type: object + required: [data] + properties: + data: {} + Site: + type: object + properties: + id: { type: integer } + name: { type: string } + internalDomain: { type: string } + dhcpRange: { type: string } + subnetMask: { type: string } + gateway: { type: string } + dnsForwarders: { type: string } + externalIp: { type: string, nullable: true } + Container: + type: object + properties: + id: { type: integer } + hostname: { type: string } + containerId: { type: integer, nullable: true } + status: { type: string } + template: { type: string } + ipv4Address: { type: string, nullable: true } + macAddress: { type: string, nullable: true } + creationJobId: { type: integer, nullable: true } + entrypoint: { type: string, nullable: true } + environmentVars: + type: object + additionalProperties: { type: string } + nvidiaRequested: { type: boolean } + sshPort: { type: integer, nullable: true } + nodeName: { type: string, nullable: true } + services: + type: array + items: { type: object } + Node: + type: object + properties: + id: { type: integer } + name: { type: string } + siteId: { type: integer } + ipv4Address: { type: string, nullable: true } + apiUrl: { type: string, nullable: true } + tokenId: { type: string, nullable: true } + tlsVerify: { type: boolean, nullable: true } + imageStorage: { type: string } + volumeStorage: { type: string } + networkBridge: { type: string } + nvidiaAvailable: { type: boolean } + hasSecret: { type: boolean } + Job: + type: object + properties: + id: { type: integer } + command: { type: string } + status: { type: string, enum: [pending, running, completed, failed] } + createdBy: { type: string } + createdAt: { type: string, format: date-time } + updatedAt: { type: string, format: date-time } + ApiKey: + type: object + properties: + id: { type: integer } + keyPrefix: { type: string } + description: { type: string, nullable: true } + lastUsedAt: { type: string, format: date-time, nullable: true } + createdAt: { type: string, format: date-time } + +security: + - BearerAuth: [] + - SessionCookie: [] + +paths: + /csrf-token: + get: + tags: [Meta] + summary: Get a CSRF token + security: [] + responses: + '200': + description: Token returned + /health: + get: + tags: [Meta] + summary: Health check + security: [] + responses: + '200': { description: OK } + + /session: + get: + tags: [Auth] + summary: Current session + responses: + '200': { description: Session payload } + '401': { description: Not authenticated, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } } + + /auth/login: + post: + tags: [Auth] + summary: Username/password login (may require 2FA) + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [username, password] + properties: + username: { type: string } + password: { type: string, format: password } + redirect: { type: string } + responses: + '200': { description: Logged in OR 2FA challenge issued } + '401': { description: Invalid credentials } + /auth/login/challenge/{id}: + get: + tags: [Auth] + summary: Poll 2FA challenge status + security: [] + parameters: + - in: path + name: id + required: true + schema: { type: string } + responses: + '200': { description: status -> pending|approved|rejected|timeout|failed|unregistered } + /auth/logout: + post: + tags: [Auth] + responses: { '200': { description: Logged out } } + /auth/register: + post: + tags: [Auth] + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [uid, givenName, sn, mail, userPassword] + properties: + uid: { type: string } + givenName: { type: string } + sn: { type: string } + mail: { type: string, format: email } + userPassword: { type: string, format: password } + inviteToken: { type: string } + responses: + '201': { description: Account created } + /auth/register/invite/{token}: + get: + tags: [Auth] + security: [] + parameters: [{ in: path, name: token, required: true, schema: { type: string } }] + responses: { '200': { description: Invite valid, returns email } } + /auth/register/2fa-qr/{token}: + get: + tags: [Auth] + security: [] + parameters: [{ in: path, name: token, required: true, schema: { type: string } }] + responses: { '200': { description: QR code data URI for push-notification enrollment } } + /auth/password-reset/request: + post: + tags: [Auth] + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [usernameOrEmail] + properties: + usernameOrEmail: { type: string } + responses: { '200': { description: Generic OK (does not reveal account existence) } } + /auth/password-reset/{token}: + get: + tags: [Auth] + security: [] + parameters: [{ in: path, name: token, required: true, schema: { type: string } }] + responses: + '200': { description: Token valid } + '404': { description: Invalid or expired } + post: + tags: [Auth] + security: [] + parameters: [{ in: path, name: token, required: true, schema: { type: string } }] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [password, confirmPassword] + properties: + password: { type: string } + confirmPassword: { type: string } + responses: { '200': { description: Password reset } } + + /sites: + get: { tags: [Sites], summary: List sites, responses: { '200': { description: Array of sites } } } + post: + tags: [Sites] + summary: Create a site (admin) + responses: { '201': { description: Created } } + /sites/{id}: + get: { tags: [Sites], parameters: [{ in: path, name: id, required: true, schema: { type: integer } }], responses: { '200': { description: Site } } } + put: { tags: [Sites], parameters: [{ in: path, name: id, required: true, schema: { type: integer } }], responses: { '200': { description: Updated } } } + delete: { tags: [Sites], parameters: [{ in: path, name: id, required: true, schema: { type: integer } }], responses: { '204': { description: Deleted } } } + + /sites/{siteId}/containers: + get: + tags: [Containers] + parameters: + - { in: path, name: siteId, required: true, schema: { type: integer } } + - { in: query, name: hostname, schema: { type: string } } + responses: { '200': { description: Array of containers } } + post: + tags: [Containers] + parameters: [{ in: path, name: siteId, required: true, schema: { type: integer } }] + requestBody: + content: + application/json: + schema: + type: object + required: [hostname, template] + properties: + hostname: { type: string } + template: { type: string } + customTemplate: { type: string } + entrypoint: { type: string } + nvidiaRequested: { type: boolean } + environmentVars: + type: array + items: { type: object, properties: { key: { type: string }, value: { type: string } } } + services: + type: object + additionalProperties: { type: object } + responses: { '201': { description: Creation job enqueued } } + /sites/{siteId}/containers/new: + get: + tags: [Containers] + summary: Form bootstrap data (domains + NVIDIA flag) + parameters: [{ in: path, name: siteId, required: true, schema: { type: integer } }] + responses: { '200': { description: Bootstrap payload } } + /sites/{siteId}/containers/metadata: + get: + tags: [Containers] + summary: Fetch Docker image metadata + parameters: + - { in: path, name: siteId, required: true, schema: { type: integer } } + - { in: query, name: image, required: true, schema: { type: string } } + responses: + '200': { description: Image metadata } + '404': { description: Image not found } + /sites/{siteId}/containers/{id}: + parameters: + - { in: path, name: siteId, required: true, schema: { type: integer } } + - { in: path, name: id, required: true, schema: { type: integer } } + get: { tags: [Containers], responses: { '200': { description: Container } } } + put: { tags: [Containers], responses: { '200': { description: Updated, optional restart job } } } + delete: { tags: [Containers], responses: { '200': { description: Deleted, with DNS cleanup warnings } } } + + /sites/{siteId}/nodes: + parameters: [{ in: path, name: siteId, required: true, schema: { type: integer } }] + get: { tags: [Nodes], responses: { '200': { description: Array of nodes } } } + post: { tags: [Nodes], responses: { '201': { description: Created (admin) } } } + /sites/{siteId}/nodes/import: + post: + tags: [Nodes] + summary: Bulk-import nodes + containers from a Proxmox cluster (admin) + parameters: [{ in: path, name: siteId, required: true, schema: { type: integer } }] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [apiUrl, username, password] + properties: + apiUrl: { type: string, format: uri } + username: { type: string } + password: { type: string } + tlsVerify: { type: boolean } + responses: { '201': { description: Imported } } + /sites/{siteId}/nodes/{id}: + parameters: + - { in: path, name: siteId, required: true, schema: { type: integer } } + - { in: path, name: id, required: true, schema: { type: integer } } + get: { tags: [Nodes], responses: { '200': { description: Node } } } + put: { tags: [Nodes], responses: { '200': { description: Updated (admin) } } } + delete: { tags: [Nodes], responses: { '204': { description: Deleted (admin) } } } + /sites/{siteId}/nodes/{id}/storages: + get: + tags: [Nodes] + parameters: + - { in: path, name: siteId, required: true, schema: { type: integer } } + - { in: path, name: id, required: true, schema: { type: integer } } + responses: { '200': { description: Array of Proxmox CT template storages } } + + /jobs/{id}: + parameters: [{ in: path, name: id, required: true, schema: { type: integer } }] + get: { tags: [Jobs], responses: { '200': { description: Job metadata } } } + /jobs/{id}/status: + get: + tags: [Jobs] + parameters: + - { in: path, name: id, required: true, schema: { type: integer } } + - { in: query, name: offset, schema: { type: integer } } + - { in: query, name: limit, schema: { type: integer } } + responses: { '200': { description: Status rows } } + /jobs/{id}/stream: + get: + tags: [Jobs] + summary: Server-sent events stream of job output + parameters: + - { in: path, name: id, required: true, schema: { type: integer } } + - { in: query, name: lastId, schema: { type: integer } } + responses: + '200': + description: SSE stream — events `log` and `status` + content: { text/event-stream: {} } + + /external-domains: + get: { tags: [External Domains], responses: { '200': { description: List } } } + post: { tags: [External Domains], responses: { '201': { description: Created (admin) } } } + /external-domains/{id}: + parameters: [{ in: path, name: id, required: true, schema: { type: integer } }] + get: { tags: [External Domains], responses: { '200': { description: Item } } } + put: { tags: [External Domains], responses: { '200': { description: Updated (admin) } } } + delete: { tags: [External Domains], responses: { '204': { description: Deleted (admin) } } } + + /users: + get: { tags: [Users], responses: { '200': { description: List (admin) } } } + post: { tags: [Users], responses: { '201': { description: Created (admin) } } } + /users/invite: + post: + tags: [Users] + summary: Send an email invitation (admin) + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email] + properties: { email: { type: string, format: email } } + responses: { '200': { description: Invitation sent } } + /users/{uidNumber}: + parameters: [{ in: path, name: uidNumber, required: true, schema: { type: integer } }] + get: { tags: [Users], responses: { '200': { description: User } } } + put: { tags: [Users], responses: { '200': { description: Updated (admin) } } } + delete: { tags: [Users], responses: { '204': { description: Deleted (admin) } } } + + /groups: + get: { tags: [Groups], responses: { '200': { description: List (admin) } } } + post: { tags: [Groups], responses: { '201': { description: Created (admin) } } } + /groups/{id}: + parameters: [{ in: path, name: id, required: true, schema: { type: integer } }] + get: { tags: [Groups], responses: { '200': { description: Group } } } + put: { tags: [Groups], responses: { '200': { description: Updated (admin) } } } + delete: { tags: [Groups], responses: { '204': { description: Deleted (admin) } } } + + /apikeys: + get: { tags: [API Keys], responses: { '200': { description: List of current user's keys } } } + post: + tags: [API Keys] + summary: Mint a new API key (plaintext returned ONCE) + responses: { '201': { description: Created with plaintext `key` } } + /apikeys/{id}: + parameters: [{ in: path, name: id, required: true, schema: { type: integer } }] + get: { tags: [API Keys], responses: { '200': { description: Key metadata } } } + delete: { tags: [API Keys], responses: { '204': { description: Revoked } } } + + /settings: + get: { tags: [Settings], responses: { '200': { description: System settings (admin) } } } + put: { tags: [Settings], responses: { '200': { description: Saved (admin) } } } diff --git a/create-a-container/package-lock.json b/create-a-container/package-lock.json index 9b8eb667..0ff47d8b 100644 --- a/create-a-container/package-lock.json +++ b/create-a-container/package-lock.json @@ -8,6 +8,8 @@ "argon2": "^0.44.0", "axios": "^1.15.2", "connect-flash": "^0.1.1", + "cookie-parser": "^1.4.7", + "csrf-csrf": "^4.0.3", "dotenv": "^17.2.3", "ejs": "^3.1.10", "express": "^5.2.1", @@ -581,6 +583,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -619,6 +640,15 @@ "node": ">= 8" } }, + "node_modules/csrf-csrf": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/csrf-csrf/-/csrf-csrf-4.0.3.tgz", + "integrity": "sha512-DaygOzelL4Qo1pHwI9LPyZL+X2456/OzpT596kNeZGiTSqKVDOk/9PPJ+FjzZacjMUEusOHw3WJKe1RW4iUhrw==", + "license": "ISC", + "dependencies": { + "http-errors": "^2.0.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", diff --git a/create-a-container/package.json b/create-a-container/package.json index 01ef0026..e882f215 100644 --- a/create-a-container/package.json +++ b/create-a-container/package.json @@ -19,6 +19,8 @@ "argon2": "^0.44.0", "axios": "^1.15.2", "connect-flash": "^0.1.1", + "cookie-parser": "^1.4.7", + "csrf-csrf": "^4.0.3", "dotenv": "^17.2.3", "ejs": "^3.1.10", "express": "^5.2.1", diff --git a/create-a-container/routers/api/v1/apikeys.js b/create-a-container/routers/api/v1/apikeys.js new file mode 100644 index 00000000..710251f5 --- /dev/null +++ b/create-a-container/routers/api/v1/apikeys.js @@ -0,0 +1,89 @@ +/** + * /api/v1/apikeys — per-user API keys. The plaintext key is returned ONCE at create time. + */ + +const express = require('express'); +const { ApiKey, User } = require('../../../models'); +const { createApiKeyData } = require('../../../utils/apikey'); +const { apiAuth, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth); + +async function currentUser(req) { + const user = await User.findOne({ where: { uid: req.session.user } }); + if (!user) throw new ApiError(401, 'unauthorized', 'Session user not found'); + return user; +} + +function serialize(k) { + return { + id: k.id, + keyPrefix: k.keyPrefix, + description: k.description, + lastUsedAt: k.lastUsedAt, + createdAt: k.createdAt, + updatedAt: k.updatedAt, + }; +} + +router.get( + '/', + asyncHandler(async (req, res) => { + const user = await currentUser(req); + const keys = await ApiKey.findAll({ + where: { uidNumber: user.uidNumber }, + order: [['createdAt', 'DESC']], + attributes: ['id', 'keyPrefix', 'description', 'lastUsedAt', 'createdAt', 'updatedAt'], + }); + return ok(res, keys.map(serialize)); + }), +); + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const user = await currentUser(req); + const key = await ApiKey.findOne({ + where: { id: req.params.id, uidNumber: user.uidNumber }, + attributes: ['id', 'keyPrefix', 'description', 'lastUsedAt', 'createdAt', 'updatedAt'], + }); + if (!key) throw new ApiError(404, 'not_found', 'API key not found'); + return ok(res, serialize(key)); + }), +); + +router.post( + '/', + asyncHandler(async (req, res) => { + const user = await currentUser(req); + const { description } = req.body || {}; + const data = await createApiKeyData(user.uidNumber, description); + const key = await ApiKey.create({ + uidNumber: data.uidNumber, + keyPrefix: data.keyPrefix, + keyHash: data.keyHash, + description: data.description, + }); + return created(res, { + ...serialize(key), + key: data.plainKey, + warning: 'This is the only time the full API key will be displayed. Store it securely.', + }); + }), +); + +router.delete( + '/:id', + asyncHandler(async (req, res) => { + const user = await currentUser(req); + const key = await ApiKey.findOne({ where: { id: req.params.id, uidNumber: user.uidNumber } }); + if (!key) throw new ApiError(404, 'not_found', 'API key not found'); + await key.destroy(); + return noContent(res); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/auth.js b/create-a-container/routers/api/v1/auth.js new file mode 100644 index 00000000..3bc5c288 --- /dev/null +++ b/create-a-container/routers/api/v1/auth.js @@ -0,0 +1,354 @@ +/** + * /api/v1/auth — login, logout, register, password reset, 2FA polling. + * + * Login flow: + * 1. POST /login { username, password } + * → 200 { data: { user, isAdmin } } // no 2FA configured, logged in + * → 200 { data: { challengeId, requires2FA: true } } // push 2FA enqueued + * → 401 { error } // bad credentials + * 2. GET /login/challenge/:id (poll) + * → 200 { data: { status: 'pending' } } + * → 200 { data: { status: 'approved', user, isAdmin } } (session now active) + * → 200 { data: { status: 'rejected' | 'timeout' | 'failed', message } } + */ + +const express = require('express'); +const QRCode = require('qrcode'); +const { Op } = require('sequelize'); +const { + User, + Setting, + ExternalDomain, + PasswordResetToken, + InviteToken, +} = require('../../../models'); +const { sendPasswordResetEmail } = require('../../../utils/email'); +const { sendPushNotificationInvite } = require('../../../utils/push-notification-invite'); +const { isSafeRedirectUrl } = require('../../../utils'); +const { asyncHandler, ok, created, ApiError } = require('../../../middlewares/api'); + +const router = express.Router(); + +// In-memory challenge store for 2FA flows. +// Keyed by challengeId; values expire after 5 minutes. +const challenges = new Map(); +const CHALLENGE_TTL_MS = 5 * 60 * 1000; +function newChallengeId() { + return require('crypto').randomBytes(16).toString('hex'); +} +function setChallenge(id, value) { + challenges.set(id, value); + setTimeout(() => challenges.delete(id), CHALLENGE_TTL_MS).unref?.(); +} + +async function safeRedirectUrl(redirect) { + let url = redirect || '/'; + const domains = await ExternalDomain.findAll({ attributes: ['name'] }); + const allowed = domains.map((d) => d.name); + return isSafeRedirectUrl(url, allowed) ? url : '/'; +} + +async function activateSession(req, user) { + req.session.user = user.uid; + req.session.isAdmin = user.groups?.some((g) => g.isAdmin) || false; + await new Promise((resolve, reject) => + req.session.save((err) => (err ? reject(err) : resolve())), + ); +} + +// POST /api/v1/auth/login +router.post( + '/login', + asyncHandler(async (req, res) => { + const { username, password, redirect } = req.body || {}; + if (!username || !password) { + throw new ApiError(400, 'invalid_request', 'username and password are required'); + } + + const user = await User.findOne({ + where: { uid: username }, + include: [{ association: 'groups' }], + }); + if (!user || !(await user.validatePassword(password))) { + throw new ApiError(401, 'invalid_credentials', 'Invalid username or password'); + } + if (user.status !== 'active') { + throw new ApiError(403, 'account_inactive', 'Account is not active. Contact an administrator.'); + } + + const settings = await Setting.getMultiple([ + 'push_notification_url', + 'push_notification_enabled', + ]); + const pushEnabled = + settings.push_notification_enabled === 'true' && + (settings.push_notification_url || '').trim() !== ''; + + const safeRedirect = await safeRedirectUrl(redirect); + + if (!pushEnabled) { + await activateSession(req, user); + return ok(res, { + user: user.uid, + isAdmin: req.session.isAdmin, + redirect: safeRedirect, + }); + } + + // 2FA push challenge — start it in the background; client polls /login/challenge/:id. + const challengeId = newChallengeId(); + setChallenge(challengeId, { status: 'pending', userId: user.uidNumber, redirect: safeRedirect }); + + (async () => { + try { + const payload = { + username: user.uid, + title: 'Authentication Request', + body: 'Please review and respond to your pending authentication request.', + actions: [ + { icon: 'approve', title: 'Approve', callback: 'approve' }, + { icon: 'reject', title: 'Reject', callback: 'reject' }, + ], + }; + const response = await fetch(`${settings.push_notification_url}/send-notification`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + const result = await response.json().catch(() => ({})); + + if ( + result.success === false && + (result.error?.includes('No device found with this Username') || + result.error?.includes('User not found')) + ) { + setChallenge(challengeId, { + status: 'unregistered', + registrationUrl: settings.push_notification_url, + }); + return; + } + if (!response.ok) { + setChallenge(challengeId, { status: 'failed', message: 'Push notification send failed' }); + return; + } + if (result.action === 'approve') { + setChallenge(challengeId, { + status: 'approved', + userId: user.uidNumber, + redirect: safeRedirect, + }); + } else if (result.action === 'reject') { + setChallenge(challengeId, { status: 'rejected', message: 'Second factor denied' }); + } else if (result.action === 'timeout') { + setChallenge(challengeId, { status: 'timeout', message: 'Second factor timed out' }); + } else { + setChallenge(challengeId, { + status: 'failed', + message: `Second factor failed: ${result.action || 'unknown'}`, + }); + } + } catch (err) { + console.error('2FA push error:', err); + setChallenge(challengeId, { status: 'failed', message: 'Push notification error' }); + } + })(); + + return ok(res, { challengeId, requires2FA: true }); + }), +); + +// GET /api/v1/auth/login/challenge/:id +router.get( + '/login/challenge/:id', + asyncHandler(async (req, res) => { + const ch = challenges.get(req.params.id); + if (!ch) throw new ApiError(404, 'challenge_not_found', 'Challenge expired or not found'); + if (ch.status === 'approved') { + const user = await User.findByPk(ch.userId, { include: [{ association: 'groups' }] }); + if (!user) throw new ApiError(500, 'user_missing', 'User no longer exists'); + await activateSession(req, user); + challenges.delete(req.params.id); + return ok(res, { + status: 'approved', + user: user.uid, + isAdmin: req.session.isAdmin, + redirect: ch.redirect || '/', + }); + } + return ok(res, { status: ch.status, message: ch.message, registrationUrl: ch.registrationUrl }); + }), +); + +// POST /api/v1/auth/logout +router.post( + '/logout', + asyncHandler(async (req, res) => { + await new Promise((resolve) => + req.session.destroy(() => { + res.clearCookie('connect.sid'); + resolve(); + }), + ); + return ok(res, { loggedOut: true }); + }), +); + +// GET /api/v1/auth/register/invite/:token — pre-fill data for invitation token +router.get( + '/register/invite/:token', + asyncHandler(async (req, res) => { + const invite = await InviteToken.validateToken(req.params.token); + if (!invite) throw new ApiError(404, 'invalid_invite', 'Invalid or expired invitation'); + return ok(res, { email: invite.email }); + }), +); + +// POST /api/v1/auth/register +router.post( + '/register', + asyncHandler(async (req, res) => { + const { uid, givenName: rawGiven, sn: rawSn, mail, userPassword, inviteToken } = req.body || {}; + if (!uid || !rawGiven || !rawSn || !mail || !userPassword) { + throw new ApiError(400, 'invalid_request', 'All registration fields are required'); + } + + let isInvitedUser = false; + let validatedInvite = null; + if (inviteToken) { + validatedInvite = await InviteToken.validateToken(inviteToken); + if (!validatedInvite) { + throw new ApiError(400, 'invalid_invite', 'Invalid or expired invitation link'); + } + if (mail.toLowerCase().trim() !== validatedInvite.email) { + throw new ApiError(400, 'email_mismatch', 'Email does not match the invitation'); + } + isInvitedUser = true; + } + + let status; + if ((await User.count()) === 0) status = 'active'; + else if (isInvitedUser) status = 'active'; + else status = 'pending'; + + const givenName = rawGiven.trim(); + const sn = rawSn.trim(); + const userParams = { + uidNumber: await User.nextUidNumber(), + uid, + sn, + givenName, + mail, + userPassword, + status, + cn: `${givenName} ${sn}`, + homeDirectory: `/home/${uid}`, + }; + + await User.create(userParams); + if (validatedInvite) await validatedInvite.markAsUsed(); + + let twoFactor = null; + if (isInvitedUser) { + const inviteResult = await sendPushNotificationInvite(userParams); + if (inviteResult?.success && inviteResult.inviteUrl) { + try { + const parsed = new URL(inviteResult.inviteUrl); + if (parsed.protocol === 'https:' || parsed.protocol === 'http:') { + const tk = parsed.searchParams.get('token'); + if (tk) twoFactor = { enrollmentToken: tk }; + } + } catch { + /* invalid URL */ + } + } else if (inviteResult?.error) { + twoFactor = { warning: inviteResult.error }; + } + } + return created(res, { + uid, + status, + message: isInvitedUser + ? 'Account created. You can now log in.' + : 'Account registered. You will be notified once approved.', + ...(twoFactor ? { twoFactor } : {}), + }); + }), +); + +// GET /api/v1/auth/register/2fa-qr/:token — produces a QR code for the push-notification enrollment URL +router.get( + '/register/2fa-qr/:token', + asyncHandler(async (req, res) => { + const notificationUrl = await Setting.get('push_notification_url'); + if (!notificationUrl?.trim()) { + throw new ApiError(404, 'push_not_configured', 'Push notifications are not configured'); + } + const url = `${notificationUrl.trim()}/register?token=${encodeURIComponent(req.params.token)}`; + const qrCodeDataUri = await QRCode.toDataURL(url, { width: 256 }); + return ok(res, { qrCodeDataUri, inviteUrl: url }); + }), +); + +// POST /api/v1/auth/password-reset/request +router.post( + '/password-reset/request', + asyncHandler(async (req, res) => { + const { usernameOrEmail } = req.body || {}; + if (!usernameOrEmail || usernameOrEmail.trim() === '') { + throw new ApiError(400, 'invalid_request', 'usernameOrEmail is required'); + } + const target = usernameOrEmail.trim(); + const user = await User.findOne({ + where: { [Op.or]: [{ uid: target }, { mail: target }] }, + }); + // Do not reveal whether the user exists. + if (user) { + const { token } = await PasswordResetToken.generateToken(user.uidNumber); + const resetUrl = `${req.protocol}://${req.get('host')}/reset-password/${token}`; + try { + await sendPasswordResetEmail(user.mail, user.uid, resetUrl); + } catch (err) { + console.error('Password reset email failed:', err); + // Fall through to generic OK to avoid disclosing detail. + } + } + return ok(res, { + message: 'If the account exists, reset instructions have been sent.', + }); + }), +); + +// GET /api/v1/auth/password-reset/:token — validate a token (returns username) +router.get( + '/password-reset/:token', + asyncHandler(async (req, res) => { + const token = await PasswordResetToken.validateToken(req.params.token); + if (!token) throw new ApiError(404, 'invalid_token', 'Invalid or expired reset link'); + return ok(res, { username: token.user.uid }); + }), +); + +// POST /api/v1/auth/password-reset/:token — set the new password +router.post( + '/password-reset/:token', + asyncHandler(async (req, res) => { + const { password, confirmPassword } = req.body || {}; + if (!password || !confirmPassword) { + throw new ApiError(400, 'invalid_request', 'password and confirmPassword are required'); + } + if (password !== confirmPassword) { + throw new ApiError(400, 'mismatch', 'Passwords do not match'); + } + if (password.length < 8) { + throw new ApiError(400, 'weak_password', 'Password must be at least 8 characters'); + } + const token = await PasswordResetToken.validateToken(req.params.token); + if (!token) throw new ApiError(404, 'invalid_token', 'Invalid or expired reset link'); + await token.user.setPassword(password); + await token.markAsUsed(); + return ok(res, { message: 'Password reset successful' }); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/containers.js b/create-a-container/routers/api/v1/containers.js new file mode 100644 index 00000000..9f6bf971 --- /dev/null +++ b/create-a-container/routers/api/v1/containers.js @@ -0,0 +1,580 @@ +/** + * /api/v1/sites/:siteId/containers — full CRUD and metadata helpers. + * Mounted with mergeParams to access :siteId from the parent /sites router. + */ + +const express = require('express'); +const { + Container, + Service, + HTTPService, + TransportService, + DnsService, + Node, + Site, + ExternalDomain, + Job, + Sequelize, + sequelize, +} = require('../../../models'); +const { parseDockerRef, getImageConfig, extractImageMetadata } = require('../../../utils/docker-registry'); +const { manageDnsRecords } = require('../../../utils/cloudflare-dns'); +const { apiAuth, asyncHandler, ok, created, ApiError } = require('../../../middlewares/api'); + +const router = express.Router({ mergeParams: true }); + +router.use(apiAuth); + +function normalizeDockerRef(ref) { + if (ref.startsWith('http://') || ref.startsWith('https://') || ref.startsWith('git@')) return ref; + let tag = 'latest'; + let imagePart = ref; + const lastColon = ref.lastIndexOf(':'); + if (lastColon !== -1) { + const potentialTag = ref.substring(lastColon + 1); + if (!potentialTag.includes('/')) { + tag = potentialTag; + imagePart = ref.substring(0, lastColon); + } + } + const parts = imagePart.split('/'); + let host = 'docker.io'; + let org = 'library'; + let image; + if (parts.length === 1) { + image = parts[0]; + } else if (parts.length === 2) { + if (parts[0].includes('.') || parts[0].includes(':')) { + host = parts[0]; + image = parts[1]; + } else { + org = parts[0]; + image = parts[1]; + } + } else { + host = parts[0]; + image = parts[parts.length - 1]; + org = parts.slice(1, -1).join('/'); + } + return `${host}/${org}/${image}:${tag}`; +} + +async function loadSite(req) { + const site = await Site.findByPk(parseInt(req.params.siteId, 10)); + if (!site) throw new ApiError(404, 'site_not_found', 'Site not found'); + return site; +} + +function serializeContainer(c, site) { + const services = c.services || []; + const ssh = services.find( + (s) => + s.type === 'transport' && + s.transportService?.protocol === 'tcp' && + Number(s.internalPort) === 22, + ); + const httpEntries = services + .filter((s) => s.type === 'http') + .map((s) => { + const host = + s.httpService?.externalHostname && s.httpService?.externalDomain?.name + ? `${s.httpService.externalHostname}.${s.httpService.externalDomain.name}` + : null; + return { port: s.internalPort, externalUrl: host ? `https://${host}` : null }; + }); + const primaryHttp = httpEntries[0] || null; + return { + id: c.id, + containerId: c.containerId, + hostname: c.hostname, + ipv4Address: c.ipv4Address, + macAddress: c.macAddress, + status: c.status, + template: c.template, + creationJobId: c.creationJobId, + entrypoint: c.entrypoint, + environmentVars: c.environmentVars ? JSON.parse(c.environmentVars) : {}, + nvidiaRequested: !!c.nvidiaRequested, + sshPort: ssh?.transportService?.externalPort || null, + sshHost: primaryHttp?.externalUrl ? new URL(primaryHttp.externalUrl).hostname : site?.externalIp, + httpEntries, + nodeName: c.node ? c.node.name : null, + services: services.map((s) => ({ + id: s.id, + type: s.type, + internalPort: s.internalPort, + httpService: s.httpService + ? { + id: s.httpService.id, + externalHostname: s.httpService.externalHostname, + externalDomainId: s.httpService.externalDomainId, + backendProtocol: s.httpService.backendProtocol, + authRequired: s.httpService.authRequired, + domain: s.httpService.externalDomain?.name, + } + : null, + transportService: s.transportService + ? { + id: s.transportService.id, + protocol: s.transportService.protocol, + externalPort: s.transportService.externalPort, + } + : null, + dnsService: s.dnsService + ? { id: s.dnsService.id, recordType: s.dnsService.recordType, dnsName: s.dnsService.dnsName } + : null, + })), + createdAt: c.createdAt, + }; +} + +// GET /containers/metadata?image=... +router.get( + '/metadata', + asyncHandler(async (req, res) => { + const { image } = req.query; + if (!image || !image.trim()) throw new ApiError(400, 'invalid_request', 'image is required'); + const normalized = normalizeDockerRef(image.trim()); + const parsed = parseDockerRef(normalized); + try { + const config = await getImageConfig(parsed.registry, `${parsed.namespace}/${parsed.image}`, parsed.tag); + return ok(res, extractImageMetadata(config)); + } catch (err) { + if (err.message.includes('HTTP 404')) { + throw new ApiError(404, 'image_not_found', 'Image not found in registry'); + } + throw new ApiError(502, 'registry_error', err.message); + } + }), +); + +// GET /containers/new — bootstrap data for the create form +router.get( + '/new', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const externalDomains = await site.getSortedExternalDomains(); + const nvidiaAvailable = + (await Node.count({ where: { siteId: site.id, nvidiaAvailable: true } })) > 0; + return ok(res, { siteId: site.id, externalDomains, nvidiaAvailable }); + }), +); + +// GET /containers +router.get( + '/', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const nodes = await Node.findAll({ where: { siteId: site.id }, attributes: ['id'] }); + const nodeIds = nodes.map((n) => n.id); + const where = { username: req.session.user, nodeId: nodeIds }; + if (req.query.hostname) where.hostname = req.query.hostname; + const rows = await Container.findAll({ + where, + include: [ + { + association: 'services', + include: [ + { association: 'httpService', include: [{ association: 'externalDomain' }] }, + { association: 'transportService' }, + { association: 'dnsService' }, + ], + }, + { association: 'node', attributes: ['id', 'name', 'apiUrl'] }, + ], + }); + return ok(res, rows.map((c) => serializeContainer(c, site))); + }), +); + +// GET /containers/:id +router.get( + '/:id', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const c = await Container.findOne({ + where: { id: parseInt(req.params.id, 10), username: req.session.user }, + include: [ + { + association: 'services', + include: [ + { association: 'httpService', include: [{ association: 'externalDomain' }] }, + { association: 'transportService' }, + { association: 'dnsService' }, + ], + }, + { association: 'node' }, + ], + }); + if (!c || !c.node || c.node.siteId !== site.id) { + throw new ApiError(404, 'not_found', 'Container not found'); + } + return ok(res, serializeContainer(c, site)); + }), +); + +// POST /containers — create + service rows + creation job (single transaction) +router.post( + '/', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const t = await sequelize.transaction(); + try { + let { + hostname, + template, + customTemplate, + services, + environmentVars, + entrypoint, + nvidiaRequested, + } = req.body || {}; + + if (!hostname || !hostname.trim()) throw new ApiError(400, 'invalid_request', 'hostname is required'); + + const wantsNvidia = !!nvidiaRequested; + let envVarsJson = null; + if (Array.isArray(environmentVars) && environmentVars.length > 0) { + const envObj = {}; + for (const e of environmentVars) { + if (e?.key && e.key.trim()) envObj[e.key.trim()] = e.value || ''; + } + if (Object.keys(envObj).length > 0) envVarsJson = JSON.stringify(envObj); + } + if (wantsNvidia) { + const obj = envVarsJson ? JSON.parse(envVarsJson) : {}; + if (!obj.NVIDIA_VISIBLE_DEVICES) obj.NVIDIA_VISIBLE_DEVICES = 'all'; + if (!obj.NVIDIA_DRIVER_CAPABILITIES) obj.NVIDIA_DRIVER_CAPABILITIES = 'utility compute'; + envVarsJson = JSON.stringify(obj); + } + + const imageRef = template === 'custom' ? customTemplate?.trim() : template; + if (!imageRef) throw new ApiError(400, 'invalid_request', 'template is required'); + const templateName = normalizeDockerRef(imageRef); + + const nodeWhere = { + siteId: site.id, + apiUrl: { [Sequelize.Op.ne]: null }, + tokenId: { [Sequelize.Op.ne]: null }, + secret: { [Sequelize.Op.ne]: null }, + }; + if (wantsNvidia) nodeWhere.nvidiaAvailable = true; + const node = await Node.findOne({ + where: nodeWhere, + include: [{ model: Container, as: 'containers', attributes: [], required: false }], + attributes: { + include: [[Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'containerCount']], + }, + group: ['Node.id'], + order: [[Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'ASC']], + subQuery: false, + }); + if (!node && wantsNvidia) { + throw new ApiError(409, 'no_nvidia_node', 'No NVIDIA-capable nodes available in this site'); + } + if (!node) throw new ApiError(409, 'no_node', 'No nodes with API access available in this site'); + + const container = await Container.create( + { + hostname, + username: req.session.user, + status: 'pending', + template: templateName, + nodeId: node.id, + siteId: site.id, + containerId: null, + macAddress: null, + ipv4Address: null, + nvidiaRequested: wantsNvidia, + environmentVars: envVarsJson, + entrypoint: entrypoint && entrypoint.trim() ? entrypoint.trim() : null, + }, + { transaction: t }, + ); + + if (services && typeof services === 'object') { + for (const key in services) { + const svc = services[key]; + const { type, internalPort, externalHostname, externalDomainId, dnsName, authRequired } = svc; + if (!type || !internalPort) continue; + let serviceType; + let protocol = null; + if (type === 'http' || type === 'https') serviceType = 'http'; + else if (type === 'srv') serviceType = 'dns'; + else { + serviceType = 'transport'; + protocol = type; + } + const createdService = await Service.create( + { containerId: container.id, type: serviceType, internalPort: parseInt(internalPort, 10) }, + { transaction: t }, + ); + if (serviceType === 'http') { + if (!externalHostname || !externalDomainId) { + throw new ApiError(400, 'invalid_service', 'HTTP services must have an externalHostname and externalDomainId'); + } + await HTTPService.create( + { + serviceId: createdService.id, + externalHostname, + externalDomainId: parseInt(externalDomainId, 10), + backendProtocol: type === 'https' ? 'https' : 'http', + authRequired: authRequired === true || authRequired === 'true', + }, + { transaction: t }, + ); + } else if (serviceType === 'dns') { + if (!dnsName) throw new ApiError(400, 'invalid_service', 'DNS services must have a dnsName'); + await DnsService.create( + { serviceId: createdService.id, recordType: 'SRV', dnsName }, + { transaction: t }, + ); + } else { + const externalPort = await TransportService.nextAvailablePortInRange(protocol, 2000, 65565, t); + await TransportService.create( + { serviceId: createdService.id, protocol, externalPort }, + { transaction: t }, + ); + } + } + } + + const job = await Job.create( + { + command: `node bin/create-container.js --container-id=${container.id}`, + createdBy: req.session.user, + status: 'pending', + }, + { transaction: t }, + ); + await container.update({ creationJobId: job.id }, { transaction: t }); + await t.commit(); + return created(res, { + containerId: container.id, + jobId: job.id, + hostname: container.hostname, + status: 'pending', + }); + } catch (err) { + await t.rollback(); + throw err; + } + }), +); + +// PUT /containers/:id — service add/delete + env/entrypoint changes + optional restart job +router.put( + '/:id', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const container = await Container.findOne({ + where: { id: parseInt(req.params.id, 10), username: req.session.user }, + include: [{ model: Node, as: 'node', where: { siteId: site.id } }], + }); + if (!container) throw new ApiError(404, 'not_found', 'Container not found'); + + const { services, environmentVars, entrypoint } = req.body || {}; + const forceRestart = req.body?.restart === true || req.body?.restart === 'true'; + const isRestartOnly = forceRestart && !services && !environmentVars && entrypoint === undefined; + + let envVarsJson = container.environmentVars; + if (!isRestartOnly && Array.isArray(environmentVars)) { + const obj = {}; + for (const e of environmentVars) { + if (e?.key) obj[e.key.trim()] = e.value || ''; + } + envVarsJson = Object.keys(obj).length > 0 ? JSON.stringify(obj) : null; + } else if (!isRestartOnly && !environmentVars) { + envVarsJson = null; + } + const newEntrypoint = isRestartOnly + ? container.entrypoint + : entrypoint && entrypoint.trim() + ? entrypoint.trim() + : null; + const envChanged = !isRestartOnly && container.environmentVars !== envVarsJson; + const entrypointChanged = !isRestartOnly && container.entrypoint !== newEntrypoint; + const needsRestart = forceRestart || envChanged || entrypointChanged; + + let restartJob = null; + const dnsWarnings = []; + await sequelize.transaction(async (t) => { + if (envChanged || entrypointChanged) { + await container.update( + { + environmentVars: envVarsJson, + entrypoint: newEntrypoint, + status: needsRestart && container.containerId ? 'restarting' : container.status, + }, + { transaction: t }, + ); + } else if (forceRestart && container.containerId) { + await container.update({ status: 'restarting' }, { transaction: t }); + } + if (needsRestart && container.containerId) { + restartJob = await Job.create( + { + command: `node bin/reconfigure-container.js --container-id=${container.id}`, + createdBy: req.session.user, + status: 'pending', + }, + { transaction: t }, + ); + } + + if (services && typeof services === 'object') { + const deletedHttp = []; + for (const key in services) { + const { id, deleted } = services[key]; + if ((deleted === true || deleted === 'true') && id) { + const svc = await Service.findByPk(parseInt(id, 10), { + include: [ + { + model: HTTPService, + as: 'httpService', + include: [{ model: ExternalDomain, as: 'externalDomain' }], + }, + ], + transaction: t, + }); + if (svc?.httpService?.externalDomain) { + deletedHttp.push({ + externalHostname: svc.httpService.externalHostname, + ExternalDomain: svc.httpService.externalDomain, + }); + } + await Service.destroy({ + where: { id: parseInt(id, 10), containerId: container.id }, + transaction: t, + }); + } + } + for (const key in services) { + const { id, deleted, authRequired } = services[key]; + if (deleted === true || deleted === 'true' || !id) continue; + const svc = await Service.findByPk(parseInt(id, 10), { + include: [{ model: HTTPService, as: 'httpService' }], + transaction: t, + }); + if (svc?.httpService) { + const next = authRequired === true || authRequired === 'true'; + if (svc.httpService.authRequired !== next) { + await svc.httpService.update({ authRequired: next }, { transaction: t }); + } + } + } + const newHttp = []; + for (const key in services) { + const { id, deleted, type, internalPort, externalHostname, externalDomainId, dnsName, authRequired } = + services[key]; + if (deleted === true || deleted === 'true' || id || !type || !internalPort) continue; + const serviceType = + type === 'srv' ? 'dns' : type === 'http' || type === 'https' ? 'http' : 'transport'; + const protocol = serviceType === 'transport' ? type : null; + const createdService = await Service.create( + { containerId: container.id, type: serviceType, internalPort: parseInt(internalPort, 10) }, + { transaction: t }, + ); + if (serviceType === 'http') { + await HTTPService.create( + { + serviceId: createdService.id, + externalHostname, + externalDomainId, + backendProtocol: type === 'https' ? 'https' : 'http', + authRequired: authRequired === true || authRequired === 'true', + }, + { transaction: t }, + ); + const domain = await ExternalDomain.findByPk(parseInt(externalDomainId, 10), { transaction: t }); + if (domain) newHttp.push({ externalHostname, ExternalDomain: domain }); + } else if (serviceType === 'dns') { + await DnsService.create( + { serviceId: createdService.id, recordType: 'SRV', dnsName }, + { transaction: t }, + ); + } else { + const externalPort = await TransportService.nextAvailablePortInRange(protocol, 2000, 65565); + await TransportService.create( + { serviceId: createdService.id, protocol, externalPort }, + { transaction: t }, + ); + } + } + if (deletedHttp.length > 0) { + dnsWarnings.push(...(await manageDnsRecords(deletedHttp, site, 'delete'))); + } + if (newHttp.length > 0) { + dnsWarnings.push(...(await manageDnsRecords(newHttp, site, 'create'))); + } + } + }); + + return ok(res, { + containerId: container.id, + jobId: restartJob ? restartJob.id : null, + dnsWarnings, + message: restartJob ? 'Container is restarting' : 'Container updated', + }); + }), +); + +// DELETE /containers/:id +router.delete( + '/:id', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const container = await Container.findOne({ + where: { id: parseInt(req.params.id, 10), username: req.session.user }, + include: [ + { model: Node, as: 'node' }, + { + model: Service, + as: 'services', + include: [ + { + model: HTTPService, + as: 'httpService', + include: [{ model: ExternalDomain, as: 'externalDomain' }], + }, + ], + }, + ], + }); + if (!container || !container.node || container.node.siteId !== site.id) { + throw new ApiError(404, 'not_found', 'Container not found'); + } + const node = container.node; + let dnsWarnings = []; + const httpServices = (container.services || []) + .filter((s) => s.httpService?.externalDomain) + .map((s) => ({ + externalHostname: s.httpService.externalHostname, + ExternalDomain: s.httpService.externalDomain, + })); + if (httpServices.length > 0) { + dnsWarnings = await manageDnsRecords(httpServices, site, 'delete'); + } + if (container.containerId && node.apiUrl && node.tokenId) { + try { + const api = await node.api(); + const config = await api.lxcConfig(node.name, container.containerId); + if (config.hostname && config.hostname !== container.hostname) { + throw new ApiError( + 409, + 'hostname_mismatch', + `Hostname mismatch (DB: ${container.hostname} vs Proxmox: ${config.hostname}). Delete aborted.`, + ); + } + await api.deleteContainer(node.name, container.containerId, true, true); + } catch (err) { + if (err instanceof ApiError) throw err; + console.log(`Proxmox deletion skipped or failed: ${err.message}`); + } + } + await container.destroy(); + return ok(res, { deleted: true, dnsWarnings }); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/external-domains.js b/create-a-container/routers/api/v1/external-domains.js new file mode 100644 index 00000000..bd1527b0 --- /dev/null +++ b/create-a-container/routers/api/v1/external-domains.js @@ -0,0 +1,102 @@ +/** + * /api/v1/external-domains — admin-only CRUD. + * The Cloudflare API key is write-only: never returned in any response. + */ + +const express = require('express'); +const { ExternalDomain, Site } = require('../../../models'); +const { apiAuth, apiAdmin, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth, apiAdmin); + +function serialize(d) { + return { + id: d.id, + name: d.name, + acmeEmail: d.acmeEmail, + acmeDirectoryUrl: d.acmeDirectoryUrl, + cloudflareApiEmail: d.cloudflareApiEmail, + siteId: d.siteId, + site: d.site ? { id: d.site.id, name: d.site.name } : null, + authServer: d.authServer, + hasCloudflareApiKey: !!d.cloudflareApiKey, + }; +} + +router.get( + '/', + asyncHandler(async (_req, res) => { + const rows = await ExternalDomain.findAll({ + include: [{ model: Site, as: 'site', attributes: ['id', 'name'], required: false }], + order: [['name', 'ASC']], + }); + return ok(res, rows.map(serialize)); + }), +); + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const d = await ExternalDomain.findByPk(req.params.id, { + include: [{ model: Site, as: 'site', attributes: ['id', 'name'], required: false }], + }); + if (!d) throw new ApiError(404, 'not_found', 'External domain not found'); + return ok(res, serialize(d)); + }), +); + +router.post( + '/', + asyncHandler(async (req, res) => { + const { name, acmeEmail, acmeDirectoryUrl, cloudflareApiEmail, cloudflareApiKey, siteId, authServer } = + req.body || {}; + const d = await ExternalDomain.create({ + name, + acmeEmail: acmeEmail || null, + acmeDirectoryUrl: acmeDirectoryUrl || null, + cloudflareApiEmail: cloudflareApiEmail || null, + cloudflareApiKey: cloudflareApiKey || null, + siteId: siteId || null, + authServer: authServer || null, + }); + return created(res, serialize(d)); + }), +); + +router.put( + '/:id', + asyncHandler(async (req, res) => { + const d = await ExternalDomain.findByPk(req.params.id); + if (!d) throw new ApiError(404, 'not_found', 'External domain not found'); + const { name, acmeEmail, acmeDirectoryUrl, cloudflareApiEmail, cloudflareApiKey, siteId, authServer } = + req.body || {}; + const update = { + name, + acmeEmail: acmeEmail || null, + acmeDirectoryUrl: acmeDirectoryUrl || null, + cloudflareApiEmail: cloudflareApiEmail || null, + siteId: siteId || null, + authServer: authServer || null, + }; + if (cloudflareApiKey && cloudflareApiKey.trim() !== '') { + update.cloudflareApiKey = cloudflareApiKey; + } + await d.update(update); + return ok(res, serialize(d)); + }), +); + +router.delete( + '/:id', + asyncHandler(async (req, res) => { + const d = await ExternalDomain.findByPk(req.params.id); + if (!d) throw new ApiError(404, 'not_found', 'External domain not found'); + await d.destroy(); + return noContent(res); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/groups.js b/create-a-container/routers/api/v1/groups.js new file mode 100644 index 00000000..4fc54371 --- /dev/null +++ b/create-a-container/routers/api/v1/groups.js @@ -0,0 +1,96 @@ +/** + * /api/v1/groups — admin-only CRUD. + */ + +const express = require('express'); +const { Group, User } = require('../../../models'); +const { apiAuth, apiAdmin, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth, apiAdmin); + +function serialize(g) { + return { + gidNumber: g.gidNumber, + cn: g.cn, + isAdmin: g.isAdmin, + userCount: g.users ? g.users.length : undefined, + }; +} + +router.get( + '/', + asyncHandler(async (_req, res) => { + const groups = await Group.findAll({ + include: [ + { + model: User, + as: 'users', + attributes: ['uidNumber', 'uid'], + through: { attributes: [] }, + }, + ], + order: [['gidNumber', 'ASC']], + }); + return ok(res, groups.map(serialize)); + }), +); + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const g = await Group.findByPk(req.params.id, { + include: [ + { + model: User, + as: 'users', + attributes: ['uidNumber', 'uid'], + through: { attributes: [] }, + }, + ], + }); + if (!g) throw new ApiError(404, 'not_found', 'Group not found'); + return ok(res, serialize(g)); + }), +); + +router.post( + '/', + asyncHandler(async (req, res) => { + const { gidNumber, cn, isAdmin } = req.body || {}; + const g = await Group.create({ + gidNumber: parseInt(gidNumber, 10), + cn, + isAdmin: isAdmin === true || isAdmin === 'true' || isAdmin === 'on', + }); + return created(res, serialize(g)); + }), +); + +router.put( + '/:id', + asyncHandler(async (req, res) => { + const g = await Group.findByPk(req.params.id); + if (!g) throw new ApiError(404, 'not_found', 'Group not found'); + const { cn, isAdmin } = req.body || {}; + await g.update({ + cn, + isAdmin: isAdmin === true || isAdmin === 'true' || isAdmin === 'on', + }); + return ok(res, serialize(g)); + }), +); + +router.delete( + '/:id', + asyncHandler(async (req, res) => { + const g = await Group.findByPk(req.params.id); + if (!g) throw new ApiError(404, 'not_found', 'Group not found'); + await g.destroy(); + return noContent(res); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/index.js b/create-a-container/routers/api/v1/index.js new file mode 100644 index 00000000..1cd6f650 --- /dev/null +++ b/create-a-container/routers/api/v1/index.js @@ -0,0 +1,68 @@ +/** + * /api/v1 mount point. JSON-only API for the React SPA and external consumers. + * Co-exists with the legacy EJS routers during the React migration. + */ + +const express = require('express'); +const path = require('path'); +const cookieParser = require('cookie-parser'); +const YAML = require('yamljs'); +const { + csrfGuard, + generateCsrfToken, + jsonErrorHandler, + apiAuth, + ok, +} = require('../../../middlewares/api'); + +const router = express.Router(); + +// OpenAPI spec (loaded once at import time) +const openapiSpec = YAML.load(path.join(__dirname, '..', '..', '..', 'openapi.v1.yaml')); + +router.use(cookieParser()); +router.use(express.json({ limit: '1mb' })); +router.use(express.urlencoded({ extended: true })); + +// Public token endpoint — clients call this before any state-changing request. +router.get('/csrf-token', (req, res) => { + const csrfToken = generateCsrfToken(req, res); + return ok(res, { csrfToken }); +}); + +// Health check (unauthenticated) +router.get('/health', (_req, res) => ok(res, { status: 'ok' })); + +// OpenAPI v1 spec (unauthenticated) +router.get('/openapi.json', (_req, res) => res.json(openapiSpec)); +router.get('/openapi.yaml', (_req, res) => { + res.type('text/yaml').sendFile(path.join(__dirname, '..', '..', '..', 'openapi.v1.yaml')); +}); + +// CSRF guard before any state-changing route below +router.use(csrfGuard); + +// Auth routes (login/register/reset are intentionally outside apiAuth) +router.use('/auth', require('./auth')); + +// Authenticated session check +router.get('/session', apiAuth, (req, res) => + ok(res, { + user: req.session.user, + isAdmin: !!req.session.isAdmin, + }), +); + +// Resource routes — each sub-router applies its own apiAuth/apiAdmin +router.use('/sites', require('./sites')); +router.use('/external-domains', require('./external-domains')); +router.use('/groups', require('./groups')); +router.use('/users', require('./users')); +router.use('/apikeys', require('./apikeys')); +router.use('/settings', require('./settings')); +router.use('/jobs', require('./jobs')); + +// Final error handler — must come after all routes +router.use(jsonErrorHandler); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/jobs.js b/create-a-container/routers/api/v1/jobs.js new file mode 100644 index 00000000..a2810fa9 --- /dev/null +++ b/create-a-container/routers/api/v1/jobs.js @@ -0,0 +1,118 @@ +/** + * /api/v1/jobs — read job metadata + status rows + live SSE stream. + * Job creation is intentionally not exposed: containers create their own jobs. + */ + +const express = require('express'); +const { Job, JobStatus, sequelize } = require('../../../models'); +const { apiAuth, asyncHandler, ok, ApiError } = require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth); + +async function authorizedJob(req) { + const id = parseInt(req.params.id, 10); + const job = await Job.findByPk(id); + if (!job) throw new ApiError(404, 'not_found', 'Job not found'); + const isAdmin = req.session && req.session.isAdmin; + if (!isAdmin && job.createdBy !== req.session.user) { + throw new ApiError(404, 'not_found', 'Job not found'); + } + return job; +} + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const job = await authorizedJob(req); + return ok(res, { + id: job.id, + command: job.command, + status: job.status, + createdAt: job.createdAt, + updatedAt: job.updatedAt, + createdBy: job.createdBy, + }); + }), +); + +router.get( + '/:id/status', + asyncHandler(async (req, res) => { + await authorizedJob(req); + const offset = req.query.offset ? Math.max(0, parseInt(req.query.offset, 10)) : 0; + const limit = req.query.limit ? Math.min(1000, parseInt(req.query.limit, 10)) : 1000; + const rows = await JobStatus.findAll({ + where: { jobId: req.params.id }, + order: [['createdAt', 'ASC']], + limit, + offset, + }); + return ok(res, rows); + }), +); + +// SSE stream — re-uses the same authorization, body matches existing /jobs/:id/stream +router.get( + '/:id/stream', + asyncHandler(async (req, res) => { + const job = await authorizedJob(req); + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + res.setHeader('X-Accel-Buffering', 'no'); + res.flushHeaders(); + + let lastId = req.query.lastId ? parseInt(req.query.lastId, 10) : 0; + let isRunning = true; + + const keepalive = setInterval(() => { + if (isRunning) res.write(':keepalive\n\n'); + }, 15000); + + const poll = setInterval(async () => { + try { + const newLogs = await JobStatus.findAll({ + where: { + jobId: job.id, + id: { [sequelize.Sequelize.Op.gt]: lastId }, + }, + order: [['id', 'ASC']], + limit: 100, + }); + for (const log of newLogs) { + res.write( + `event: log\ndata: ${JSON.stringify({ + id: log.id, + output: log.output, + timestamp: log.createdAt, + })}\n\n`, + ); + lastId = log.id; + } + const current = await Job.findByPk(job.id); + if (!current || !['pending', 'running'].includes(current.status)) { + res.write( + `event: status\ndata: ${JSON.stringify({ + status: current ? current.status : 'unknown', + })}\n\n`, + ); + cleanup(); + res.end(); + } + } catch (err) { + console.error('SSE poll error:', err); + } + }, 2000); + + function cleanup() { + isRunning = false; + clearInterval(keepalive); + clearInterval(poll); + } + req.on('close', cleanup); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/nodes.js b/create-a-container/routers/api/v1/nodes.js new file mode 100644 index 00000000..d01e097d --- /dev/null +++ b/create-a-container/routers/api/v1/nodes.js @@ -0,0 +1,266 @@ +/** + * /api/v1/sites/:siteId/nodes — admin-only CRUD + Proxmox storages + Proxmox import. + */ + +const express = require('express'); +const https = require('https'); +const { Node, Site, Container } = require('../../../models'); +const { apiAuth, apiAdmin, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router({ mergeParams: true }); + +router.use(apiAuth); + +function serialize(n) { + return { + id: n.id, + name: n.name, + siteId: n.siteId, + ipv4Address: n.ipv4Address, + apiUrl: n.apiUrl, + tokenId: n.tokenId, + tlsVerify: n.tlsVerify, + imageStorage: n.imageStorage, + volumeStorage: n.volumeStorage, + networkBridge: n.networkBridge, + nvidiaAvailable: n.nvidiaAvailable, + hasSecret: !!n.secret, + }; +} + +async function loadSite(req) { + const site = await Site.findByPk(parseInt(req.params.siteId, 10)); + if (!site) throw new ApiError(404, 'site_not_found', 'Site not found'); + return site; +} + +router.get( + '/', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const nodes = await Node.findAll({ where: { siteId: site.id }, order: [['name', 'ASC']] }); + return ok(res, nodes.map(serialize)); + }), +); + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const node = await Node.findOne({ + where: { id: parseInt(req.params.id, 10), siteId: site.id }, + }); + if (!node) throw new ApiError(404, 'not_found', 'Node not found'); + return ok(res, serialize(node)); + }), +); + +router.get( + '/:id/storages', + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const node = await Node.findOne({ + where: { id: parseInt(req.params.id, 10), siteId: site.id }, + }); + if (!node || !node.apiUrl || !node.tokenId || !node.secret) return ok(res, []); + try { + const client = await node.api(); + const storages = await client.datastores(node.name, 'vztmpl', true); + return ok(res, storages.map((s) => ({ name: s.storage, total: s.total, available: s.avail }))); + } catch (err) { + console.error('Error fetching storages:', err.message); + return ok(res, []); + } + }), +); + +router.post( + '/', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage, networkBridge, nvidiaAvailable } = + req.body || {}; + const node = await Node.create({ + name, + ipv4Address: ipv4Address || null, + apiUrl: apiUrl || null, + tokenId: tokenId || null, + secret: secret || null, + tlsVerify: + tlsVerify === '' || tlsVerify === null || tlsVerify === undefined ? null : tlsVerify === true || tlsVerify === 'true', + imageStorage: imageStorage || 'local', + volumeStorage: volumeStorage || 'local-lvm', + networkBridge: networkBridge || 'vmbr0', + nvidiaAvailable: nvidiaAvailable === true || nvidiaAvailable === 'true', + siteId: site.id, + }); + return created(res, serialize(node)); + }), +); + +router.put( + '/:id', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const node = await Node.findOne({ + where: { id: parseInt(req.params.id, 10), siteId: site.id }, + }); + if (!node) throw new ApiError(404, 'not_found', 'Node not found'); + const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage, networkBridge, nvidiaAvailable } = + req.body || {}; + const update = { + name, + ipv4Address: ipv4Address || null, + apiUrl: apiUrl || null, + tokenId: tokenId || null, + tlsVerify: + tlsVerify === '' || tlsVerify === null || tlsVerify === undefined ? null : tlsVerify === true || tlsVerify === 'true', + imageStorage: imageStorage || 'local', + volumeStorage: volumeStorage || 'local-lvm', + networkBridge: networkBridge || 'vmbr0', + nvidiaAvailable: nvidiaAvailable === true || nvidiaAvailable === 'true', + }; + if (secret && secret.trim() !== '') update.secret = secret; + await node.update(update); + return ok(res, serialize(node)); + }), +); + +router.delete( + '/:id', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const node = await Node.findOne({ + where: { id: parseInt(req.params.id, 10), siteId: site.id }, + include: [{ model: Container, as: 'containers' }], + }); + if (!node) throw new ApiError(404, 'not_found', 'Node not found'); + if (node.containers && node.containers.length > 0) { + throw new ApiError( + 409, + 'has_containers', + `Cannot delete node ${node.name}: ${node.containers.length} container(s) still reference it`, + ); + } + await node.destroy(); + return noContent(res); + }), +); + +// POST /nodes/import — import nodes (and their containers) from Proxmox +router.post( + '/import', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await loadSite(req); + const { apiUrl, username, password, tlsVerify } = req.body || {}; + if (!apiUrl || !username || !password) { + throw new ApiError(400, 'invalid_request', 'apiUrl, username, password are required'); + } + const httpsAgent = new https.Agent({ rejectUnauthorized: tlsVerify !== false && tlsVerify !== 'false' }); + void httpsAgent; + try { + const tempNode = Node.build({ + name: 'temp', + apiUrl, + tokenId: username, + secret: password, + tlsVerify: tlsVerify !== false && tlsVerify !== 'false', + }); + const client = await tempNode.api(); + const proxNodes = await client.nodes(); + + const nodesWithIp = await Promise.all( + proxNodes.map(async (n) => { + let ipv4Address = null; + let imageStorage = 'local'; + let volumeStorage = 'local-lvm'; + try { + const ifaces = await client.nodeNetwork(n.node); + const primary = ifaces.find( + (i) => i.iface === 'vmbr0' || (i.type === 'bridge' && i.active), + ); + ipv4Address = primary?.address || null; + } catch (err) { + console.error(`Failed to fetch network for ${n.node}:`, err.message); + } + try { + const storages = await client.datastores(n.node, 'vztmpl', true); + if (storages.length > 0) { + const largest = storages.reduce((max, s) => (s.total > max.total ? s : max), storages[0]); + imageStorage = largest.storage; + } + } catch (err) { + console.error(`Failed to fetch image storages for ${n.node}:`, err.message); + } + try { + const storages = await client.datastores(n.node, 'rootdir', true); + if (storages.length > 0) { + const largest = storages.reduce((max, s) => (s.total > max.total ? s : max), storages[0]); + volumeStorage = largest.storage; + } + } catch (err) { + console.error(`Failed to fetch volume storages for ${n.node}:`, err.message); + } + return { + name: n.node, + ipv4Address, + apiUrl, + tokenId: username, + secret: password, + tlsVerify: tlsVerify !== false && tlsVerify !== 'false', + imageStorage, + volumeStorage, + networkBridge: 'vmbr0', + siteId: site.id, + }; + }), + ); + const importedNodes = await Node.bulkCreate(nodesWithIp); + + const containerList = await client.clusterResources('lxc'); + const containerRows = await Promise.all( + containerList.map(async (c) => { + const config = await client.lxcConfig(c.node, c.vmid); + const macMatch = config.net0?.match(/hwaddr=([0-9A-Fa-f:]+)/); + const macAddress = macMatch ? macMatch[1] : null; + let ipv4Address = null; + const ipMatch = config.net0?.match(/ip=([^,]+)/); + if (ipMatch && ipMatch[1] !== 'dhcp') { + ipv4Address = ipMatch[1].split('/')[0]; + } else if (ipMatch && ipMatch[1] === 'dhcp') { + try { + ipv4Address = await client.getLxcIpAddress(c.node, c.vmid, 3, 2000); + } catch (err) { + console.error(`Failed to get IP for DHCP container ${c.vmid}: ${err.message}`); + } + } + return { + hostname: config.hostname, + username: req.session.user, + nodeId: importedNodes.find((n) => n.name === c.node).id, + siteId: site.id, + containerId: c.vmid, + macAddress, + ipv4Address, + status: 'running', + }; + }), + ); + await Container.bulkCreate(containerRows); + return created(res, { + nodes: importedNodes.map(serialize), + importedContainerCount: containerRows.length, + }); + } catch (err) { + console.error('Import failed:', err); + throw new ApiError(502, 'import_failed', `Failed to import nodes: ${err.message}`); + } + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/settings.js b/create-a-container/routers/api/v1/settings.js new file mode 100644 index 00000000..2e6fbb70 --- /dev/null +++ b/create-a-container/routers/api/v1/settings.js @@ -0,0 +1,83 @@ +/** + * /api/v1/settings — admin-only key/value system settings + default container env vars. + */ + +const express = require('express'); +const { Setting } = require('../../../models'); +const { apiAuth, apiAdmin, asyncHandler, ok, ApiError } = require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth, apiAdmin); + +const KEYS = [ + 'push_notification_url', + 'push_notification_enabled', + 'push_notification_api_key', + 'smtp_url', + 'smtp_noreply_address', + 'default_container_env_vars', +]; + +router.get( + '/', + asyncHandler(async (_req, res) => { + const settings = await Setting.getMultiple(KEYS); + let defaultContainerEnvVars = []; + try { + defaultContainerEnvVars = await Setting.getDefaultContainerEnvVars(); + } catch { + /* malformed JSON — treat as empty */ + } + return ok(res, { + pushNotificationUrl: settings.push_notification_url || '', + pushNotificationEnabled: settings.push_notification_enabled === 'true', + pushNotificationApiKey: settings.push_notification_api_key || '', + smtpUrl: settings.smtp_url || '', + smtpNoreplyAddress: settings.smtp_noreply_address || '', + defaultContainerEnvVars, + }); + }), +); + +router.put( + '/', + asyncHandler(async (req, res) => { + const { + pushNotificationUrl, + pushNotificationEnabled, + pushNotificationApiKey, + smtpUrl, + smtpNoreplyAddress, + defaultContainerEnvVars, + } = req.body || {}; + + if (pushNotificationEnabled === true && (!pushNotificationUrl || pushNotificationUrl.trim() === '')) { + throw new ApiError(400, 'invalid_request', 'pushNotificationUrl is required when push notifications are enabled'); + } + + const envVars = []; + if (Array.isArray(defaultContainerEnvVars)) { + for (const e of defaultContainerEnvVars) { + if (e && e.key && e.key.trim()) { + envVars.push({ + key: e.key.trim(), + value: e.value || '', + description: e.description || '', + }); + } + } + } + + await Setting.set('push_notification_url', pushNotificationUrl || ''); + await Setting.set('push_notification_enabled', pushNotificationEnabled ? 'true' : 'false'); + await Setting.set('push_notification_api_key', pushNotificationApiKey || ''); + await Setting.set('smtp_url', smtpUrl || ''); + await Setting.set('smtp_noreply_address', smtpNoreplyAddress || ''); + await Setting.set('default_container_env_vars', JSON.stringify(envVars)); + + return ok(res, { saved: true }); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/sites.js b/create-a-container/routers/api/v1/sites.js new file mode 100644 index 00000000..2ee1b59e --- /dev/null +++ b/create-a-container/routers/api/v1/sites.js @@ -0,0 +1,111 @@ +/** + * /api/v1/sites — site CRUD + nested resource pointers. + * Nested containers/nodes/jobs are mounted under /api/v1/sites/:siteId/* via this router. + */ + +const express = require('express'); +const { Site, Node } = require('../../../models'); +const { apiAuth, apiAdmin, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth); + +// Nested mounts +router.use('/:siteId/containers', require('./containers')); +router.use('/:siteId/nodes', require('./nodes')); + +function serialize(site) { + return { + id: site.id, + name: site.name, + internalDomain: site.internalDomain, + dhcpRange: site.dhcpRange, + subnetMask: site.subnetMask, + gateway: site.gateway, + dnsForwarders: site.dnsForwarders, + externalIp: site.externalIp, + nodeCount: site.nodes ? site.nodes.length : undefined, + }; +} + +router.get( + '/', + asyncHandler(async (_req, res) => { + const sites = await Site.findAll({ + include: [{ model: Node, as: 'nodes', attributes: ['id', 'name'] }], + order: [['id', 'ASC']], + }); + return ok(res, sites.map(serialize)); + }), +); + +router.get( + '/:id', + asyncHandler(async (req, res) => { + const site = await Site.findByPk(req.params.id, { + include: [{ model: Node, as: 'nodes', attributes: ['id', 'name'] }], + }); + if (!site) throw new ApiError(404, 'not_found', 'Site not found'); + return ok(res, serialize(site)); + }), +); + +router.post( + '/', + apiAdmin, + asyncHandler(async (req, res) => { + const { name, internalDomain, dhcpRange, subnetMask, gateway, dnsForwarders, externalIp } = + req.body || {}; + const site = await Site.create({ + name, + internalDomain, + dhcpRange, + subnetMask, + gateway, + dnsForwarders, + externalIp: externalIp || null, + }); + return created(res, serialize(site)); + }), +); + +router.put( + '/:id', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await Site.findByPk(req.params.id); + if (!site) throw new ApiError(404, 'not_found', 'Site not found'); + const { name, internalDomain, dhcpRange, subnetMask, gateway, dnsForwarders, externalIp } = + req.body || {}; + await site.update({ + name, + internalDomain, + dhcpRange, + subnetMask, + gateway, + dnsForwarders, + externalIp: externalIp || null, + }); + return ok(res, serialize(site)); + }), +); + +router.delete( + '/:id', + apiAdmin, + asyncHandler(async (req, res) => { + const site = await Site.findByPk(req.params.id, { + include: [{ model: Node, as: 'nodes' }], + }); + if (!site) throw new ApiError(404, 'not_found', 'Site not found'); + if (site.nodes && site.nodes.length > 0) { + throw new ApiError(409, 'has_nodes', 'Cannot delete site with associated nodes'); + } + await site.destroy(); + return noContent(res); + }), +); + +module.exports = router; diff --git a/create-a-container/routers/api/v1/users.js b/create-a-container/routers/api/v1/users.js new file mode 100644 index 00000000..35fa0f6b --- /dev/null +++ b/create-a-container/routers/api/v1/users.js @@ -0,0 +1,159 @@ +/** + * /api/v1/users — admin-only CRUD + invite. + */ + +const express = require('express'); +const { User, Group, InviteToken, Setting } = require('../../../models'); +const { sendInviteEmail } = require('../../../utils/email'); +const { sendPushNotificationInvite } = require('../../../utils/push-notification-invite'); +const { apiAuth, apiAdmin, asyncHandler, ok, created, noContent, ApiError } = + require('../../../middlewares/api'); + +const router = express.Router(); + +router.use(apiAuth, apiAdmin); + +function serialize(u) { + return { + uidNumber: u.uidNumber, + uid: u.uid, + givenName: u.givenName, + sn: u.sn, + cn: u.cn, + mail: u.mail, + status: u.status, + groups: u.groups + ? u.groups.map((g) => ({ gidNumber: g.gidNumber, cn: g.cn, isAdmin: g.isAdmin })) + : undefined, + isAdmin: u.groups?.some((g) => g.isAdmin) || false, + }; +} + +router.get( + '/', + asyncHandler(async (_req, res) => { + const users = await User.findAll({ + include: [{ association: 'groups', attributes: ['gidNumber', 'cn', 'isAdmin'] }], + order: [['uidNumber', 'ASC']], + }); + return ok(res, users.map(serialize)); + }), +); + +router.get( + '/:uidNumber', + asyncHandler(async (req, res) => { + const u = await User.findByPk(req.params.uidNumber, { + include: [{ association: 'groups' }], + }); + if (!u) throw new ApiError(404, 'not_found', 'User not found'); + return ok(res, serialize(u)); + }), +); + +router.post( + '/', + asyncHandler(async (req, res) => { + const { uid, givenName, sn, mail, userPassword, status, groupIds } = req.body || {}; + if (!uid || !givenName || !sn || !mail || !userPassword) { + throw new ApiError(400, 'invalid_request', 'uid, givenName, sn, mail, userPassword are required'); + } + const trimmedGiven = givenName.trim(); + const trimmedSn = sn.trim(); + const user = await User.create({ + uidNumber: await User.nextUidNumber(), + uid, + givenName: trimmedGiven, + sn: trimmedSn, + cn: `${trimmedGiven} ${trimmedSn}`, + mail, + userPassword, + status: status || 'pending', + homeDirectory: `/home/${uid}`, + loginShell: '/bin/bash', + gidNumber: 2001, + }); + if (Array.isArray(groupIds) && groupIds.length > 0) { + const groups = await Group.findAll({ where: { gidNumber: groupIds } }); + await user.setGroups(groups); + } + return created(res, serialize(user)); + }), +); + +router.put( + '/:uidNumber', + asyncHandler(async (req, res) => { + const user = await User.findByPk(req.params.uidNumber); + if (!user) throw new ApiError(404, 'not_found', 'User not found'); + const { uid, givenName, sn, mail, userPassword, status, groupIds } = req.body || {}; + const trimmedGiven = (givenName || '').trim(); + const trimmedSn = (sn || '').trim(); + const previousStatus = user.status; + + user.uid = uid ?? user.uid; + user.givenName = trimmedGiven || user.givenName; + user.sn = trimmedSn || user.sn; + user.cn = `${user.givenName} ${user.sn}`; + user.mail = mail ?? user.mail; + user.status = status || user.status; + user.homeDirectory = `/home/${user.uid}`; + if (userPassword && userPassword.trim() !== '') { + user.userPassword = userPassword; + } + await user.save(); + + let twoFactorWarning; + if (previousStatus !== 'active' && user.status === 'active') { + const inviteResult = await sendPushNotificationInvite(user); + if (inviteResult && !inviteResult.success) { + twoFactorWarning = inviteResult.error; + } + } + if (Array.isArray(groupIds)) { + const groups = await Group.findAll({ where: { gidNumber: groupIds } }); + await user.setGroups(groups); + } + return ok(res, { ...serialize(user), ...(twoFactorWarning ? { twoFactorWarning } : {}) }); + }), +); + +router.delete( + '/:uidNumber', + asyncHandler(async (req, res) => { + const u = await User.findByPk(req.params.uidNumber); + if (!u) throw new ApiError(404, 'not_found', 'User not found'); + await u.destroy(); + return noContent(res); + }), +); + +// POST /api/v1/users/invite — send an invitation email +router.post( + '/invite', + asyncHandler(async (req, res) => { + const { email } = req.body || {}; + if (!email || email.trim() === '') { + throw new ApiError(400, 'invalid_request', 'email is required'); + } + const normalized = email.toLowerCase().trim(); + const settings = await Setting.getMultiple(['smtp_url']); + if (!settings.smtp_url || settings.smtp_url.trim() === '') { + throw new ApiError(409, 'smtp_not_configured', 'SMTP is not configured'); + } + const existing = await User.findOne({ where: { mail: normalized } }); + if (existing) throw new ApiError(409, 'duplicate_email', 'A user with this email already exists'); + + const { token } = await InviteToken.generateToken(normalized, 24); + const inviteUrl = `${req.protocol}://${req.get('host')}/register?token=${token}`; + try { + await sendInviteEmail(normalized, inviteUrl); + } catch (err) { + console.error('Invite email failed:', err); + throw new ApiError(502, 'email_failed', 'Failed to send invitation email'); + } + return ok(res, { email: normalized, message: 'Invitation sent' }); + }), +); + +module.exports = router; diff --git a/create-a-container/server.js b/create-a-container/server.js index 212ab62c..3bd47b3c 100644 --- a/create-a-container/server.js +++ b/create-a-container/server.js @@ -152,7 +152,12 @@ async function main() { const settingsRouter = require('./routers/settings'); const apikeysRouter = require('./routers/apikeys'); const resetPasswordRouter = require('./routers/reset-password'); - + const apiV1Router = require('./routers/api/v1'); + + // Mount the new JSON API for the React SPA + external consumers BEFORE the legacy EJS routes, + // so /api/v1/* never falls through to a user-facing redirect. + app.use('/api/v1', apiV1Router); + app.use('/jobs', jobsRouter); app.use('/login', loginRouter); app.use('/register', registerRouter); From 25aa6227afe1c0a41155354b1188a3de9ebaaa62 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 10:51:07 -0700 Subject: [PATCH 03/11] feat(client): SPA shell + auth flows on /api/v1 - lib/api.ts: typed JSON client with credential cookies, CSRF double-submit (lazy fetch + retry on 403), { data }/{ error } envelope, 401 hook for redirect-to-login. - lib/auth.ts: useSession query + login mutation w/ 2FA challenge support + logout (clears CSRF, resets query cache). - providers.tsx: ThemeProvider, ToastProvider, SidebarProvider, CommandPaletteProvider. - AppLayout: @mieweb/ui Sidebar + AppHeader + CommandPalette shell. - AuthLayout: branded auth shell. - RequireAuth: route guard, redirects to /login w/ redirect param. - Sidebar/Header components composed from @mieweb/ui primitives. - Auth pages: LoginPage (w/ 2FA push polling), RegisterPage (supports invite tokens), RegisterSuccessPage (w/ 2FA QR code), ResetPasswordRequestPage, ResetPasswordPage. --- .../client/src/app/AppLayout.tsx | 21 +- .../client/src/app/AuthLayout.tsx | 13 +- create-a-container/client/src/app/Header.tsx | 56 +++++ .../client/src/app/RequireAuth.tsx | 34 +++ create-a-container/client/src/app/Sidebar.tsx | 106 +++++++++ .../client/src/app/providers.tsx | 19 ++ create-a-container/client/src/app/router.tsx | 21 +- create-a-container/client/src/lib/api.ts | 146 +++++++++++++ create-a-container/client/src/lib/auth.ts | 96 +++++++++ create-a-container/client/src/main.tsx | 5 +- .../client/src/pages/auth/LoginPage.tsx | 204 ++++++++++++++++++ .../client/src/pages/auth/RegisterPage.tsx | 192 +++++++++++++++++ .../src/pages/auth/RegisterSuccessPage.tsx | 107 +++++++++ .../src/pages/auth/ResetPasswordPage.tsx | 131 +++++++++++ .../pages/auth/ResetPasswordRequestPage.tsx | 85 ++++++++ 15 files changed, 1219 insertions(+), 17 deletions(-) create mode 100644 create-a-container/client/src/app/Header.tsx create mode 100644 create-a-container/client/src/app/RequireAuth.tsx create mode 100644 create-a-container/client/src/app/Sidebar.tsx create mode 100644 create-a-container/client/src/app/providers.tsx create mode 100644 create-a-container/client/src/lib/api.ts create mode 100644 create-a-container/client/src/lib/auth.ts create mode 100644 create-a-container/client/src/pages/auth/LoginPage.tsx create mode 100644 create-a-container/client/src/pages/auth/RegisterPage.tsx create mode 100644 create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx create mode 100644 create-a-container/client/src/pages/auth/ResetPasswordPage.tsx create mode 100644 create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx diff --git a/create-a-container/client/src/app/AppLayout.tsx b/create-a-container/client/src/app/AppLayout.tsx index 4d2c5129..3591b4c6 100644 --- a/create-a-container/client/src/app/AppLayout.tsx +++ b/create-a-container/client/src/app/AppLayout.tsx @@ -1,14 +1,21 @@ import { Outlet } from 'react-router'; +import { Sidebar, CommandPalette } from '@mieweb/ui'; +import { AppSidebar } from './Sidebar'; +import { AppTopHeader } from './Header'; export function AppLayout() { return ( -
-
-

MIE Container Manager

-
-
- -
+
+ + + +
+ +
+ +
+
+
); } diff --git a/create-a-container/client/src/app/AuthLayout.tsx b/create-a-container/client/src/app/AuthLayout.tsx index 9f336a4f..aea2d330 100644 --- a/create-a-container/client/src/app/AuthLayout.tsx +++ b/create-a-container/client/src/app/AuthLayout.tsx @@ -1,10 +1,17 @@ import { Outlet } from 'react-router'; +import { Box } from 'lucide-react'; export function AuthLayout() { return ( -
-
- +
+
+
+ + Container Manager +
+
+ +
); diff --git a/create-a-container/client/src/app/Header.tsx b/create-a-container/client/src/app/Header.tsx new file mode 100644 index 00000000..f10de7ca --- /dev/null +++ b/create-a-container/client/src/app/Header.tsx @@ -0,0 +1,56 @@ +import { + AppHeader, + AppHeaderSection, + AppHeaderBrand, + AppHeaderActions, + AppHeaderIconButton, + AppHeaderUserMenu, + SidebarMobileToggle, + useThemeContext, +} from '@mieweb/ui'; +import { Moon, Search, Sun } from 'lucide-react'; +import { useSession } from '@/lib/auth'; +import { useCommandPalette } from '@mieweb/ui'; + +function initialsOf(name: string | undefined) { + if (!name) return '?'; + const parts = name.split(/[.\s_-]+/).filter(Boolean); + if (parts.length === 0) return name.slice(0, 2).toUpperCase(); + return (parts[0][0] + (parts[1]?.[0] || '')).toUpperCase(); +} + +export function AppTopHeader() { + const { data: session } = useSession(); + const { resolvedTheme, setTheme } = useThemeContext(); + const palette = useCommandPalette(); + + const isDark = resolvedTheme === 'dark'; + + return ( + + + + Container Manager + + + + } + label="Search (⌘K)" + onClick={palette.open} + /> + : } + label={isDark ? 'Switch to light theme' : 'Switch to dark theme'} + onClick={() => setTheme(isDark ? 'light' : 'dark')} + /> + + + + + ); +} diff --git a/create-a-container/client/src/app/RequireAuth.tsx b/create-a-container/client/src/app/RequireAuth.tsx new file mode 100644 index 00000000..c022870c --- /dev/null +++ b/create-a-container/client/src/app/RequireAuth.tsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; +import { Navigate, Outlet, useLocation } from 'react-router'; +import { Spinner } from '@mieweb/ui'; +import { useSession } from '@/lib/auth'; +import { setOnUnauthorized } from '@/lib/api'; + +/** + * Guards authenticated routes. Redirects to /login with the originating + * path stored on history state so the login flow can return the user. + */ +export function RequireAuth() { + const { data: session, isLoading, isError } = useSession(); + const location = useLocation(); + const [unauthorizedTrigger, setUnauthorizedTrigger] = useState(0); + + useEffect(() => { + setOnUnauthorized(() => setUnauthorizedTrigger((n) => n + 1)); + }, []); + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (!session || isError || unauthorizedTrigger > 0) { + const redirect = encodeURIComponent(location.pathname + location.search); + return ; + } + + return ; +} diff --git a/create-a-container/client/src/app/Sidebar.tsx b/create-a-container/client/src/app/Sidebar.tsx new file mode 100644 index 00000000..a1a90952 --- /dev/null +++ b/create-a-container/client/src/app/Sidebar.tsx @@ -0,0 +1,106 @@ +import { useNavigate, useLocation } from 'react-router'; +import { + SidebarNav, + SidebarNavItem, + SidebarHeader, + SidebarContent, + SidebarFooter, + SidebarToggle, +} from '@mieweb/ui'; +import { + Box, + Building2, + Globe, + KeyRound, + Settings, + Users, + UsersRound, + Container as ContainerIcon, +} from 'lucide-react'; +import { useSession, useLogoutMutation } from '@/lib/auth'; + +interface NavLink { + to: string; + label: string; + icon: React.ReactNode; + adminOnly?: boolean; + /** Match this prefix to mark active (defaults to exact `to`). */ + match?: string; +} + +const PRIMARY: NavLink[] = [ + { to: '/sites', label: 'Sites', icon: , match: '/sites' }, + { + to: '/containers', + label: 'Containers', + icon: , + match: '/containers', + }, +]; + +const ADMIN: NavLink[] = [ + { to: '/users', label: 'Users', icon: , adminOnly: true }, + { to: '/groups', label: 'Groups', icon: , adminOnly: true }, + { + to: '/external-domains', + label: 'External Domains', + icon: , + adminOnly: true, + }, + { to: '/apikeys', label: 'API Keys', icon: }, + { to: '/settings', label: 'Settings', icon: , adminOnly: true }, +]; + +export function AppSidebar() { + const navigate = useNavigate(); + const location = useLocation(); + const { data: session } = useSession(); + const logout = useLogoutMutation(); + const isAdmin = !!session?.isAdmin; + + const isActive = (link: NavLink) => { + const prefix = link.match ?? link.to; + return location.pathname === prefix || location.pathname.startsWith(`${prefix}/`); + }; + + const renderLink = (link: NavLink) => ( + navigate(link.to)} + /> + ); + + return ( + <> + +
+ + Container Manager +
+
+ + {PRIMARY.map(renderLink)} + + {ADMIN.filter((l) => !l.adminOnly || isAdmin).map(renderLink)} + + + +
+ {session?.user} + +
+ +
+ + ); +} diff --git a/create-a-container/client/src/app/providers.tsx b/create-a-container/client/src/app/providers.tsx new file mode 100644 index 00000000..b517e80e --- /dev/null +++ b/create-a-container/client/src/app/providers.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from 'react'; +import { + ThemeProvider, + ToastProvider, + SidebarProvider, + CommandPaletteProvider, +} from '@mieweb/ui'; + +export function AppProviders({ children }: { children: ReactNode }) { + return ( + + + + {children} + + + + ); +} diff --git a/create-a-container/client/src/app/router.tsx b/create-a-container/client/src/app/router.tsx index cd809caa..84d277a3 100644 --- a/create-a-container/client/src/app/router.tsx +++ b/create-a-container/client/src/app/router.tsx @@ -2,20 +2,28 @@ import { createBrowserRouter, Navigate } from 'react-router'; import { AppLayout } from './AppLayout'; import { AuthLayout } from './AuthLayout'; import { PlaceholderPage } from './PlaceholderPage'; +import { RequireAuth } from './RequireAuth'; +import { LoginPage } from '@/pages/auth/LoginPage'; +import { RegisterPage } from '@/pages/auth/RegisterPage'; +import { RegisterSuccessPage } from '@/pages/auth/RegisterSuccessPage'; +import { ResetPasswordRequestPage } from '@/pages/auth/ResetPasswordRequestPage'; +import { ResetPasswordPage } from '@/pages/auth/ResetPasswordPage'; export const router = createBrowserRouter([ { element: , children: [ - { path: '/login', element: }, - { path: '/register', element: }, - { path: '/register/success', element: }, - { path: '/verify/:token', element: }, - { path: '/reset-password', element: }, - { path: '/reset-password/:token', element: }, + { path: '/login', element: }, + { path: '/register', element: }, + { path: '/register/invite/:token', element: }, + { path: '/register/success', element: }, + { path: '/reset-password', element: }, + { path: '/reset-password/:token', element: }, ], }, { + element: , + children: [{ element: , children: [ { index: true, element: }, @@ -47,5 +55,6 @@ export const router = createBrowserRouter([ { path: '/settings', element: }, { path: '*', element: }, ], + }], }, ]); diff --git a/create-a-container/client/src/lib/api.ts b/create-a-container/client/src/lib/api.ts new file mode 100644 index 00000000..bd1e06ce --- /dev/null +++ b/create-a-container/client/src/lib/api.ts @@ -0,0 +1,146 @@ +/** + * Typed JSON client for /api/v1. Handles: + * - cookies (session) + * - CSRF double-submit token (fetched on demand, cached, refreshed on 403) + * - `{ data }` / `{ error }` envelope unwrapping + * - 401 → optional auth-failure handler (router decides what to do) + */ + +export interface ApiErrorPayload { + code: string; + message: string; + fields?: Record; +} + +export class ApiError extends Error { + status: number; + code: string; + fields?: Record; + constructor(status: number, payload: ApiErrorPayload) { + super(payload.message || `Request failed (${status})`); + this.name = 'ApiError'; + this.status = status; + this.code = payload.code || 'unknown'; + this.fields = payload.fields; + } +} + +let csrfToken: string | null = null; +let onUnauthorized: (() => void) | null = null; + +export function setOnUnauthorized(fn: () => void) { + onUnauthorized = fn; +} + +async function fetchCsrfToken(): Promise { + const res = await fetch('/api/v1/csrf-token', { credentials: 'include' }); + if (!res.ok) throw new Error(`Failed to fetch CSRF token: ${res.status}`); + const body = (await res.json()) as { data: { csrfToken: string } }; + csrfToken = body.data.csrfToken; + return csrfToken; +} + +export function clearCsrfToken() { + csrfToken = null; +} + +export interface RequestOptions { + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; + body?: unknown; + signal?: AbortSignal; + /** Pass the raw Response back instead of parsing JSON (for SSE). */ + raw?: boolean; + headers?: Record; +} + +const MUTATING_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']); + +export async function apiRequest( + path: string, + options: RequestOptions = {}, +): Promise { + const method = options.method || 'GET'; + const isMutation = MUTATING_METHODS.has(method); + + if (isMutation && !csrfToken) { + await fetchCsrfToken(); + } + + const doFetch = async (): Promise => { + const headers: Record = { + Accept: 'application/json', + ...options.headers, + }; + if (options.body !== undefined && !(options.body instanceof FormData)) { + headers['Content-Type'] = 'application/json'; + } + if (isMutation && csrfToken) headers['X-CSRF-Token'] = csrfToken; + + return fetch(path.startsWith('/') ? path : `/api/v1/${path}`, { + method, + credentials: 'include', + signal: options.signal, + headers, + body: + options.body === undefined + ? undefined + : options.body instanceof FormData + ? options.body + : JSON.stringify(options.body), + }); + }; + + let response = await doFetch(); + + // CSRF token may have rotated; retry once. + if (response.status === 403 && isMutation) { + const text = await response.clone().text(); + if (text.includes('csrf') || text.includes('CSRF')) { + await fetchCsrfToken(); + response = await doFetch(); + } + } + + if (response.status === 401) { + if (onUnauthorized) onUnauthorized(); + throw new ApiError(401, { code: 'unauthorized', message: 'Sign-in required' }); + } + + if (options.raw) return response as unknown as T; + + if (response.status === 204) return undefined as T; + + const contentType = response.headers.get('content-type') || ''; + if (!contentType.includes('application/json')) { + if (!response.ok) { + throw new ApiError(response.status, { + code: 'http_error', + message: `HTTP ${response.status}`, + }); + } + return undefined as T; + } + + const body = (await response.json()) as + | { data: T; meta?: unknown } + | { error: ApiErrorPayload }; + + if (!response.ok || 'error' in body) { + const err = 'error' in body ? body.error : { code: 'unknown', message: 'Request failed' }; + throw new ApiError(response.status, err); + } + return body.data; +} + +export const api = { + get: (path: string, opts?: Omit) => + apiRequest(path, { ...opts, method: 'GET' }), + post: (path: string, body?: unknown, opts?: Omit) => + apiRequest(path, { ...opts, method: 'POST', body }), + put: (path: string, body?: unknown, opts?: Omit) => + apiRequest(path, { ...opts, method: 'PUT', body }), + patch: (path: string, body?: unknown, opts?: Omit) => + apiRequest(path, { ...opts, method: 'PATCH', body }), + delete: (path: string, opts?: Omit) => + apiRequest(path, { ...opts, method: 'DELETE' }), +}; diff --git a/create-a-container/client/src/lib/auth.ts b/create-a-container/client/src/lib/auth.ts new file mode 100644 index 00000000..58573e59 --- /dev/null +++ b/create-a-container/client/src/lib/auth.ts @@ -0,0 +1,96 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { api, ApiError, clearCsrfToken } from './api'; + +export interface SessionUser { + user: string; + isAdmin: boolean; +} + +export const sessionKey = ['session'] as const; + +export function useSession() { + return useQuery({ + queryKey: sessionKey, + queryFn: async () => { + try { + return await api.get('/api/v1/session'); + } catch (err) { + if (err instanceof ApiError && err.status === 401) return null; + throw err; + } + }, + staleTime: 60_000, + refetchOnWindowFocus: true, + }); +} + +export interface LoginInput { + username: string; + password: string; + redirect?: string; +} + +export type LoginResult = + | { kind: 'logged-in'; user: string; isAdmin: boolean; redirect: string } + | { kind: '2fa'; challengeId: string }; + +interface LoginResponse { + user?: string; + isAdmin?: boolean; + redirect?: string; + challengeId?: string; + requires2FA?: boolean; +} + +export function useLoginMutation() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: async (input) => { + const data = await api.post('/api/v1/auth/login', input); + if (data.requires2FA && data.challengeId) { + return { kind: '2fa', challengeId: data.challengeId }; + } + return { + kind: 'logged-in', + user: data.user || input.username, + isAdmin: !!data.isAdmin, + redirect: data.redirect || '/', + }; + }, + onSuccess: (result) => { + if (result.kind === 'logged-in') { + qc.setQueryData(sessionKey, { + user: result.user, + isAdmin: result.isAdmin, + }); + } + }, + }); +} + +export interface ChallengeStatus { + status: 'pending' | 'approved' | 'rejected' | 'timeout' | 'failed' | 'unregistered'; + user?: string; + isAdmin?: boolean; + redirect?: string; + message?: string; + registrationUrl?: string; +} + +export async function fetchChallenge(id: string): Promise { + return api.get(`/api/v1/auth/login/challenge/${encodeURIComponent(id)}`); +} + +export function useLogoutMutation() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: async () => { + await api.post('/api/v1/auth/logout'); + }, + onSettled: () => { + clearCsrfToken(); + qc.setQueryData(sessionKey, null); + qc.clear(); + }, + }); +} diff --git a/create-a-container/client/src/main.tsx b/create-a-container/client/src/main.tsx index 0c453e2d..e95a07bf 100644 --- a/create-a-container/client/src/main.tsx +++ b/create-a-container/client/src/main.tsx @@ -2,6 +2,7 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RouterProvider } from 'react-router'; +import { AppProviders } from './app/providers'; import { router } from './app/router'; import './styles/index.css'; @@ -25,7 +26,9 @@ if (!rootEl) throw new Error('Missing #root element'); createRoot(rootEl).render( - + + + , ); diff --git a/create-a-container/client/src/pages/auth/LoginPage.tsx b/create-a-container/client/src/pages/auth/LoginPage.tsx new file mode 100644 index 00000000..32fc9f47 --- /dev/null +++ b/create-a-container/client/src/pages/auth/LoginPage.tsx @@ -0,0 +1,204 @@ +import { useEffect, useRef, useState } from 'react'; +import { Link, useNavigate, useSearchParams } from 'react-router'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; +import { useLoginMutation, fetchChallenge, type ChallengeStatus } from '@/lib/auth'; +import { ApiError } from '@/lib/api'; + +const schema = z.object({ + username: z.string().min(1, 'Username is required'), + password: z.string().min(1, 'Password is required'), +}); +type FormData = z.infer; + +const POLL_INTERVAL_MS = 2000; +const POLL_MAX_MS = 5 * 60 * 1000; + +export function LoginPage() { + const navigate = useNavigate(); + const [params] = useSearchParams(); + const redirect = params.get('redirect') || '/'; + const login = useLoginMutation(); + + const [challengeId, setChallengeId] = useState(null); + const [challenge, setChallenge] = useState(null); + const pollTimer = useRef(null); + const pollStart = useRef(0); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ resolver: zodResolver(schema) }); + + useEffect(() => { + return () => { + if (pollTimer.current) window.clearTimeout(pollTimer.current); + }; + }, []); + + function startPolling(id: string) { + pollStart.current = Date.now(); + const poll = async () => { + try { + const status = await fetchChallenge(id); + setChallenge(status); + if (status.status === 'approved') { + navigate(status.redirect && status.redirect !== '/' ? status.redirect : redirect, { + replace: true, + }); + return; + } + if ( + status.status === 'rejected' || + status.status === 'timeout' || + status.status === 'failed' || + status.status === 'unregistered' + ) { + return; + } + if (Date.now() - pollStart.current > POLL_MAX_MS) { + setChallenge({ status: 'timeout', message: 'Challenge expired' }); + return; + } + pollTimer.current = window.setTimeout(poll, POLL_INTERVAL_MS); + } catch (err) { + setChallenge({ + status: 'failed', + message: err instanceof ApiError ? err.message : 'Failed to check challenge', + }); + } + }; + poll(); + } + + const onSubmit = handleSubmit(async (values) => { + setChallenge(null); + setChallengeId(null); + try { + const result = await login.mutateAsync({ ...values, redirect }); + if (result.kind === 'logged-in') { + navigate(result.redirect && result.redirect !== '/' ? result.redirect : redirect, { + replace: true, + }); + } else { + setChallengeId(result.challengeId); + setChallenge({ status: 'pending' }); + startPolling(result.challengeId); + } + } catch { + /* error handled via login.error */ + } + }); + + const submissionError = + login.error && login.error.status !== 401 + ? login.error.message + : login.error?.status === 401 + ? 'Invalid username or password' + : null; + + if (challengeId && challenge) { + return setChallengeId(null)} />; + } + + return ( +
+
+

Sign in

+

+ Use your account credentials. +

+
+ + {submissionError && ( + + Sign in failed + {submissionError} + + )} + + + + + + +
+ + Forgot password? + + + Create account + +
+
+ ); +} + +function ChallengeStatusView({ + status, + onCancel, +}: { + status: ChallengeStatus; + onCancel: () => void; +}) { + if (status.status === 'pending') { + return ( +
+ +

Approve sign-in on your device

+

+ We sent a push notification to confirm this sign-in. +

+ +
+ ); + } + if (status.status === 'unregistered') { + return ( +
+ + Device not registered + + No device is enrolled for push 2FA. Contact an administrator to receive an invite. + + + +
+ ); + } + return ( +
+ + Sign-in not completed + {status.message || `Status: ${status.status}`} + + +
+ ); +} diff --git a/create-a-container/client/src/pages/auth/RegisterPage.tsx b/create-a-container/client/src/pages/auth/RegisterPage.tsx new file mode 100644 index 00000000..9f043679 --- /dev/null +++ b/create-a-container/client/src/pages/auth/RegisterPage.tsx @@ -0,0 +1,192 @@ +import { useEffect, useState } from 'react'; +import { Link, useNavigate, useParams } from 'react-router'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; + +const schema = z + .object({ + uid: z.string().min(2, 'Username must be at least 2 characters').max(32), + givenName: z.string().min(1, 'First name is required'), + sn: z.string().min(1, 'Last name is required'), + mail: z.string().email('Valid email required'), + userPassword: z.string().min(8, 'At least 8 characters'), + confirmPassword: z.string(), + }) + .refine((d) => d.userPassword === d.confirmPassword, { + message: 'Passwords do not match', + path: ['confirmPassword'], + }); + +type FormData = z.infer; + +interface InviteData { + email: string; +} +interface RegisterResponse { + uid: string; + status: 'active' | 'pending'; + message: string; + twoFactor?: { enrollmentToken?: string; warning?: string }; +} + +export function RegisterPage() { + const navigate = useNavigate(); + const { token } = useParams<{ token?: string }>(); + const [invite, setInvite] = useState(null); + const [inviteError, setInviteError] = useState(null); + const [inviteLoading, setInviteLoading] = useState(!!token); + const [submitError, setSubmitError] = useState(null); + + const form = useForm({ resolver: zodResolver(schema) }); + const { register, handleSubmit, formState, reset } = form; + + useEffect(() => { + if (!token) return; + let cancelled = false; + (async () => { + try { + const data = await api.get( + `/api/v1/auth/register/invite/${encodeURIComponent(token)}`, + ); + if (cancelled) return; + setInvite(data); + reset({ mail: data.email } as Partial); + } catch (err) { + if (cancelled) return; + setInviteError(err instanceof ApiError ? err.message : 'Invitation invalid'); + } finally { + if (!cancelled) setInviteLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, [token, reset]); + + const onSubmit = handleSubmit(async (values) => { + setSubmitError(null); + try { + const { confirmPassword: _confirm, ...payload } = values; + void _confirm; + const res = await api.post('/api/v1/auth/register', { + ...payload, + inviteToken: token, + }); + navigate('/register/success', { + state: { + uid: res.uid, + status: res.status, + message: res.message, + enrollmentToken: res.twoFactor?.enrollmentToken, + warning: res.twoFactor?.warning, + }, + }); + } catch (err) { + setSubmitError(err instanceof ApiError ? err.message : 'Registration failed'); + } + }); + + if (inviteLoading) { + return ( +
+ +
+ ); + } + if (token && inviteError) { + return ( + + Invitation invalid + {inviteError} + + ); + } + + return ( +
+
+

Create your account

+ {invite && ( +

+ Invitation for {invite.email} +

+ )} +
+ + {submitError && ( + + Could not register + {submitError} + + )} + + +
+ + +
+ + + + + + +

+ + Back to sign in + +

+
+ ); +} diff --git a/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx b/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx new file mode 100644 index 00000000..43412ea0 --- /dev/null +++ b/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx @@ -0,0 +1,107 @@ +import { useEffect, useState } from 'react'; +import { Link, useLocation } from 'react-router'; +import { Alert, AlertDescription, AlertTitle, Spinner } from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; + +interface RegisterState { + uid?: string; + status?: 'active' | 'pending'; + message?: string; + enrollmentToken?: string; + warning?: string; +} + +export function RegisterSuccessPage() { + const location = useLocation(); + const state = (location.state as RegisterState | null) || {}; + const [qr, setQr] = useState<{ qrCodeDataUri: string; inviteUrl: string } | null>(null); + const [qrError, setQrError] = useState(null); + const [qrLoading, setQrLoading] = useState(false); + + useEffect(() => { + if (!state.enrollmentToken) return; + let cancelled = false; + setQrLoading(true); + (async () => { + try { + const data = await api.get<{ qrCodeDataUri: string; inviteUrl: string }>( + `/api/v1/auth/register/2fa-qr/${encodeURIComponent(state.enrollmentToken!)}`, + ); + if (!cancelled) setQr(data); + } catch (err) { + if (!cancelled) setQrError(err instanceof ApiError ? err.message : 'QR code unavailable'); + } finally { + if (!cancelled) setQrLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, [state.enrollmentToken]); + + return ( +
+
+

+ {state.status === 'active' ? 'Welcome aboard' : 'Almost there'} +

+

+ {state.message || + (state.status === 'active' + ? 'Your account is ready. You can sign in.' + : 'Your account is awaiting administrator approval.')} +

+
+ + {state.warning && ( + + Notice + {state.warning} + + )} + + {state.enrollmentToken && ( +
+

Enroll your second factor

+

+ Scan this QR code with the push-notification app to register your device for 2FA. +

+ {qrLoading && ( +
+ +
+ )} + {qrError && ( + + {qrError} + + )} + {qr && ( + + )} +
+ )} + + + Continue to sign in + +
+ ); +} diff --git a/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx b/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx new file mode 100644 index 00000000..9ae96a2d --- /dev/null +++ b/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx @@ -0,0 +1,131 @@ +import { useEffect, useState } from 'react'; +import { Link, useNavigate, useParams } from 'react-router'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; + +const schema = z + .object({ + password: z.string().min(8, 'At least 8 characters'), + confirmPassword: z.string(), + }) + .refine((d) => d.password === d.confirmPassword, { + message: 'Passwords do not match', + path: ['confirmPassword'], + }); +type FormData = z.infer; + +export function ResetPasswordPage() { + const { token } = useParams<{ token: string }>(); + const navigate = useNavigate(); + const [username, setUsername] = useState(null); + const [tokenError, setTokenError] = useState(null); + const [loading, setLoading] = useState(true); + const [submitError, setSubmitError] = useState(null); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ resolver: zodResolver(schema) }); + + useEffect(() => { + if (!token) { + setTokenError('Missing reset token'); + setLoading(false); + return; + } + let cancelled = false; + (async () => { + try { + const data = await api.get<{ username: string }>( + `/api/v1/auth/password-reset/${encodeURIComponent(token)}`, + ); + if (!cancelled) setUsername(data.username); + } catch (err) { + if (!cancelled) + setTokenError(err instanceof ApiError ? err.message : 'Invalid or expired reset link'); + } finally { + if (!cancelled) setLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, [token]); + + const onSubmit = handleSubmit(async (values) => { + setSubmitError(null); + try { + await api.post(`/api/v1/auth/password-reset/${encodeURIComponent(token!)}`, values); + navigate('/login', { replace: true }); + } catch (err) { + setSubmitError(err instanceof ApiError ? err.message : 'Could not reset password'); + } + }); + + if (loading) { + return ( +
+ +
+ ); + } + if (tokenError) { + return ( +
+ + Reset link invalid + {tokenError} + + + Request a new link + +
+ ); + } + + return ( +
+
+

Set a new password

+ {username && ( +

+ For account {username} +

+ )} +
+ {submitError && ( + + {submitError} + + )} + + + +
+ ); +} diff --git a/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx b/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx new file mode 100644 index 00000000..379e9a4d --- /dev/null +++ b/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx @@ -0,0 +1,85 @@ +import { useState } from 'react'; +import { Link } from 'react-router'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { Alert, AlertDescription, AlertTitle, Button, Input } from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; + +const schema = z.object({ + usernameOrEmail: z.string().min(1, 'Required'), +}); +type FormData = z.infer; + +export function ResetPasswordRequestPage() { + const [submitted, setSubmitted] = useState(false); + const [error, setError] = useState(null); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ resolver: zodResolver(schema) }); + + const onSubmit = handleSubmit(async (values) => { + setError(null); + try { + await api.post('/api/v1/auth/password-reset/request', values); + setSubmitted(true); + } catch (err) { + setError(err instanceof ApiError ? err.message : 'Request failed'); + } + }); + + if (submitted) { + return ( +
+ + Check your inbox + + If the account exists, we sent password reset instructions. + + + + Back to sign in + +
+ ); + } + + return ( +
+
+

Reset password

+

+ Enter your username or email and we'll send a reset link. +

+
+ {error && ( + + {error} + + )} + + + + Back to sign in + +
+ ); +} From 2cdecbe7e115cb1d166a35632d13b2dc9dd51cbe Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 15:49:52 -0700 Subject: [PATCH 04/11] =?UTF-8?q?feat(client):=20Phase=204=20=E2=80=94=20f?= =?UTF-8?q?eature=20pages=20for=20all=20resources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- create-a-container/client/src/app/router.tsx | 90 ++-- create-a-container/client/src/lib/queries.ts | 91 ++++ create-a-container/client/src/lib/toast.ts | 17 + create-a-container/client/src/lib/types.ts | 169 ++++++++ .../client/src/pages/NotFoundPage.tsx | 12 + .../src/pages/apikeys/ApiKeysListPage.tsx | 157 +++++++ .../pages/containers/ContainerFormPage.tsx | 389 ++++++++++++++++++ .../pages/containers/ContainersListPage.tsx | 188 +++++++++ .../ExternalDomainFormPage.tsx | 143 +++++++ .../ExternalDomainsListPage.tsx | 112 +++++ .../client/src/pages/groups/GroupFormPage.tsx | 101 +++++ .../src/pages/groups/GroupsListPage.tsx | 92 +++++ .../client/src/pages/jobs/JobDetailPage.tsx | 145 +++++++ .../client/src/pages/nodes/NodeFormPage.tsx | 129 ++++++ .../client/src/pages/nodes/NodeImportPage.tsx | 80 ++++ .../client/src/pages/nodes/NodesListPage.tsx | 103 +++++ .../src/pages/settings/SettingsPage.tsx | 140 +++++++ .../client/src/pages/sites/SiteFormPage.tsx | 120 ++++++ .../client/src/pages/sites/SitesListPage.tsx | 134 ++++++ .../client/src/pages/users/InviteUserPage.tsx | 54 +++ .../client/src/pages/users/UserFormPage.tsx | 145 +++++++ .../client/src/pages/users/UsersListPage.tsx | 101 +++++ 22 files changed, 2680 insertions(+), 32 deletions(-) create mode 100644 create-a-container/client/src/lib/queries.ts create mode 100644 create-a-container/client/src/lib/toast.ts create mode 100644 create-a-container/client/src/lib/types.ts create mode 100644 create-a-container/client/src/pages/NotFoundPage.tsx create mode 100644 create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx create mode 100644 create-a-container/client/src/pages/containers/ContainerFormPage.tsx create mode 100644 create-a-container/client/src/pages/containers/ContainersListPage.tsx create mode 100644 create-a-container/client/src/pages/external-domains/ExternalDomainFormPage.tsx create mode 100644 create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx create mode 100644 create-a-container/client/src/pages/groups/GroupFormPage.tsx create mode 100644 create-a-container/client/src/pages/groups/GroupsListPage.tsx create mode 100644 create-a-container/client/src/pages/jobs/JobDetailPage.tsx create mode 100644 create-a-container/client/src/pages/nodes/NodeFormPage.tsx create mode 100644 create-a-container/client/src/pages/nodes/NodeImportPage.tsx create mode 100644 create-a-container/client/src/pages/nodes/NodesListPage.tsx create mode 100644 create-a-container/client/src/pages/settings/SettingsPage.tsx create mode 100644 create-a-container/client/src/pages/sites/SiteFormPage.tsx create mode 100644 create-a-container/client/src/pages/sites/SitesListPage.tsx create mode 100644 create-a-container/client/src/pages/users/InviteUserPage.tsx create mode 100644 create-a-container/client/src/pages/users/UserFormPage.tsx create mode 100644 create-a-container/client/src/pages/users/UsersListPage.tsx diff --git a/create-a-container/client/src/app/router.tsx b/create-a-container/client/src/app/router.tsx index 84d277a3..da315b10 100644 --- a/create-a-container/client/src/app/router.tsx +++ b/create-a-container/client/src/app/router.tsx @@ -1,13 +1,30 @@ import { createBrowserRouter, Navigate } from 'react-router'; import { AppLayout } from './AppLayout'; import { AuthLayout } from './AuthLayout'; -import { PlaceholderPage } from './PlaceholderPage'; import { RequireAuth } from './RequireAuth'; import { LoginPage } from '@/pages/auth/LoginPage'; import { RegisterPage } from '@/pages/auth/RegisterPage'; import { RegisterSuccessPage } from '@/pages/auth/RegisterSuccessPage'; import { ResetPasswordRequestPage } from '@/pages/auth/ResetPasswordRequestPage'; import { ResetPasswordPage } from '@/pages/auth/ResetPasswordPage'; +import { SitesListPage } from '@/pages/sites/SitesListPage'; +import { SiteFormPage } from '@/pages/sites/SiteFormPage'; +import { ContainersListPage } from '@/pages/containers/ContainersListPage'; +import { ContainerFormPage } from '@/pages/containers/ContainerFormPage'; +import { NodesListPage } from '@/pages/nodes/NodesListPage'; +import { NodeFormPage } from '@/pages/nodes/NodeFormPage'; +import { NodeImportPage } from '@/pages/nodes/NodeImportPage'; +import { ExternalDomainsListPage } from '@/pages/external-domains/ExternalDomainsListPage'; +import { ExternalDomainFormPage } from '@/pages/external-domains/ExternalDomainFormPage'; +import { JobDetailPage } from '@/pages/jobs/JobDetailPage'; +import { UsersListPage } from '@/pages/users/UsersListPage'; +import { UserFormPage } from '@/pages/users/UserFormPage'; +import { InviteUserPage } from '@/pages/users/InviteUserPage'; +import { GroupsListPage } from '@/pages/groups/GroupsListPage'; +import { GroupFormPage } from '@/pages/groups/GroupFormPage'; +import { ApiKeysListPage } from '@/pages/apikeys/ApiKeysListPage'; +import { SettingsPage } from '@/pages/settings/SettingsPage'; +import { NotFoundPage } from '@/pages/NotFoundPage'; export const router = createBrowserRouter([ { @@ -23,38 +40,47 @@ export const router = createBrowserRouter([ }, { element: , - children: [{ - element: , children: [ - { index: true, element: }, - { path: '/sites', element: }, - { path: '/sites/new', element: }, - { path: '/sites/:id/edit', element: }, - { path: '/sites/:siteId/containers', element: }, - { path: '/sites/:siteId/containers/new', element: }, - { path: '/sites/:siteId/containers/:id/edit', element: }, - { path: '/sites/:siteId/nodes', element: }, - { path: '/sites/:siteId/nodes/new', element: }, - { path: '/sites/:siteId/nodes/import', element: }, - { path: '/sites/:siteId/nodes/:id/edit', element: }, - { path: '/external-domains', element: }, - { path: '/external-domains/new', element: }, - { path: '/external-domains/:id/edit', element: }, - { path: '/jobs/:id', element: }, - { path: '/users', element: }, - { path: '/users/new', element: }, - { path: '/users/invite', element: }, - { path: '/users/:uid/edit', element: }, - { path: '/groups', element: }, - { path: '/groups/new', element: }, - { path: '/groups/:id/edit', element: }, - { path: '/apikeys', element: }, - { path: '/apikeys/new', element: }, - { path: '/apikeys/created', element: }, - { path: '/apikeys/:id', element: }, - { path: '/settings', element: }, - { path: '*', element: }, + { + element: , + children: [ + { index: true, element: }, + + { path: '/sites', element: }, + { path: '/sites/new', element: }, + { path: '/sites/:id/edit', element: }, + + { path: '/sites/:siteId/containers', element: }, + { path: '/sites/:siteId/containers/new', element: }, + { path: '/sites/:siteId/containers/:id/edit', element: }, + + { path: '/sites/:siteId/nodes', element: }, + { path: '/sites/:siteId/nodes/new', element: }, + { path: '/sites/:siteId/nodes/import', element: }, + { path: '/sites/:siteId/nodes/:id/edit', element: }, + + { path: '/external-domains', element: }, + { path: '/external-domains/new', element: }, + { path: '/external-domains/:id/edit', element: }, + + { path: '/jobs/:id', element: }, + + { path: '/users', element: }, + { path: '/users/new', element: }, + { path: '/users/invite', element: }, + { path: '/users/:uid/edit', element: }, + + { path: '/groups', element: }, + { path: '/groups/new', element: }, + { path: '/groups/:id/edit', element: }, + + { path: '/apikeys', element: }, + + { path: '/settings', element: }, + + { path: '*', element: }, + ], + }, ], - }], }, ]); diff --git a/create-a-container/client/src/lib/queries.ts b/create-a-container/client/src/lib/queries.ts new file mode 100644 index 00000000..0d4a5e0e --- /dev/null +++ b/create-a-container/client/src/lib/queries.ts @@ -0,0 +1,91 @@ +/** + * Centralized query keys + fetcher functions for TanStack Query. + * Each `keys.x()` returns a stable tuple that callers pass to useQuery. + */ +import { api } from './api'; +import type { + ApiKey, + Container, + ContainerMetadata, + ContainerNewBootstrap, + ExternalDomain, + Group, + Job, + JobStatusRow, + Node, + AppSettings, + Site, + User, +} from './types'; + +export const keys = { + sites: () => ['sites'] as const, + site: (id: number | string) => ['sites', String(id)] as const, + nodes: (siteId: number | string) => ['sites', String(siteId), 'nodes'] as const, + node: (siteId: number | string, id: number | string) => + ['sites', String(siteId), 'nodes', String(id)] as const, + containers: (siteId: number | string) => ['sites', String(siteId), 'containers'] as const, + container: (siteId: number | string, id: number | string) => + ['sites', String(siteId), 'containers', String(id)] as const, + containerBootstrap: (siteId: number | string) => + ['sites', String(siteId), 'containers', 'new'] as const, + containerMetadata: (image: string) => ['container-metadata', image] as const, + externalDomains: () => ['external-domains'] as const, + externalDomain: (id: number | string) => ['external-domains', String(id)] as const, + users: () => ['users'] as const, + user: (uid: number | string) => ['users', String(uid)] as const, + groups: () => ['groups'] as const, + group: (id: number | string) => ['groups', String(id)] as const, + apikeys: () => ['apikeys'] as const, + settings: () => ['settings'] as const, + job: (id: number | string) => ['jobs', String(id)] as const, + jobStatuses: (id: number | string) => ['jobs', String(id), 'statuses'] as const, +}; + +export const queries = { + // Sites + listSites: () => api.get('/api/v1/sites'), + getSite: (id: number | string) => api.get(`/api/v1/sites/${id}`), + + // Nodes + listNodes: (siteId: number | string) => + api.get(`/api/v1/sites/${siteId}/nodes`), + getNode: (siteId: number | string, id: number | string) => + api.get(`/api/v1/sites/${siteId}/nodes/${id}`), + + // Containers + listContainers: (siteId: number | string) => + api.get(`/api/v1/sites/${siteId}/containers`), + getContainer: (siteId: number | string, id: number | string) => + api.get(`/api/v1/sites/${siteId}/containers/${id}`), + containerBootstrap: (siteId: number | string) => + api.get(`/api/v1/sites/${siteId}/containers/new`), + containerMetadata: (siteId: number | string, image: string) => + api.get( + `/api/v1/sites/${siteId}/containers/metadata?image=${encodeURIComponent(image)}`, + ), + + // External domains + listExternalDomains: () => api.get('/api/v1/external-domains'), + getExternalDomain: (id: number | string) => + api.get(`/api/v1/external-domains/${id}`), + + // Users + listUsers: () => api.get('/api/v1/users'), + getUser: (uid: number | string) => api.get(`/api/v1/users/${uid}`), + + // Groups + listGroups: () => api.get('/api/v1/groups'), + getGroup: (id: number | string) => api.get(`/api/v1/groups/${id}`), + + // API keys + listApiKeys: () => api.get('/api/v1/apikeys'), + + // Settings + getSettings: () => api.get('/api/v1/settings'), + + // Jobs + getJob: (id: number | string) => api.get(`/api/v1/jobs/${id}`), + getJobStatuses: (id: number | string, offset = 0, limit = 1000) => + api.get(`/api/v1/jobs/${id}/status?offset=${offset}&limit=${limit}`), +}; diff --git a/create-a-container/client/src/lib/toast.ts b/create-a-container/client/src/lib/toast.ts new file mode 100644 index 00000000..334fc53a --- /dev/null +++ b/create-a-container/client/src/lib/toast.ts @@ -0,0 +1,17 @@ +import { useToast } from '@mieweb/ui'; +import type { ApiError } from './api'; + +/** + * Centralized error-to-toast helper. Pass to mutation `onError`. + */ +export function useApiErrorToast() { + const { error } = useToast(); + return (err: unknown) => { + const e = err as ApiError; + if (e && typeof e === 'object' && 'message' in e) { + error(e.message || 'Request failed'); + } else { + error('Request failed'); + } + }; +} diff --git a/create-a-container/client/src/lib/types.ts b/create-a-container/client/src/lib/types.ts new file mode 100644 index 00000000..f18eb549 --- /dev/null +++ b/create-a-container/client/src/lib/types.ts @@ -0,0 +1,169 @@ +/** + * Typed resource models matching /api/v1 response shapes. + * Keep in sync with the serializers in routers/api/v1/*. + */ + +export interface Site { + id: number; + name: string; + internalDomain: string; + dhcpRange: string | null; + subnetMask: string | null; + gateway: string | null; + dnsForwarders: string | null; + externalIp: string | null; + nodeCount?: number; +} + +export interface Node { + id: number; + name: string; + siteId: number; + ipv4Address: string | null; + apiUrl: string | null; + tokenId: string | null; + tlsVerify: boolean | null; + imageStorage: string; + volumeStorage: string; + networkBridge: string; + nvidiaAvailable: boolean; + hasSecret: boolean; +} + +export interface ExternalDomain { + id: number; + name: string; + acmeEmail: string | null; + acmeDirectoryUrl: string | null; + cloudflareApiEmail: string | null; + siteId: number | null; + site: { id: number; name: string } | null; + authServer: string | null; + hasCloudflareApiKey: boolean; +} + +export interface ServiceHttp { + id: number; + externalHostname: string; + externalDomainId: number; + backendProtocol: 'http' | 'https'; + authRequired: boolean; + domain?: string; +} +export interface ServiceTransport { + id: number; + protocol: 'tcp' | 'udp'; + externalPort: number; +} +export interface ServiceDns { + id: number; + recordType: string; + dnsName: string; +} +export interface ContainerService { + id: number; + type: 'http' | 'transport' | 'dns'; + internalPort: number; + httpService: ServiceHttp | null; + transportService: ServiceTransport | null; + dnsService: ServiceDns | null; +} + +export interface Container { + id: number; + containerId: number | null; + hostname: string; + ipv4Address: string | null; + macAddress: string | null; + status: string; + template: string | null; + creationJobId: number | null; + entrypoint: string | null; + environmentVars: Record; + nvidiaRequested: boolean; + sshPort: number | null; + sshHost: string | null; + httpEntries: { port: number; externalUrl: string | null }[]; + nodeName: string | null; + services: ContainerService[]; + createdAt: string; +} + +export interface ContainerCreateResult { + containerId: number; + jobId: number; + hostname: string; + status: string; +} + +export interface ContainerNewBootstrap { + siteId: number; + externalDomains: { id: number; name: string }[]; + nvidiaAvailable: boolean; +} + +export interface ContainerMetadata { + exposedPorts?: string[]; + entrypoint?: string[] | string; + cmd?: string[] | string; + env?: string[]; +} + +export interface Job { + id: number; + command: string; + status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'; + createdAt: string; + updatedAt: string; + createdBy: string; +} + +export interface JobStatusRow { + id: number; + jobId: number; + output: string; + createdAt: string; +} + +export interface User { + uidNumber: number; + uid: string; + givenName: string; + sn: string; + cn: string; + mail: string; + status: 'pending' | 'active' | 'disabled'; + groups?: { gidNumber: number; cn: string; isAdmin: boolean }[]; + isAdmin: boolean; + twoFactorWarning?: string; +} + +export interface Group { + gidNumber: number; + cn: string; + isAdmin: boolean; + userCount?: number; +} + +export interface ApiKey { + id: number; + keyPrefix: string; + description: string | null; + lastUsedAt: string | null; + createdAt: string; + updatedAt: string; +} + +export interface ApiKeyCreated extends ApiKey { + key: string; + warning: string; +} + +export interface AppSettings { + pushNotificationUrl: string; + pushNotificationEnabled: boolean; + pushNotificationApiKey: string; + smtpUrl: string; + smtpNoreplyAddress: string; + defaultContainerEnvVars: { key: string; value: string; description?: string }[]; +} diff --git a/create-a-container/client/src/pages/NotFoundPage.tsx b/create-a-container/client/src/pages/NotFoundPage.tsx new file mode 100644 index 00000000..eecbacdc --- /dev/null +++ b/create-a-container/client/src/pages/NotFoundPage.tsx @@ -0,0 +1,12 @@ +import { useNavigate } from 'react-router'; +import { ErrorPage } from '@mieweb/ui'; + +export function NotFoundPage() { + const navigate = useNavigate(); + return ( + navigate('/sites') }} + /> + ); +} diff --git a/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx b/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx new file mode 100644 index 00000000..ef5422bc --- /dev/null +++ b/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx @@ -0,0 +1,157 @@ +import { useState } from 'react'; +import { Link } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { KeyRound, Plus, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { ApiKey, ApiKeyCreated } from '@/lib/types'; + +export function ApiKeysListPage() { + const { data, isLoading, error } = useQuery({ queryKey: keys.apikeys(), queryFn: queries.listApiKeys }); + const qc = useQueryClient(); + const toast = useToast(); + const [creating, setCreating] = useState(false); + const [description, setDescription] = useState(''); + const [created, setCreated] = useState(null); + + const createMutation = useMutation({ + mutationFn: () => api.post('/api/v1/apikeys', { description }), + onSuccess: (key) => { + setCreated(key); + setDescription(''); + setCreating(false); + qc.invalidateQueries({ queryKey: keys.apikeys() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/apikeys/${id}`), + onSuccess: () => { + toast.success('API key revoked'); + qc.invalidateQueries({ queryKey: keys.apikeys() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ + !creating && ( + + ) + } + /> + + {created && ( + + +
+ {created.warning} +
+ + {created.key} + + +
+
+ { e.preventDefault(); setCreated(null); }}> + Dismiss + +
+
+
+
+ )} + + {creating && ( +
{ + e.preventDefault(); + createMutation.mutate(); + }} + className="flex flex-col gap-3 rounded-lg border border-(--color-border,#e5e7eb) p-4" + > + setDescription(e.target.value)} + required + /> +
+ + +
+
+ )} + + {error && {(error as ApiError).message}} + {isLoading &&
} + {data && ( + + + + Prefix + Description + Last used + Created + Actions + + + + {data.map((k: ApiKey) => ( + + {k.keyPrefix}… + {k.description || '—'} + {k.lastUsedAt ? new Date(k.lastUsedAt).toLocaleString() : 'Never'} + {new Date(k.createdAt).toLocaleDateString()} + + + + + ))} + +
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/containers/ContainerFormPage.tsx b/create-a-container/client/src/pages/containers/ContainerFormPage.tsx new file mode 100644 index 00000000..8dc5d019 --- /dev/null +++ b/create-a-container/client/src/pages/containers/ContainerFormPage.tsx @@ -0,0 +1,389 @@ +import { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useFieldArray, useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Select, + Spinner, + Switch, + useToast, +} from '@mieweb/ui'; +import { Plus, Search, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { ContainerCreateResult, ContainerMetadata } from '@/lib/types'; + +const SERVICE_TYPES = [ + { value: 'http', label: 'HTTP' }, + { value: 'https', label: 'HTTPS (backend TLS)' }, + { value: 'tcp', label: 'TCP' }, + { value: 'udp', label: 'UDP' }, + { value: 'srv', label: 'DNS (SRV record)' }, +]; + +const serviceSchema = z.object({ + id: z.number().optional(), + type: z.enum(['http', 'https', 'tcp', 'udp', 'srv']), + internalPort: z.string(), + externalHostname: z.string().optional(), + externalDomainId: z.string().optional(), + dnsName: z.string().optional(), + authRequired: z.boolean().optional(), + deleted: z.boolean().optional(), +}); + +const envVarSchema = z.object({ key: z.string(), value: z.string() }); + +const schema = z.object({ + hostname: z.string().min(1, 'Required'), + template: z.string().optional(), + customTemplate: z.string().optional(), + entrypoint: z.string().optional(), + nvidiaRequested: z.boolean().optional(), + restart: z.boolean().optional(), + services: z.array(serviceSchema), + environmentVars: z.array(envVarSchema), +}); +type FormData = z.infer; + +const COMMON_TEMPLATES = [ + 'ubuntu-22.04-standard', + 'ubuntu-24.04-standard', + 'debian-12-standard', + 'docker:nginx:latest', + 'docker:postgres:16', +]; + +export function ContainerFormPage() { + const { siteId, id } = useParams<{ siteId: string; id?: string }>(); + const isEdit = !!id; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: bootstrap, isLoading: bootstrapLoading } = useQuery({ + queryKey: keys.containerBootstrap(siteId!), + queryFn: () => queries.containerBootstrap(siteId!), + enabled: !!siteId, + }); + const { data: container, isLoading: containerLoading } = useQuery({ + queryKey: keys.container(siteId!, id ?? 'new'), + queryFn: () => queries.getContainer(siteId!, id!), + enabled: isEdit, + }); + + const { register, handleSubmit, control, reset, watch, setValue, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + services: [], + environmentVars: [], + nvidiaRequested: false, + restart: false, + }, + }); + const services = useFieldArray({ control, name: 'services' }); + const envVars = useFieldArray({ control, name: 'environmentVars' }); + const template = watch('template'); + const nvidiaRequested = watch('nvidiaRequested'); + const restart = watch('restart'); + + useEffect(() => { + if (container) { + reset({ + hostname: container.hostname, + template: container.template || '', + entrypoint: container.entrypoint || '', + nvidiaRequested: container.nvidiaRequested, + restart: false, + services: container.services.map((s) => ({ + id: s.id, + type: + s.type === 'dns' + ? 'srv' + : s.type === 'http' + ? s.httpService?.backendProtocol === 'https' + ? 'https' + : 'http' + : (s.transportService?.protocol ?? 'tcp'), + internalPort: String(s.internalPort), + externalHostname: s.httpService?.externalHostname || '', + externalDomainId: s.httpService ? String(s.httpService.externalDomainId) : '', + dnsName: s.dnsService?.dnsName || '', + authRequired: !!s.httpService?.authRequired, + deleted: false, + })), + environmentVars: Object.entries(container.environmentVars || {}).map(([key, value]) => ({ key, value })), + }); + } + }, [container, reset]); + + const [metadataMsg, setMetadataMsg] = useState(null); + const metadataMutation = useMutation({ + mutationFn: (image: string) => queries.containerMetadata(siteId!, image), + onSuccess: (meta: ContainerMetadata) => { + const ports = (meta.exposedPorts || []).filter((p) => /^\d+(\/\w+)?$/.test(p)); + let added = 0; + ports.forEach((p) => { + const [portStr, proto] = p.split('/'); + const port = parseInt(portStr, 10); + const t = !proto || proto === 'tcp' ? 'http' : 'tcp'; + services.append({ + type: t as FormData['services'][number]['type'], + internalPort: String(port), + externalHostname: '', + externalDomainId: '', + dnsName: '', + authRequired: false, + deleted: false, + }); + added += 1; + }); + if (meta.entrypoint) { + const ep = Array.isArray(meta.entrypoint) ? meta.entrypoint.join(' ') : meta.entrypoint; + setValue('entrypoint', ep); + } + if (Array.isArray(meta.env)) { + meta.env.forEach((e) => { + const eq = e.indexOf('='); + if (eq > 0) envVars.append({ key: e.slice(0, eq), value: e.slice(eq + 1) }); + }); + } + setMetadataMsg(`Loaded metadata: ${added} port(s) discovered.`); + }, + onError: (err: ApiError) => setMetadataMsg(err.message), + }); + + const mutation = useMutation({ + mutationFn: (values: FormData) => { + const servicesObj: Record = {}; + values.services.forEach((s, idx) => { + if (s.deleted && s.id) { + servicesObj[`s${idx}`] = { id: s.id, deleted: true }; + return; + } + if (s.deleted) return; + servicesObj[`s${idx}`] = { + id: s.id, + type: s.type, + internalPort: s.internalPort ? parseInt(s.internalPort, 10) : undefined, + externalHostname: s.externalHostname, + externalDomainId: s.externalDomainId ? parseInt(s.externalDomainId, 10) : undefined, + dnsName: s.dnsName, + authRequired: s.authRequired, + }; + }); + const payload = { + hostname: values.hostname, + template: values.template === 'custom' ? values.customTemplate : values.template, + customTemplate: values.customTemplate, + entrypoint: values.entrypoint, + nvidiaRequested: values.nvidiaRequested, + services: servicesObj, + environmentVars: values.environmentVars.filter((e) => e.key.trim()), + restart: values.restart, + }; + type UpdateResult = { containerId: number; jobId: number | null; message: string; dnsWarnings: string[] }; + type SaveResult = UpdateResult | ContainerCreateResult; + return (isEdit + ? api.put(`/api/v1/sites/${siteId}/containers/${id}`, payload) + : api.post(`/api/v1/sites/${siteId}/containers`, payload) + ) as Promise; + }, + onSuccess: (result) => { + const dnsWarnings = (result as { dnsWarnings?: string[] }).dnsWarnings; + if (dnsWarnings && dnsWarnings.length > 0) { + toast.warning(`Saved with DNS warnings: ${dnsWarnings.join('; ')}`); + } else { + toast.success(isEdit ? 'Container updated' : 'Container queued for creation'); + } + qc.invalidateQueries({ queryKey: keys.containers(siteId!) }); + const jobId = (result as { jobId?: number | null }).jobId; + if (jobId) { + navigate(`/jobs/${jobId}`); + } else { + navigate(`/sites/${siteId}/containers`); + } + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + if ((isEdit && containerLoading) || bootstrapLoading) { + return
; + } + + const domainOptions = [ + { value: '', label: '— None —' }, + ...(bootstrap?.externalDomains.map((d) => ({ value: String(d.id), label: d.name })) || []), + ]; + const templateOptions = [ + { value: '', label: 'Select a template' }, + ...COMMON_TEMPLATES.map((t) => ({ value: t, label: t })), + { value: 'custom', label: 'Custom…' }, + ]; + + return ( +
+ +
mutation.mutate(v))} noValidate className="grid max-w-4xl gap-6"> + +
+

Basic

+ + {!isEdit && ( + <> + + )} +
+
+ +
+ +
+ {metadataMsg &&

{metadataMsg}

} + + )} + + {bootstrap?.nvidiaAvailable && ( + setValue('nvidiaRequested', c)} + /> + )} + {isEdit && ( + setValue('restart', c)} + /> + )} +
+ +
+
+

Services

+ +
+ {services.fields.length === 0 &&

No services defined.

} + {services.fields.map((f, idx) => { + const svc = watch(`services.${idx}`); + if (svc.deleted) return null; + return ( +
+
+ + +
+ {(svc.type === 'http' || svc.type === 'https') && ( +
+ + + )} +
+ ); + })} +
+ +
+
+

Environment variables

+ +
+ {envVars.fields.map((f, idx) => ( +
+ + + +
+ ))} +
+ + {mutation.error && {(mutation.error as ApiError).message}} + +
+ + +
+
+
+ ); +} diff --git a/create-a-container/client/src/pages/containers/ContainersListPage.tsx b/create-a-container/client/src/pages/containers/ContainersListPage.tsx new file mode 100644 index 00000000..ec53a62c --- /dev/null +++ b/create-a-container/client/src/pages/containers/ContainersListPage.tsx @@ -0,0 +1,188 @@ +import { Link, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + AlertTitle, + Badge, + Button, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { Container as ContainerIcon, ExternalLink, Pencil, Plus, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Container } from '@/lib/types'; + +function statusVariant(s: string): 'default' | 'success' | 'warning' | 'danger' | 'secondary' { + switch (s) { + case 'running': + return 'success'; + case 'pending': + case 'restarting': + return 'warning'; + case 'failed': + case 'error': + return 'danger'; + case 'stopped': + return 'secondary'; + default: + return 'default'; + } +} + +export function ContainersListPage() { + const { siteId } = useParams<{ siteId: string }>(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: site } = useQuery({ + queryKey: keys.site(siteId!), + queryFn: () => queries.getSite(siteId!), + enabled: !!siteId, + }); + const { data, isLoading, error } = useQuery({ + queryKey: keys.containers(siteId!), + queryFn: () => queries.listContainers(siteId!), + enabled: !!siteId, + }); + + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/sites/${siteId}/containers/${id}`), + onSuccess: () => { + toast.success('Container deleted'); + qc.invalidateQueries({ queryKey: keys.containers(siteId!) }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ +
+ + + + + + +
+ } + /> + + {error && ( + + {(error as ApiError).message} + + )} + {isLoading && ( +
+ +
+ )} + {data && data.length === 0 && ( + + No containers + Create your first container with the button above. + + )} + + {data && data.length > 0 && ( + + + + Hostname + Status + Node + Template + HTTP + SSH + Actions + + + + {data.map((c: Container) => ( + + {c.hostname} + + {c.status} + + {c.nodeName || '—'} + + {c.template || '—'} + + + {c.httpEntries.length === 0 ? ( + '—' + ) : ( +
+ {c.httpEntries.slice(0, 2).map((h) => + h.externalUrl ? ( + + + {h.externalUrl.replace(/^https?:\/\//, '')} + + ) : ( + + :{h.port} + + ), + )} +
+ )} +
+ + {c.sshHost && c.sshPort ? `${c.sshHost}:${c.sshPort}` : '—'} + + + {c.creationJobId && ( + + + + )} + + + + + +
+ ))} +
+
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/external-domains/ExternalDomainFormPage.tsx b/create-a-container/client/src/pages/external-domains/ExternalDomainFormPage.tsx new file mode 100644 index 00000000..1d818ab9 --- /dev/null +++ b/create-a-container/client/src/pages/external-domains/ExternalDomainFormPage.tsx @@ -0,0 +1,143 @@ +import { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Select, + Spinner, + useToast, +} from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { ExternalDomain } from '@/lib/types'; + +const schema = z.object({ + name: z.string().min(1, 'Required'), + siteId: z.string().optional(), + acmeEmail: z.string().email('Must be a valid email').or(z.literal('')).optional(), + acmeDirectoryUrl: z.string().url('Must be a valid URL').or(z.literal('')).optional(), + cloudflareApiEmail: z.string().email('Must be a valid email').or(z.literal('')).optional(), + cloudflareApiKey: z.string().optional(), + authServer: z.string().optional(), +}); +type FormData = z.infer; + +export function ExternalDomainFormPage() { + const { id } = useParams<{ id?: string }>(); + const isEdit = !!id; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: domain, isLoading } = useQuery({ + queryKey: keys.externalDomain(id ?? 'new'), + queryFn: () => queries.getExternalDomain(id!), + enabled: isEdit, + }); + const { data: sites } = useQuery({ queryKey: keys.sites(), queryFn: queries.listSites }); + + const { register, handleSubmit, reset, setValue, watch, formState } = useForm({ + resolver: zodResolver(schema), + }); + const siteIdValue = watch('siteId'); + + useEffect(() => { + if (domain) { + reset({ + name: domain.name, + siteId: domain.siteId ? String(domain.siteId) : '', + acmeEmail: domain.acmeEmail || '', + acmeDirectoryUrl: domain.acmeDirectoryUrl || '', + cloudflareApiEmail: domain.cloudflareApiEmail || '', + cloudflareApiKey: '', + authServer: domain.authServer || '', + }); + } + }, [domain, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => { + const payload = { + ...values, + siteId: values.siteId ? parseInt(values.siteId, 10) : null, + }; + return isEdit + ? api.put(`/api/v1/external-domains/${id}`, payload) + : api.post('/api/v1/external-domains', payload); + }, + onSuccess: () => { + toast.success(isEdit ? 'External domain updated' : 'External domain created'); + qc.invalidateQueries({ queryKey: keys.externalDomains() }); + navigate('/external-domains'); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + const onSubmit = handleSubmit((v) => mutation.mutate(v)); + + if (isEdit && isLoading) { + return ( +
+ +
+ ); + } + + return ( +
+ +
+ + + + + + + + {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} +
+ + +
+
+
+ ); +} diff --git a/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx b/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx new file mode 100644 index 00000000..6cc130d9 --- /dev/null +++ b/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx @@ -0,0 +1,112 @@ +import { Link } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Badge, + Button, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { Globe, Pencil, Plus, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { ExternalDomain } from '@/lib/types'; + +export function ExternalDomainsListPage() { + const { data, isLoading, error } = useQuery({ + queryKey: keys.externalDomains(), + queryFn: queries.listExternalDomains, + }); + const qc = useQueryClient(); + const toast = useToast(); + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/external-domains/${id}`), + onSuccess: () => { + toast.success('External domain deleted'); + qc.invalidateQueries({ queryKey: keys.externalDomains() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ + + + + } + /> + {error && ( + + {(error as ApiError).message} + + )} + {isLoading && ( +
+ +
+ )} + {data && ( + + + + Domain + Site + Cloudflare + Auth server + Actions + + + + {data.map((d: ExternalDomain) => ( + + {d.name} + {d.site?.name || '—'} + + {d.hasCloudflareApiKey ? ( + Configured + ) : ( + Not configured + )} + + {d.authServer || '—'} + + + + + + + + ))} + +
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/groups/GroupFormPage.tsx b/create-a-container/client/src/pages/groups/GroupFormPage.tsx new file mode 100644 index 00000000..a3f0d664 --- /dev/null +++ b/create-a-container/client/src/pages/groups/GroupFormPage.tsx @@ -0,0 +1,101 @@ +import { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Checkbox, + Input, + PageHeader, + Spinner, + useToast, +} from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Group } from '@/lib/types'; + +const schema = z.object({ + gidNumber: z.string().min(1, 'Required').regex(/^\d+$/, 'Must be a positive integer'), + cn: z.string().min(1, 'Required'), + isAdmin: z.boolean().optional(), +}); +type FormData = z.infer; + +export function GroupFormPage() { + const { id } = useParams<{ id?: string }>(); + const isEdit = !!id; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: group, isLoading } = useQuery({ + queryKey: keys.group(id ?? 'new'), + queryFn: () => queries.getGroup(id!), + enabled: isEdit, + }); + + const { register, handleSubmit, reset, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { isAdmin: false }, + }); + + useEffect(() => { + if (group) { + reset({ gidNumber: String(group.gidNumber), cn: group.cn, isAdmin: group.isAdmin }); + } + }, [group, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => { + const payload = { ...values, gidNumber: parseInt(values.gidNumber, 10) }; + return isEdit + ? api.put(`/api/v1/groups/${id}`, payload) + : api.post('/api/v1/groups', payload); + }, + onSuccess: () => { + toast.success(isEdit ? 'Group updated' : 'Group created'); + qc.invalidateQueries({ queryKey: keys.groups() }); + navigate('/groups'); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + if (isEdit && isLoading) { + return
; + } + + return ( +
+ +
mutation.mutate(v))} noValidate className="grid max-w-md gap-4"> + + + + {mutation.error && {(mutation.error as ApiError).message}} +
+ + +
+ +
+ ); +} diff --git a/create-a-container/client/src/pages/groups/GroupsListPage.tsx b/create-a-container/client/src/pages/groups/GroupsListPage.tsx new file mode 100644 index 00000000..449a7cb6 --- /dev/null +++ b/create-a-container/client/src/pages/groups/GroupsListPage.tsx @@ -0,0 +1,92 @@ +import { Link } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Badge, + Button, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { Pencil, Plus, Trash2, UsersRound } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Group } from '@/lib/types'; + +export function GroupsListPage() { + const { data, isLoading, error } = useQuery({ queryKey: keys.groups(), queryFn: queries.listGroups }); + const qc = useQueryClient(); + const toast = useToast(); + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/groups/${id}`), + onSuccess: () => { + toast.success('Group deleted'); + qc.invalidateQueries({ queryKey: keys.groups() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ + + + + } + /> + {error && {(error as ApiError).message}} + {isLoading &&
} + {data && ( + + + + GID + Name + Admin + Users + Actions + + + + {data.map((g: Group) => ( + + {g.gidNumber} + {g.cn} + {g.isAdmin ? Admin : No} + {g.userCount ?? 0} + + + + + + + + ))} + +
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/jobs/JobDetailPage.tsx b/create-a-container/client/src/pages/jobs/JobDetailPage.tsx new file mode 100644 index 00000000..d5c4035b --- /dev/null +++ b/create-a-container/client/src/pages/jobs/JobDetailPage.tsx @@ -0,0 +1,145 @@ +import { useEffect, useRef, useState } from 'react'; +import { Link, useParams } from 'react-router'; +import { useQuery } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Badge, + Button, + PageHeader, + Spinner, +} from '@mieweb/ui'; +import { ArrowLeft, Terminal } from 'lucide-react'; +import { ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { JobStatusRow } from '@/lib/types'; + +interface SseLogEvent { + id: number; + output: string; + timestamp: string; +} + +function statusVariant(s: string): 'default' | 'success' | 'warning' | 'danger' | 'secondary' { + switch (s) { + case 'running': + return 'warning'; + case 'completed': + return 'success'; + case 'failed': + return 'danger'; + case 'cancelled': + return 'secondary'; + default: + return 'default'; + } +} + +export function JobDetailPage() { + const { id } = useParams<{ id: string }>(); + const { data: job, isLoading, error, refetch } = useQuery({ + queryKey: keys.job(id!), + queryFn: () => queries.getJob(id!), + enabled: !!id, + }); + + const [logs, setLogs] = useState([]); + const [liveStatus, setLiveStatus] = useState(null); + const containerRef = useRef(null); + + // Initial backfill of statuses. + useEffect(() => { + if (!id) return; + queries.getJobStatuses(id).then(setLogs).catch(() => undefined); + }, [id]); + + // Live SSE stream — only while job is pending/running. + useEffect(() => { + if (!id || !job) return; + if (!['pending', 'running'].includes(job.status)) return; + + const lastId = logs.length > 0 ? logs[logs.length - 1].id : 0; + const source = new EventSource(`/api/v1/jobs/${id}/stream?lastId=${lastId}`); + + source.addEventListener('log', (ev: MessageEvent) => { + try { + const data = JSON.parse(ev.data) as SseLogEvent; + setLogs((prev) => + prev.some((r) => r.id === data.id) + ? prev + : [...prev, { id: data.id, jobId: Number(id), output: data.output, createdAt: data.timestamp }], + ); + } catch { + /* ignore malformed payload */ + } + }); + source.addEventListener('status', (ev: MessageEvent) => { + try { + const data = JSON.parse(ev.data) as { status: string }; + setLiveStatus(data.status); + refetch(); + } catch { + /* ignore */ + } + source.close(); + }); + source.onerror = () => source.close(); + + return () => source.close(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id, job?.status]); + + // Auto-scroll on new log lines. + useEffect(() => { + const el = containerRef.current; + if (el) el.scrollTop = el.scrollHeight; + }, [logs.length]); + + if (isLoading) return
; + if (error || !job) { + return ( + + {(error as ApiError | null)?.message || 'Job not found'} + + ); + } + + const effectiveStatus = liveStatus || job.status; + + return ( +
+ } + actions={ + + + + } + /> + +
+ {effectiveStatus} + + Started {new Date(job.createdAt).toLocaleString()} · by {job.createdBy} + +
+ +
+ {logs.length === 0 ? ( + No log output yet… + ) : ( + logs.map((row) => ( +
+              {row.output}
+            
+ )) + )} +
+
+ ); +} diff --git a/create-a-container/client/src/pages/nodes/NodeFormPage.tsx b/create-a-container/client/src/pages/nodes/NodeFormPage.tsx new file mode 100644 index 00000000..55976af7 --- /dev/null +++ b/create-a-container/client/src/pages/nodes/NodeFormPage.tsx @@ -0,0 +1,129 @@ +import { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Spinner, + Switch, + useToast, +} from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Node } from '@/lib/types'; + +const schema = z.object({ + name: z.string().min(1, 'Required'), + ipv4Address: z.string().optional(), + apiUrl: z.string().url('Must be a valid URL').or(z.literal('')).optional(), + tokenId: z.string().optional(), + secret: z.string().optional(), + tlsVerify: z.boolean().optional(), + imageStorage: z.string().min(1, 'Required'), + volumeStorage: z.string().min(1, 'Required'), + networkBridge: z.string().min(1, 'Required'), + nvidiaAvailable: z.boolean().optional(), +}); +type FormData = z.infer; + +export function NodeFormPage() { + const { siteId, id } = useParams<{ siteId: string; id?: string }>(); + const isEdit = !!id; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: node, isLoading } = useQuery({ + queryKey: keys.node(siteId!, id ?? 'new'), + queryFn: () => queries.getNode(siteId!, id!), + enabled: isEdit, + }); + + const { register, handleSubmit, reset, watch, setValue, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + tlsVerify: true, + nvidiaAvailable: false, + imageStorage: 'local', + volumeStorage: 'local-lvm', + networkBridge: 'vmbr0', + }, + }); + const tlsVerify = watch('tlsVerify'); + const nvidiaAvailable = watch('nvidiaAvailable'); + + useEffect(() => { + if (node) { + reset({ + name: node.name, + ipv4Address: node.ipv4Address || '', + apiUrl: node.apiUrl || '', + tokenId: node.tokenId || '', + secret: '', + tlsVerify: node.tlsVerify ?? true, + imageStorage: node.imageStorage, + volumeStorage: node.volumeStorage, + networkBridge: node.networkBridge, + nvidiaAvailable: node.nvidiaAvailable, + }); + } + }, [node, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => { + const payload = { ...values }; + if (isEdit && !values.secret) delete payload.secret; + return isEdit + ? api.put(`/api/v1/sites/${siteId}/nodes/${id}`, payload) + : api.post(`/api/v1/sites/${siteId}/nodes`, payload); + }, + onSuccess: () => { + toast.success(isEdit ? 'Node updated' : 'Node created'); + qc.invalidateQueries({ queryKey: keys.nodes(siteId!) }); + navigate(`/sites/${siteId}/nodes`); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + if (isEdit && isLoading) return
; + + return ( +
+ +
mutation.mutate(v))} noValidate className="grid max-w-2xl gap-4"> + + + + + + setValue('tlsVerify', c)} /> +
+ + + +
+ setValue('nvidiaAvailable', c)} /> + + {mutation.error && {(mutation.error as ApiError).message}} +
+ + +
+ +
+ ); +} diff --git a/create-a-container/client/src/pages/nodes/NodeImportPage.tsx b/create-a-container/client/src/pages/nodes/NodeImportPage.tsx new file mode 100644 index 00000000..bfd4ef03 --- /dev/null +++ b/create-a-container/client/src/pages/nodes/NodeImportPage.tsx @@ -0,0 +1,80 @@ +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + AlertTitle, + Button, + Input, + PageHeader, + Switch, + useToast, +} from '@mieweb/ui'; +import { Download } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys } from '@/lib/queries'; +import type { Node } from '@/lib/types'; + +const schema = z.object({ + apiUrl: z.string().url('Must be a valid URL'), + username: z.string().min(1, 'Required'), + password: z.string().min(1, 'Required'), + tlsVerify: z.boolean().optional(), +}); +type FormData = z.infer; + +interface ImportResult { + nodes: Node[]; + importedContainerCount: number; +} + +export function NodeImportPage() { + const { siteId } = useParams<{ siteId: string }>(); + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { register, handleSubmit, watch, setValue, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { tlsVerify: true }, + }); + const tlsVerify = watch('tlsVerify'); + + const mutation = useMutation({ + mutationFn: (v: FormData) => api.post(`/api/v1/sites/${siteId}/nodes/import`, v), + onSuccess: (r) => { + toast.success(`Imported ${r.nodes.length} node(s) and ${r.importedContainerCount} container(s)`); + qc.invalidateQueries({ queryKey: keys.nodes(siteId!) }); + qc.invalidateQueries({ queryKey: keys.containers(siteId!) }); + navigate(`/sites/${siteId}/nodes`); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } bordered /> + + Bulk import + + Sign in to a Proxmox cluster and import every node along with its existing LXC containers into this site. + + +
mutation.mutate(v))} noValidate className="grid max-w-xl gap-4"> + + + + setValue('tlsVerify', c)} /> + + {mutation.error && {(mutation.error as ApiError).message}} +
+ + +
+ +
+ ); +} diff --git a/create-a-container/client/src/pages/nodes/NodesListPage.tsx b/create-a-container/client/src/pages/nodes/NodesListPage.tsx new file mode 100644 index 00000000..ae55de90 --- /dev/null +++ b/create-a-container/client/src/pages/nodes/NodesListPage.tsx @@ -0,0 +1,103 @@ +import { Link, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Badge, + Button, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { Download, Pencil, Plus, Server, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Node } from '@/lib/types'; + +export function NodesListPage() { + const { siteId } = useParams<{ siteId: string }>(); + const qc = useQueryClient(); + const toast = useToast(); + const { data: site } = useQuery({ queryKey: keys.site(siteId!), queryFn: () => queries.getSite(siteId!), enabled: !!siteId }); + const { data, isLoading, error } = useQuery({ + queryKey: keys.nodes(siteId!), + queryFn: () => queries.listNodes(siteId!), + enabled: !!siteId, + }); + + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/sites/${siteId}/nodes/${id}`), + onSuccess: () => { + toast.success('Node deleted'); + qc.invalidateQueries({ queryKey: keys.nodes(siteId!) }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ +
+ + + + + + +
+ } + /> + {error && {(error as ApiError).message}} + {isLoading &&
} + {data && ( + + + + Name + IPv4 + API URL + NVIDIA + Credentials + Actions + + + + {data.map((n: Node) => ( + + {n.name} + {n.ipv4Address || '—'} + {n.apiUrl || '—'} + {n.nvidiaAvailable ? Available : No} + {n.hasSecret ? Set : Missing} + + + + + + + + ))} + +
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/settings/SettingsPage.tsx b/create-a-container/client/src/pages/settings/SettingsPage.tsx new file mode 100644 index 00000000..fa2b44b9 --- /dev/null +++ b/create-a-container/client/src/pages/settings/SettingsPage.tsx @@ -0,0 +1,140 @@ +import { useEffect } from 'react'; +import { useFieldArray, useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Spinner, + Switch, + useToast, +} from '@mieweb/ui'; +import { Plus, Settings as SettingsIcon, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { AppSettings } from '@/lib/types'; + +const envVarSchema = z.object({ + key: z.string(), + value: z.string(), + description: z.string().optional(), +}); + +const schema = z.object({ + pushNotificationEnabled: z.boolean(), + pushNotificationUrl: z.string(), + pushNotificationApiKey: z.string(), + smtpUrl: z.string(), + smtpNoreplyAddress: z.string(), + defaultContainerEnvVars: z.array(envVarSchema), +}).refine( + (v) => !v.pushNotificationEnabled || v.pushNotificationUrl.trim() !== '', + { path: ['pushNotificationUrl'], message: 'URL is required when push notifications are enabled' }, +); +type FormData = z.infer; + +export function SettingsPage() { + const qc = useQueryClient(); + const toast = useToast(); + const { data, isLoading, error } = useQuery({ queryKey: keys.settings(), queryFn: queries.getSettings }); + + const { register, handleSubmit, reset, control, watch, setValue, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + pushNotificationEnabled: false, + pushNotificationUrl: '', + pushNotificationApiKey: '', + smtpUrl: '', + smtpNoreplyAddress: '', + defaultContainerEnvVars: [], + }, + }); + const { fields, append, remove } = useFieldArray({ control, name: 'defaultContainerEnvVars' }); + const pushEnabled = watch('pushNotificationEnabled'); + + useEffect(() => { + if (data) reset(data); + }, [data, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => api.put('/api/v1/settings', values), + onSuccess: () => { + toast.success('Settings saved'); + qc.invalidateQueries({ queryKey: keys.settings() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + if (isLoading) return
; + if (error) return {(error as ApiError).message}; + + return ( +
+ } bordered /> +
mutation.mutate(v))} className="grid max-w-3xl gap-8"> +
+

Push notifications

+ setValue('pushNotificationEnabled', c)} + /> + + +
+ +
+

SMTP

+ + +
+ +
+
+

Default container environment variables

+ +
+ {fields.length === 0 &&

No defaults defined.

} + {fields.map((f, idx) => ( +
+ + + + +
+ ))} +
+ + {mutation.error && {(mutation.error as ApiError).message}} + +
+ +
+
+
+ ); +} diff --git a/create-a-container/client/src/pages/sites/SiteFormPage.tsx b/create-a-container/client/src/pages/sites/SiteFormPage.tsx new file mode 100644 index 00000000..cbe0c177 --- /dev/null +++ b/create-a-container/client/src/pages/sites/SiteFormPage.tsx @@ -0,0 +1,120 @@ +import { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + Spinner, + useToast, +} from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { Site } from '@/lib/types'; + +const schema = z.object({ + name: z.string().min(1, 'Required'), + internalDomain: z.string().min(1, 'Required'), + dhcpRange: z.string().optional().nullable(), + subnetMask: z.string().optional().nullable(), + gateway: z.string().optional().nullable(), + dnsForwarders: z.string().optional().nullable(), + externalIp: z.string().optional().nullable(), +}); +type FormData = z.infer; + +export function SiteFormPage() { + const { id } = useParams<{ id?: string }>(); + const isEdit = !!id; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: site, isLoading } = useQuery({ + queryKey: keys.site(id ?? 'new'), + queryFn: () => queries.getSite(id!), + enabled: isEdit, + }); + + const form = useForm({ resolver: zodResolver(schema) }); + const { register, handleSubmit, reset, formState } = form; + + useEffect(() => { + if (site) reset(site as FormData); + }, [site, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => + isEdit + ? api.put(`/api/v1/sites/${id}`, values) + : api.post('/api/v1/sites', values), + onSuccess: (saved) => { + toast.success(isEdit ? 'Site updated' : 'Site created'); + qc.invalidateQueries({ queryKey: keys.sites() }); + navigate(`/sites/${saved.id}/containers`); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + const onSubmit = handleSubmit((values) => mutation.mutate(values)); + + if (isEdit && isLoading) { + return ( +
+ +
+ ); + } + + return ( +
+ +
+ + +
+ + +
+
+ + +
+ + + {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} + +
+ + +
+
+
+ ); +} diff --git a/create-a-container/client/src/pages/sites/SitesListPage.tsx b/create-a-container/client/src/pages/sites/SitesListPage.tsx new file mode 100644 index 00000000..878862d9 --- /dev/null +++ b/create-a-container/client/src/pages/sites/SitesListPage.tsx @@ -0,0 +1,134 @@ +import { Link } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + Alert, + AlertDescription, + AlertTitle, + Badge, + Button, + PageHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useToast, +} from '@mieweb/ui'; +import { Building2, Pencil, Plus, Trash2 } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import { useSession } from '@/lib/auth'; +import type { Site } from '@/lib/types'; + +export function SitesListPage() { + const { data: session } = useSession(); + const { data, isLoading, error } = useQuery({ + queryKey: keys.sites(), + queryFn: queries.listSites, + }); + const qc = useQueryClient(); + const toast = useToast(); + const del = useMutation({ + mutationFn: (id: number) => api.delete(`/api/v1/sites/${id}`), + onSuccess: () => { + toast.success('Site deleted'); + qc.invalidateQueries({ queryKey: keys.sites() }); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } + actions={ + session?.isAdmin && ( + + + + ) + } + /> + + {error && ( + + Failed to load sites + {(error as ApiError).message} + + )} + {isLoading && ( +
+ +
+ )} + {data && data.length === 0 && ( + + No sites yet + Create a site to begin managing nodes and containers. + + )} + {data && data.length > 0 && ( + + + + Name + Internal domain + Gateway + External IP + Nodes + Actions + + + + {data.map((s: Site) => ( + + + + {s.name} + + + {s.internalDomain} + {s.gateway || '—'} + {s.externalIp || '—'} + + {s.nodeCount ?? 0} + + + {session?.isAdmin && ( + <> + + + + + + )} + + + ))} + +
+ )} +
+ ); +} diff --git a/create-a-container/client/src/pages/users/InviteUserPage.tsx b/create-a-container/client/src/pages/users/InviteUserPage.tsx new file mode 100644 index 00000000..c65f361c --- /dev/null +++ b/create-a-container/client/src/pages/users/InviteUserPage.tsx @@ -0,0 +1,54 @@ +import { useNavigate } from 'react-router'; +import { useMutation } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Input, + PageHeader, + useToast, +} from '@mieweb/ui'; +import { Mail } from 'lucide-react'; +import { api, ApiError } from '@/lib/api'; + +const schema = z.object({ email: z.string().email('Must be a valid email') }); +type FormData = z.infer; + +export function InviteUserPage() { + const navigate = useNavigate(); + const toast = useToast(); + const { register, handleSubmit, formState } = useForm({ resolver: zodResolver(schema) }); + + const mutation = useMutation({ + mutationFn: (v: FormData) => api.post<{ email: string; message: string }>('/api/v1/users/invite', v), + onSuccess: (r) => { + toast.success(`Invitation sent to ${r.email}`); + navigate('/users'); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + return ( +
+ } bordered /> +
mutation.mutate(v))} noValidate className="grid max-w-md gap-4"> + + {mutation.error && {(mutation.error as ApiError).message}} +
+ + +
+
+
+ ); +} diff --git a/create-a-container/client/src/pages/users/UserFormPage.tsx b/create-a-container/client/src/pages/users/UserFormPage.tsx new file mode 100644 index 00000000..bc8abf36 --- /dev/null +++ b/create-a-container/client/src/pages/users/UserFormPage.tsx @@ -0,0 +1,145 @@ +import { useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + Alert, + AlertDescription, + Button, + Checkbox, + Input, + PageHeader, + Select, + Spinner, + useToast, +} from '@mieweb/ui'; +import { api, ApiError } from '@/lib/api'; +import { keys, queries } from '@/lib/queries'; +import type { User } from '@/lib/types'; + +const schema = z.object({ + uid: z.string().min(1, 'Required'), + givenName: z.string().min(1, 'Required'), + sn: z.string().min(1, 'Required'), + mail: z.string().email('Must be a valid email'), + userPassword: z.string().optional(), + status: z.enum(['pending', 'active', 'disabled']), + groupIds: z.array(z.number()).optional(), +}); +type FormData = z.infer; + +export function UserFormPage() { + const { uid } = useParams<{ uid?: string }>(); + const isEdit = !!uid; + const navigate = useNavigate(); + const qc = useQueryClient(); + const toast = useToast(); + + const { data: user, isLoading } = useQuery({ + queryKey: keys.user(uid ?? 'new'), + queryFn: () => queries.getUser(uid!), + enabled: isEdit, + }); + const { data: groups } = useQuery({ queryKey: keys.groups(), queryFn: queries.listGroups }); + + const { register, handleSubmit, reset, watch, setValue, formState } = useForm({ + resolver: zodResolver(schema), + defaultValues: { status: 'pending', groupIds: [] }, + }); + const status = watch('status'); + const groupIds = watch('groupIds') || []; + + useEffect(() => { + if (user) { + reset({ + uid: user.uid, + givenName: user.givenName, + sn: user.sn, + mail: user.mail, + userPassword: '', + status: user.status, + groupIds: user.groups?.map((g) => g.gidNumber) || [], + }); + } + }, [user, reset]); + + const mutation = useMutation({ + mutationFn: (values: FormData) => { + const payload: Record = { ...values }; + if (!values.userPassword) delete payload.userPassword; + return isEdit + ? api.put(`/api/v1/users/${uid}`, payload) + : api.post('/api/v1/users', payload); + }, + onSuccess: (result) => { + if (result.twoFactorWarning) { + toast.warning(`User saved, but 2FA invite failed: ${result.twoFactorWarning}`); + } else { + toast.success(isEdit ? 'User updated' : 'User created'); + } + qc.invalidateQueries({ queryKey: keys.users() }); + navigate('/users'); + }, + onError: (err: ApiError) => toast.error(err.message), + }); + + if (isEdit && isLoading) return
; + + function toggleGroup(gid: number) { + const next = groupIds.includes(gid) ? groupIds.filter((g) => g !== gid) : [...groupIds, gid]; + setValue('groupIds', next); + } + + return ( +
+ +
mutation.mutate(v))} noValidate className="grid max-w-2xl gap-4"> + +
+ + +
+ + + - -
-
- <% if (apiKey.description) { %> - Description: <%= apiKey.description %> - <% } %> -
-
- -
-
-
Key Details
-
-
Key Prefix:
-
<%= apiKey.keyPrefix %>********
- -
Created:
-
<%= new Date(apiKey.createdAt).toLocaleString() %>
- -
Key ID:
-
<%= apiKey.id %>
-
-
-
- - - - -
-
- - - - - -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/apikeys/form.ejs b/create-a-container/views/apikeys/form.ejs deleted file mode 100644 index 7753f8f3..00000000 --- a/create-a-container/views/apikeys/form.ejs +++ /dev/null @@ -1,52 +0,0 @@ -<%- include('../layouts/header', { - title: 'Create API Key - MIE', - breadcrumbs: [ - { label: 'API Keys', url: '/apikeys' }, - { label: 'Create', url: '/apikeys/new' } - ], - req -}) %> - -
-
-
-
-

Create New API Key

- - - - -
- - -
- Optional: Add a description to help identify this key's purpose. -
-
- -
- - Cancel - - -
- -
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/apikeys/index.ejs b/create-a-container/views/apikeys/index.ejs deleted file mode 100644 index c30a88ea..00000000 --- a/create-a-container/views/apikeys/index.ejs +++ /dev/null @@ -1,129 +0,0 @@ -<%- include('../layouts/header', { - title: 'API Keys - MIE', - breadcrumbs: [ - { label: 'API Keys', url: '/apikeys' } - ], - req -}) %> -
-
-
-

API Keys

- - New API Key - -
- - -
- <% if (apiKeys.length === 0) { %> -
-

No API keys found. Create one to get started.

-
- <% } else { %> - <% apiKeys.forEach(key => { %> -
-
-
-
Key Prefix
- <% if (key.lastUsedAt) { %> - Active - <% } else { %> - Unused - <% } %> -
- <%= key.keyPrefix %>******** - - <% if (key.description) { %> -

Description: <%= key.description %>

- <% } %> - -

- - Created: <%= new Date(key.createdAt).toLocaleDateString() %>
- Last Used: <%= key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleDateString() : 'Never' %> -
-

- -
- - View Details - -
- - -
-
-
-
- <% }) %> - <% } %> -
- - -
- - - - - - - - - - - - <% if (apiKeys.length === 0) { %> - - - - <% } else { %> - <% apiKeys.forEach(key => { %> - - - - - - - - <% }) %> - <% } %> - -
Key PrefixDescriptionLast UsedCreatedActions
-

No API keys found. Create one to get started.

-
- <%= key.keyPrefix %>******** - - <% if (key.description) { %> - <%= key.description %> - <% } else { %> - No description - <% } %> - - <% if (key.lastUsedAt) { %> - <%= new Date(key.lastUsedAt).toLocaleString() %> - <% } else { %> - Never - <% } %> - - <%= new Date(key.createdAt).toLocaleString() %> - -
- - View - -
- - -
-
-
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/apikeys/show.ejs b/create-a-container/views/apikeys/show.ejs deleted file mode 100644 index 7890183a..00000000 --- a/create-a-container/views/apikeys/show.ejs +++ /dev/null @@ -1,82 +0,0 @@ -<%- include('../layouts/header', { - title: 'API Key Details - MIE', - breadcrumbs: [ - { label: 'API Keys', url: '/apikeys' }, - { label: 'Details', url: '#' } - ], - req -}) %> - -
-
-
-
-
-

API Key Details

-
- - -
-
- - - -
-
Key Prefix:
-
- <%= apiKey.keyPrefix %>******** -
- -
Description:
-
- <% if (apiKey.description) { %> - <%= apiKey.description %> - <% } else { %> - No description provided - <% } %> -
- -
Key ID:
-
- <%= apiKey.id %> -
- -
Created:
-
- <%= new Date(apiKey.createdAt).toLocaleString() %> -
- -
Last Updated:
-
- <%= new Date(apiKey.updatedAt).toLocaleString() %> -
- -
Last Used:
-
- <% if (apiKey.lastUsedAt) { %> - <%= new Date(apiKey.lastUsedAt).toLocaleString() %> - Active - <% } else { %> - Never used - Inactive - <% } %> -
-
- - -
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/containers/form.ejs b/create-a-container/views/containers/form.ejs deleted file mode 100644 index 0d5cb730..00000000 --- a/create-a-container/views/containers/form.ejs +++ /dev/null @@ -1,711 +0,0 @@ -<% -const isEdit = typeof container !== 'undefined' && container; -const pageTitle = isEdit ? 'Edit Container Services' : 'Create Your Container'; -const formAction = isEdit ? `/sites/${site.id}/containers/${container.id}` : `/sites/${site.id}/containers`; -const breadcrumbLabel = isEdit ? 'Edit' : 'New'; -%> -<%- include('../layouts/header', { - title: pageTitle + ' - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: site.name, url: `/sites/${site.id}/containers` }, - { label: breadcrumbLabel, url: null } - ], - colWidth: 'col-12 col-lg-10', - req -}) %> - - - - - -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/containers/index.ejs b/create-a-container/views/containers/index.ejs deleted file mode 100644 index cb024ab8..00000000 --- a/create-a-container/views/containers/index.ejs +++ /dev/null @@ -1,130 +0,0 @@ -<%- include('../layouts/header', { - title: 'Your Containers - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: site.name, url: `/sites/${site.id}/containers` } - ], - colWidth: 'col-12 col-lg-10', - req -}) %> - -
-
-
-

Your Containers - <%= site.name %>

- New Container -
- -
- - - - - - - - - - - - - - - <% if (rows && rows.length) { %> - <% rows.forEach(r => { %> - - - - - - - - - - - <% }) %> - <% } else { %> - - - - <% } %> - -
HostnameStatusIPv4TemplateNodeSSH PortHTTPActions
<%= r.hostname %> - <% if (r.status === 'running') { %> - Running - <% } else if (r.status === 'pending') { %> - - - Pending - - <% } else if (r.status === 'creating') { %> - - - Creating - - <% } else if (r.status === 'failed') { %> - Failed - <% } else if (r.status === 'restarting') { %> - - - Restarting - - <% } else { %> - <%= r.status || 'Unknown' %> - <% } %> - <% if (r.creationJobId && (r.status === 'pending' || r.status === 'creating' || r.status === 'failed')) { %> - - Details - - <% } %> - <%= r.ipv4Address || '-' %><%= r.template || '-' %> - <% if (r.nodeApiUrl && r.containerId) { %> - <%= r.nodeName %> - <% } else { %> - <%= r.nodeName %> - <% } %> - - <% if (r.sshPort && r.sshHost) { %> - <%= r.sshPort %> - - - <% } else { %> - <%= r.sshPort || '-' %> - <% } %> - - <% if (r.httpEntries && r.httpEntries.length > 0) { %> - <% r.httpEntries.forEach((entry, i) => { %> - <% if (i > 0) { %>
<% } %> - <% if (entry.externalUrl) { %> - <%= entry.port %> - <% } else { %> - <%= entry.port %> - <% } %> - <% }) %> - <% } else { %> - - - <% } %> -
- <% if (r.status === 'running' || r.status === 'failed') { %> - Edit -
- - - -
- <% } %> -
- - -
-
- No containers found. Click "New Container" to create your first one. -
-
-
- -
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/external-domains/form.ejs b/create-a-container/views/external-domains/form.ejs deleted file mode 100644 index b324f625..00000000 --- a/create-a-container/views/external-domains/form.ejs +++ /dev/null @@ -1,174 +0,0 @@ -<%- include('../layouts/header', { - title: (isEdit ? 'Edit External Domain' : 'New External Domain') + ' - MIE', - breadcrumbs: [ - { label: 'External Domains', url: '/external-domains' }, - { label: isEdit ? 'Edit' : 'New', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

<%= isEdit ? 'Edit External Domain' : 'Create New External Domain' %>

- -
- <% if (isEdit) { %> - - <% } %> - -
- - -
The fully qualified domain name (e.g., example.com)
-
- -
- - -
The site whose DNS is assumed pre-configured (e.g., wildcard A record). Cross-site services on other sites will have Cloudflare A records created automatically.
-
- -
- - -
Email address for ACME certificate registration and renewal notifications
-
- -
- - -
- Select the ACME certificate authority. ZeroSSL will automatically retrieve EAB credentials using your email. -
-
- -
- - -
Cloudflare account email for DNS-01 challenge validation and cross-site DNS record management
-
- -
- - -
- API token with DNS:Edit permission. <%= isEdit ? 'Leave blank to keep existing API key. Enter new key to update.' : '' %> -
-
- -
- - -
- URL of an authentication server that supports the NGINX - auth_request - protocol. Must implement GET /verify (return 2xx if authenticated, 401 otherwise) - and GET /login?redirect=<url> for unauthenticated users. - The server must be on a subdomain of this external domain for session cookie sharing. -
- -
- -
- Cancel - -
-
-
- -
- - - -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/external-domains/index.ejs b/create-a-container/views/external-domains/index.ejs deleted file mode 100644 index de250ce2..00000000 --- a/create-a-container/views/external-domains/index.ejs +++ /dev/null @@ -1,62 +0,0 @@ -<%- include('../layouts/header', { - title: 'External Domains - MIE', - breadcrumbs: [ - { label: 'External Domains', url: '/external-domains' } - ], - req -}) %> - -
-
-
-

External Domains

- New External Domain -
- -
- - - - - - - - - - - - - <% if (rows && rows.length) { %> - <% rows.forEach(domain => { %> - - - - - - - - - <% }) %> - <% } else { %> - - - - <% } %> - -
Domain NameDefault SiteACME EmailACME DirectoryCloudflare API EmailActions
<%= domain.name %><%= domain.defaultSite || '-' %><%= domain.acmeEmail || '-' %><%= domain.acmeDirectoryUrl || '-' %><%= domain.cloudflareApiEmail || '-' %> - Edit -
- - -
-
- No external domains found. Click "New External Domain" to create your first one. -
-
-
- -
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/groups/form.ejs b/create-a-container/views/groups/form.ejs deleted file mode 100644 index e42ac639..00000000 --- a/create-a-container/views/groups/form.ejs +++ /dev/null @@ -1,109 +0,0 @@ -<%- include('../layouts/header', { - title: (isEdit ? 'Edit Group' : 'New Group') + ' - MIE', - breadcrumbs: [ - { label: 'Groups', url: '/groups' }, - { label: isEdit ? 'Edit' : 'New', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

<%= isEdit ? 'Edit Group' : 'Create New Group' %>

- -
- <% if (isEdit) { %> - - <% } %> - - <% if (!isEdit) { %> -
- - -
Numeric group identifier (must be unique, 2000+)
-
- <% } else { %> -
- - -
Group ID cannot be changed
-
- <% } %> - -
- - -
Common name for the group
-
- -
-
- - > - -
Members of this group will have admin privileges
-
-
- -
- Cancel - -
-
- - <% if (isEdit) { %> -
-
-
-
Danger Zone
-

Permanently delete this group

-
- -
- - - <% } %> -
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/groups/index.ejs b/create-a-container/views/groups/index.ejs deleted file mode 100644 index bbcb171a..00000000 --- a/create-a-container/views/groups/index.ejs +++ /dev/null @@ -1,60 +0,0 @@ -<%- include('../layouts/header', { - title: 'Groups - MIE', - breadcrumbs: [ - { label: 'Groups', url: '/groups' } - ], - req -}) %> -
-
-
-

Groups

- New Group -
- -
- - - - - - - - - - - - <% if (rows.length === 0) { %> - - - - <% } else { %> - <% rows.forEach(row => { %> - - - - - - - - <% }) %> - <% } %> - -
GIDNameAdmin GroupUser CountActions
No groups found
<%= row.gidNumber %><%= row.cn %> - <% if (row.isAdmin) { %> - Yes - <% } else { %> - No - <% } %> - <%= row.userCount %> - Edit -
- - -
-
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/jobs/show.ejs b/create-a-container/views/jobs/show.ejs deleted file mode 100644 index b4e19984..00000000 --- a/create-a-container/views/jobs/show.ejs +++ /dev/null @@ -1,228 +0,0 @@ -<%- include('../layouts/header', { - title: `Job #${job.id} - MIE`, - breadcrumbs: [ - { label: 'Jobs', url: '/jobs' }, - { label: `Job #${job.id}`, url: `/jobs/${job.id}` } - ], - colWidth: 'col-12 col-lg-10', - req -}) %> - -
-
-

- Job #<%= job.id %> - - <% if (job.status === 'pending' || job.status === 'running') { %> - - <% } %> - <%= job.status.charAt(0).toUpperCase() + job.status.slice(1) %> - -

- <% if (container) { %> - - Back to Containers - - <% } %> -
- -
-
-
-

Command:

- <%= job.command %> -
-
-

Created:

- <%= new Date(job.createdAt).toLocaleString() %> -
-
-

Created By:

- <%= job.createdBy || 'System' %> -
-
- - <% if (container) { %> - - <% } %> - -
-
Output
-
- - -
-
- -
- <% if (initialOutput && initialOutput.length > 0) { %> - <% initialOutput.forEach(line => { %><%= line.output %><% }) %> - <% } %> -
- -
- <% if (job.status === 'pending' || job.status === 'running') { %> - Connecting to live stream... - <% } else { %> - Job completed - <% } %> -
-
- - -
- - - - - -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/layouts/footer.ejs b/create-a-container/views/layouts/footer.ejs deleted file mode 100644 index c713a198..00000000 --- a/create-a-container/views/layouts/footer.ejs +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - diff --git a/create-a-container/views/layouts/header.ejs b/create-a-container/views/layouts/header.ejs deleted file mode 100644 index 48b03837..00000000 --- a/create-a-container/views/layouts/header.ejs +++ /dev/null @@ -1,163 +0,0 @@ - - - - - <%= typeof title !== 'undefined' ? title : 'MIE Container Creation' %> - - - - - - - - - -
-
- - - - -
-
-
- - <% - const successMessages = req.flash ? req.flash('success') : []; - const errorMessages = req.flash ? req.flash('error') : []; - %> - <% if (successMessages && successMessages.length > 0) { %> - <% successMessages.forEach(msg => { %> - - <% }) %> - <% } %> - - <% if (errorMessages && errorMessages.length > 0) { %> - <% errorMessages.forEach(msg => { %> - - <% }) %> - <% } %> - - <% - const warningMessages = req.flash ? req.flash('warning') : []; - %> - <% if (warningMessages && warningMessages.length > 0) { %> - <% warningMessages.forEach(msg => { %> - - <% }) %> - <% } %> - - diff --git a/create-a-container/views/login.ejs b/create-a-container/views/login.ejs deleted file mode 100644 index a241f220..00000000 --- a/create-a-container/views/login.ejs +++ /dev/null @@ -1,84 +0,0 @@ - - - - - MIE Container Creation Login - - - - - - - - - \ No newline at end of file diff --git a/create-a-container/views/nodes/form.ejs b/create-a-container/views/nodes/form.ejs deleted file mode 100644 index f197f13b..00000000 --- a/create-a-container/views/nodes/form.ejs +++ /dev/null @@ -1,194 +0,0 @@ -<%- include('../layouts/header', { - title: (isEdit ? 'Edit Node' : 'New Node') + ' - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: site.name, url: `/sites/${site.id}/nodes` }, - { label: isEdit ? 'Edit' : 'New', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

<%= isEdit ? 'Edit Node' : 'Create New Node' %>

- -
- <% if (isEdit) { %> - - <% } %> -
- - -
The Proxmox node name (must be unique)
-
- -
- - -
The node's IPv4 address (optional)
-
- -
- - -
The Proxmox API endpoint URL (optional)
-
- -
- - -
Proxmox username (e.g., root@pam)
-
- -
- - -
- <%= isEdit ? 'Leave blank to keep existing password. Enter new password to update.' : 'Proxmox password' %> -
-
- -
- - -
Whether to verify TLS certificates when connecting to this node
-
- -
- - - -
Storage for CT Template images used when building containers
-
- -
- - - -
Storage for container root filesystems
-
- -
- - -
Proxmox network bridge used when creating containers
-
- -
- - aria-label="NVIDIA GPU available on this node" - > - -
Enable if this node has NVIDIA drivers and nvidia-container-toolkit installed
-
-
- -
- Cancel - -
- -
- -
- -<%- include('../layouts/footer') %> - -<% if (isEdit) { %> - -<% } %> diff --git a/create-a-container/views/nodes/import.ejs b/create-a-container/views/nodes/import.ejs deleted file mode 100644 index 451c7d4a..00000000 --- a/create-a-container/views/nodes/import.ejs +++ /dev/null @@ -1,74 +0,0 @@ -<%- include('../layouts/header', { - title: 'Import Nodes - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: site.name, url: `/sites/${site.id}/nodes` }, - { label: 'Import', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

Import Nodes

- -
-
- - -
The URL of the external API endpoint
-
- -
- - -
Proxmox username (e.g., root@pam)
-
- -
- - -
Proxmox password
-
- -
- - -
Whether to verify TLS certificates when importing nodes
-
- -
- Cancel - -
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/nodes/index.ejs b/create-a-container/views/nodes/index.ejs deleted file mode 100644 index f48da939..00000000 --- a/create-a-container/views/nodes/index.ejs +++ /dev/null @@ -1,84 +0,0 @@ -<%- include('../layouts/header', { - title: 'Nodes - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: site.name, url: `/sites/${site.id}/nodes` } - ], - req -}) %> - -
-
-
-

Proxmox Nodes for <%= site.name %>

- -
- -
- - - - - - - - - - - - - <% if (rows && rows.length) { %> - <% rows.forEach(r => { %> - - - - - - - - - <% }) %> - <% } else { %> - - - - <% } %> - -
NameIPv4 AddressAPI URLTLS VerifyContainersActions
<%= r.name %><%= r.ipv4Address || '-' %><%= r.apiUrl || '-' %> - <% if (r.tlsVerify === true) { %> - Yes - <% } else if (r.tlsVerify === false) { %> - No - <% } else { %> - Not Set - <% } %> - <%= r.containerCount %> - Edit -
- - -
-
- No nodes found. Click "New Node" to create your first one. -
-
-
- -
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/register-success.ejs b/create-a-container/views/register-success.ejs deleted file mode 100644 index bcf16261..00000000 --- a/create-a-container/views/register-success.ejs +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Registration Complete - - - - - - - - diff --git a/create-a-container/views/register.ejs b/create-a-container/views/register.ejs deleted file mode 100644 index 2c002f66..00000000 --- a/create-a-container/views/register.ejs +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - Register - - - - - - - - - - \ No newline at end of file diff --git a/create-a-container/views/reset-password/request.ejs b/create-a-container/views/reset-password/request.ejs deleted file mode 100644 index f4b8997a..00000000 --- a/create-a-container/views/reset-password/request.ejs +++ /dev/null @@ -1,52 +0,0 @@ - - - - - Reset Password - MIE - - - - - - - diff --git a/create-a-container/views/reset-password/reset.ejs b/create-a-container/views/reset-password/reset.ejs deleted file mode 100644 index aee74290..00000000 --- a/create-a-container/views/reset-password/reset.ejs +++ /dev/null @@ -1,59 +0,0 @@ - - - - - Set New Password - MIE - - - - - - - diff --git a/create-a-container/views/settings/index.ejs b/create-a-container/views/settings/index.ejs deleted file mode 100644 index a5a236ec..00000000 --- a/create-a-container/views/settings/index.ejs +++ /dev/null @@ -1,219 +0,0 @@ -<%- include('../layouts/header', { - title: 'System Settings - MIE', - breadcrumbs: [ - { label: 'Settings', url: '/settings' } - ], - req -}) %> - -
-
-

System Settings

- -
-
-
Email Configuration
- -
- - -
- Format: smtp[s]://[[username][:password]@]<host>[:port] -
-
- -
- - -
- Email address used as "from" address for system emails -
-
-
- -
-
Push Notification 2FA
- -
- - -
- URL of the push notification service endpoint -
-
- -
-
- - aria-describedby="push_notification_enabled_help" - > - -
-
- When enabled, users must approve login attempts via push notification. URL must be configured. -
-
- -
- - -
- Bearer token used to authenticate API requests to the push notification service (e.g. 2FA invite) -
-
-
- -
-
Default Container Environment Variables
-

- These variables are automatically injected into every new container at creation time. - Use them for shared configuration such as agent connection details. - Per-container values always take precedence over these defaults. -

-
- -
- - - - - - - - - - -
NameValueDescriptionAction
-
- -
- - - Cancel - -
-
-
-
- -<%- include('../layouts/footer') %> - - diff --git a/create-a-container/views/sites/form.ejs b/create-a-container/views/sites/form.ejs deleted file mode 100644 index 03ee7aca..00000000 --- a/create-a-container/views/sites/form.ejs +++ /dev/null @@ -1,145 +0,0 @@ -<%- include('../layouts/header', { - title: (isEdit ? 'Edit Site' : 'New Site') + ' - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' }, - { label: isEdit ? 'Edit' : 'New', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

<%= isEdit ? 'Edit Site' : 'Create New Site' %>

- -
- <% if (isEdit) { %> - - <% } %> - -
- - -
Friendly name for this site
-
- -
- - -
Internal DNS domain for this site
-
- -
-
- - -
Comma-separated start and end IPs for DHCP range
-
- -
- - -
Subnet mask for DHCP configuration
-
-
- -
- - -
Gateway IP address
-
- -
- - -
Comma-separated list of DNS server IPs
-
- -
- - -
Public IP address used as the target for Cloudflare DNS A records
-
- -
- Cancel - -
-
- - <% if (isEdit) { %> -
-
-
-
Danger Zone
-

Permanently delete this site

-
- -
- - - <% } %> -
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/sites/index.ejs b/create-a-container/views/sites/index.ejs deleted file mode 100644 index 9fd9e1b3..00000000 --- a/create-a-container/views/sites/index.ejs +++ /dev/null @@ -1,76 +0,0 @@ -<%- include('../layouts/header', { - title: 'Sites - MIE', - breadcrumbs: [ - { label: 'Sites', url: '/sites' } - ], - req -}) %> -
-
-
-

Sites

- <% if (req.session && req.session.isAdmin) { %> - New Site - <% } %> -
- -
- - - - - - - - - - - - - - <% if (rows.length === 0) { %> - - - - <% } else { %> - <% rows.forEach(row => { %> - - - - - - - - - - <% }) %> - <% } %> - -
IDNameInternal DomainDHCP RangeGatewayNode CountActions
No sites found
<%= row.id %><%= row.name %><%= row.internalDomain || '-' %><%= row.dhcpRange || '-' %><%= row.gateway || '-' %><%= row.nodeCount %> - Containers - <% if (req.session && req.session.isAdmin) { %> - Nodes - Edit -
- - -
- <% } %> -
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/users/form.ejs b/create-a-container/views/users/form.ejs deleted file mode 100644 index b8369cdb..00000000 --- a/create-a-container/views/users/form.ejs +++ /dev/null @@ -1,151 +0,0 @@ -<%- include('../layouts/header', { - title: (isEdit ? 'Edit User' : 'New User') + ' - MIE', - breadcrumbs: [ - { label: 'Users', url: '/users' }, - { label: isEdit ? 'Edit' : 'New', url: '#' } - ], - colWidth: 'col-lg-8', - req -}) %> - -
-
-

<%= isEdit ? 'Edit User' : 'Create New User' %>

- -
- <% if (isEdit) { %> - - <% } %> - -
- - -
The login username
-
- -
-
- - -
- -
- - -
-
- -
- - -
- -
- - - placeholder="<%= isEdit ? 'Leave blank to keep current password' : 'Enter password' %>" - > - <% if (isEdit) { %> -
Leave blank to keep the current password
- <% } %> -
- -
- - -
User account status
-
- -
- - <% groups.forEach(group => { %> -
- g.gidNumber === group.gidNumber) ? 'checked' : '' %> - > - -
- <% }) %> -
Select which groups this user belongs to
-
- -
- - Cancel - - <% if (isEdit) { %> - - <% } %> -
-
- - <% if (isEdit) { %> - - <% } %> -
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/users/index.ejs b/create-a-container/views/users/index.ejs deleted file mode 100644 index ded466ea..00000000 --- a/create-a-container/views/users/index.ejs +++ /dev/null @@ -1,77 +0,0 @@ -<%- include('../layouts/header', { - title: 'Users - MIE', - breadcrumbs: [ - { label: 'Users', url: '/users' } - ], - req -}) %> -
-
-
-

Users

- -
- -
- - - - - - - - - - - - - - - <% if (rows.length === 0) { %> - - - - <% } else { %> - <% rows.forEach(row => { %> - - - - - - - - - - - <% }) %> - <% } %> - -
UIDUsernameFull NameEmailStatusGroupsAdminActions
No users found
<%= row.uidNumber %><%= row.uid %><%= row.cn %><%= row.mail %> - <% if (row.status === 'active') { %> - Active - <% } else if (row.status === 'pending') { %> - Pending - <% } else { %> - <%= row.status %> - <% } %> - <%= row.groups %> - <% if (row.isAdmin) { %> - Yes - <% } else { %> - No - <% } %> - - Edit -
- - -
-
-
-
-
- -<%- include('../layouts/footer') %> diff --git a/create-a-container/views/users/invite.ejs b/create-a-container/views/users/invite.ejs deleted file mode 100644 index 6c9d002f..00000000 --- a/create-a-container/views/users/invite.ejs +++ /dev/null @@ -1,46 +0,0 @@ -<%- include('../layouts/header', { - title: 'Invite User - MIE', - breadcrumbs: [ - { label: 'Users', url: '/users' }, - { label: 'Invite', url: '#' } - ], - colWidth: 'col-lg-6', - req -}) %> - -
-
-

Invite User

-

- Send an invitation email to a new user. They will receive a link to register their account, - and their account will be automatically activated upon registration. -

- -
-
- - -
- The invitation link will be sent to this email address and will expire in 24 hours. -
-
- -
- - Cancel -
-
-
-
- -<%- include('../layouts/footer') %> From 47263bc43d592525f39d6d7d16c5d1436d6471c9 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 22:05:49 -0700 Subject: [PATCH 06/11] fix(dev): SPA boots end-to-end on sqlite - middlewares/api.js: drop __Host- prefix off-prod (requires Secure) and only bind CSRF token to session id once a user is signed in; saveUninitialized: false handed out a fresh session id per anon request, breaking double-submit. - client/vite.config.ts: drop /login, /logout, /nginx-conf, /dnsmasq proxies now that those are SPA routes; /api is the only backend proxy. - migrations: make 3 postgres-first migrations sqlite-compatible (guard undefined fk.constraintName, tolerate missing named constraints, rewrite UPDATE...FROM as scalar subquery, tolerate re-added columns). - package.json: add sqlite3 as a real dependency. --- .gitignore | 1 + create-a-container/client/vite.config.ts | 4 - create-a-container/middlewares/api.js | 13 +- ...0-make-external-domain-site-id-nullable.js | 4 +- .../20260218000000-remove-node-name-unique.js | 6 +- ...00001-container-site-scoped-constraints.js | 24 +- create-a-container/package-lock.json | 644 +++++++++++++++++- create-a-container/package.json | 1 + 8 files changed, 668 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 37d7e734..45adf944 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules .env +.tmp-verify/ diff --git a/create-a-container/client/vite.config.ts b/create-a-container/client/vite.config.ts index baafd22a..b6610742 100644 --- a/create-a-container/client/vite.config.ts +++ b/create-a-container/client/vite.config.ts @@ -17,10 +17,6 @@ export default defineConfig({ strictPort: true, proxy: { '/api': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, - '/login': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, - '/logout': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, - '/nginx-conf': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, - '/dnsmasq': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, }, }, build: { diff --git a/create-a-container/middlewares/api.js b/create-a-container/middlewares/api.js index 2d69c6aa..a0ee176a 100644 --- a/create-a-container/middlewares/api.js +++ b/create-a-container/middlewares/api.js @@ -13,18 +13,25 @@ const { doubleCsrf } = require('csrf-csrf'); // Skipped for Bearer-token API key requests (those are already cryptographically auth'd). const csrfSecret = () => process.env.CSRF_SECRET || process.env.SESSION_SECRET || 'dev-csrf-secret-change-me'; +// __Host- prefix requires Secure + Path=/ + no Domain; browsers reject it on +// plain HTTP (including dev), so fall back to a plain cookie name off-prod. +const isProd = process.env.NODE_ENV === 'production'; + const { doubleCsrfProtection, generateCsrfToken, invalidCsrfTokenError, } = doubleCsrf({ getSecret: csrfSecret, - getSessionIdentifier: (req) => (req.session && req.session.id) || req.ip || 'anonymous', - cookieName: '__Host-csrf.token', + // Double-submit pattern is sufficient on its own; binding to req.session.id + // breaks for anon requests because saveUninitialized:false hands out a fresh + // session id every request until a user signs in. + getSessionIdentifier: (req) => (req.session && req.session.user && req.session.id) || req.ip || 'anonymous', + cookieName: isProd ? '__Host-csrf.token' : 'csrf.token', cookieOptions: { sameSite: 'lax', path: '/', - secure: process.env.NODE_ENV === 'production', + secure: isProd, httpOnly: true, }, size: 32, diff --git a/create-a-container/migrations/20260217000000-make-external-domain-site-id-nullable.js b/create-a-container/migrations/20260217000000-make-external-domain-site-id-nullable.js index 5a029051..497fddb7 100644 --- a/create-a-container/migrations/20260217000000-make-external-domain-site-id-nullable.js +++ b/create-a-container/migrations/20260217000000-make-external-domain-site-id-nullable.js @@ -5,7 +5,7 @@ module.exports = { // Discover and remove all FK constraints on siteId const fks = await queryInterface.getForeignKeyReferencesForTable('ExternalDomains'); for (const fk of fks) { - if (fk.columnName === 'siteId') { + if (fk.columnName === 'siteId' && fk.constraintName) { await queryInterface.removeConstraint('ExternalDomains', fk.constraintName); } } @@ -30,7 +30,7 @@ module.exports = { async down(queryInterface, Sequelize) { const fks = await queryInterface.getForeignKeyReferencesForTable('ExternalDomains'); for (const fk of fks) { - if (fk.columnName === 'siteId') { + if (fk.columnName === 'siteId' && fk.constraintName) { await queryInterface.removeConstraint('ExternalDomains', fk.constraintName); } } diff --git a/create-a-container/migrations/20260218000000-remove-node-name-unique.js b/create-a-container/migrations/20260218000000-remove-node-name-unique.js index ea890113..d7c47f43 100644 --- a/create-a-container/migrations/20260218000000-remove-node-name-unique.js +++ b/create-a-container/migrations/20260218000000-remove-node-name-unique.js @@ -2,7 +2,11 @@ module.exports = { async up(queryInterface) { - await queryInterface.removeConstraint('Nodes', 'Nodes_name_key'); + try { + await queryInterface.removeConstraint('Nodes', 'Nodes_name_key'); + } catch (err) { + if (!/does not exist|no such/i.test(err.message)) throw err; + } }, async down(queryInterface) { diff --git a/create-a-container/migrations/20260218000001-container-site-scoped-constraints.js b/create-a-container/migrations/20260218000001-container-site-scoped-constraints.js index 2b3e49f6..97a9ccc6 100644 --- a/create-a-container/migrations/20260218000001-container-site-scoped-constraints.js +++ b/create-a-container/migrations/20260218000001-container-site-scoped-constraints.js @@ -3,20 +3,22 @@ module.exports = { async up(queryInterface, Sequelize) { // 1. Add siteId column to Containers (nullable initially for backfill) - await queryInterface.addColumn('Containers', 'siteId', { - type: Sequelize.INTEGER, - allowNull: true, - references: { model: 'Sites', key: 'id' }, - onUpdate: 'CASCADE', - onDelete: 'RESTRICT' - }); + try { + await queryInterface.addColumn('Containers', 'siteId', { + type: Sequelize.INTEGER, + allowNull: true, + references: { model: 'Sites', key: 'id' }, + onUpdate: 'CASCADE', + onDelete: 'RESTRICT' + }); + } catch (err) { + if (!/duplicate column|already exists/i.test(err.message)) throw err; + } // 2. Backfill siteId from Node await queryInterface.sequelize.query(` - UPDATE "Containers" c - SET "siteId" = n."siteId" - FROM "Nodes" n - WHERE c."nodeId" = n.id + UPDATE "Containers" + SET "siteId" = (SELECT n."siteId" FROM "Nodes" n WHERE n.id = "Containers"."nodeId") `); // 3. Make siteId NOT NULL after backfill diff --git a/create-a-container/package-lock.json b/create-a-container/package-lock.json index 5c24266f..b1271d31 100644 --- a/create-a-container/package-lock.json +++ b/create-a-container/package-lock.json @@ -21,6 +21,7 @@ "qrcode": "^1.5.4", "sequelize": "^6.37.8", "sequelize-cli": "^6.6.3", + "sqlite3": "^6.0.1", "swagger-ui-express": "^5.0.1", "yamljs": "^0.3.0" }, @@ -50,6 +51,18 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -185,14 +198,6 @@ "node": ">=16.17.0" } }, - "node_modules/argon2/node_modules/node-addon-api": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -240,6 +245,26 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -271,6 +296,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -338,6 +383,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -407,6 +476,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -664,6 +742,30 @@ "node": ">=0.10.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -681,6 +783,15 @@ "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", @@ -799,6 +910,25 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -863,6 +993,22 @@ "node": ">= 0.6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "optional": true + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -970,6 +1116,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -1131,6 +1283,12 @@ "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -1219,6 +1377,12 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -1358,6 +1522,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -1632,6 +1816,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -1644,6 +1840,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -1653,6 +1858,24 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -1722,6 +1945,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1730,6 +1959,52 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", + "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz", + "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -1740,6 +2015,58 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/nodemailer": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz", @@ -2112,6 +2439,43 @@ "node": ">=0.10.0" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -2146,6 +2510,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/qrcode": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", @@ -2345,6 +2719,35 @@ "url": "https://opencollective.com/express" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2690,6 +3093,51 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -2718,6 +3166,33 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "license": "BSD-3-Clause" }, + "node_modules/sqlite3": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-6.0.1.tgz", + "integrity": "sha512-X0czUUMG2tmSqJpEQa3tCuZSHKIx8PwM53vLZzKp/o6Rpy25fiVfjdbnZ988M8+O3ZWR1ih0K255VumCb3MAnQ==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.0.0", + "prebuild-install": "^7.1.3", + "tar": "^7.5.10" + }, + "engines": { + "node": ">=20.17.0" + }, + "optionalDependencies": { + "node-gyp": "12.x" + }, + "peerDependencies": { + "node-gyp": "12.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2726,6 +3201,15 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -2822,6 +3306,15 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2871,6 +3364,104 @@ "express": ">=4.0.0 || >=5.0.0-beta" } }, + "node_modules/tar": { + "version": "7.5.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", + "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "optional": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2908,6 +3499,18 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -2951,6 +3554,16 @@ "dev": true, "license": "MIT" }, + "node_modules/undici": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", + "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -2974,6 +3587,12 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -3143,6 +3762,15 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/yamljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", diff --git a/create-a-container/package.json b/create-a-container/package.json index fe006b32..7c6afa8d 100644 --- a/create-a-container/package.json +++ b/create-a-container/package.json @@ -32,6 +32,7 @@ "qrcode": "^1.5.4", "sequelize": "^6.37.8", "sequelize-cli": "^6.6.3", + "sqlite3": "^6.0.1", "swagger-ui-express": "^5.0.1", "yamljs": "^0.3.0" }, From a7ed333cd303b706f5ae81fd6d76bbc2a641df7f Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 22:11:23 -0700 Subject: [PATCH 07/11] fix(client): align top bar with sidebar header, polish footer, fix mobile - AppLayout: switch to h-screen overflow-hidden shell with internal main scroll so the sidebar can't scroll out of view on long pages. - Sidebar: pin SidebarHeader to h-16 (was 65px from py-4) so its bottom border matches the AppHeader bottom border to the pixel; rebuild the footer as a single user card (avatar + name + role + icon sign-out) with a top border so it visually anchors the bottom of the sidebar. - Header: drop the duplicate Container Manager brand on desktop (the sidebar already shows it); render a plain span only on mobile where the sidebar is collapsed off-canvas (AppHeaderBrand is hidden below md by @mieweb/ui so it can't be used there). --- .../client/src/app/AppLayout.tsx | 4 +- create-a-container/client/src/app/Header.tsx | 6 ++- create-a-container/client/src/app/Sidebar.tsx | 37 +++++++++++++++---- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/create-a-container/client/src/app/AppLayout.tsx b/create-a-container/client/src/app/AppLayout.tsx index 3591b4c6..c05f2035 100644 --- a/create-a-container/client/src/app/AppLayout.tsx +++ b/create-a-container/client/src/app/AppLayout.tsx @@ -5,13 +5,13 @@ import { AppTopHeader } from './Header'; export function AppLayout() { return ( -
+
-
+
diff --git a/create-a-container/client/src/app/Header.tsx b/create-a-container/client/src/app/Header.tsx index f10de7ca..3cbcd406 100644 --- a/create-a-container/client/src/app/Header.tsx +++ b/create-a-container/client/src/app/Header.tsx @@ -1,7 +1,6 @@ import { AppHeader, AppHeaderSection, - AppHeaderBrand, AppHeaderActions, AppHeaderIconButton, AppHeaderUserMenu, @@ -30,7 +29,10 @@ export function AppTopHeader() { - Container Manager + {/* Sidebar already shows the brand on desktop; only repeat it on mobile + where the sidebar is collapsed off-canvas. AppHeaderBrand itself + is hidden below md by the library, so render a plain span. */} + Container Manager diff --git a/create-a-container/client/src/app/Sidebar.tsx b/create-a-container/client/src/app/Sidebar.tsx index a1a90952..dacccf5c 100644 --- a/create-a-container/client/src/app/Sidebar.tsx +++ b/create-a-container/client/src/app/Sidebar.tsx @@ -12,6 +12,7 @@ import { Building2, Globe, KeyRound, + LogOut, Settings, Users, UsersRound, @@ -19,6 +20,13 @@ import { } from 'lucide-react'; import { useSession, useLogoutMutation } from '@/lib/auth'; +function initialsOf(name: string | undefined) { + if (!name) return '?'; + const parts = name.split(/[.\s_-]+/).filter(Boolean); + if (parts.length === 0) return name.slice(0, 2).toUpperCase(); + return (parts[0][0] + (parts[1]?.[0] || '')).toUpperCase(); +} + interface NavLink { to: string; label: string; @@ -75,8 +83,8 @@ export function AppSidebar() { return ( <> - -
+ +
Container Manager
@@ -87,16 +95,31 @@ export function AppSidebar() { {ADMIN.filter((l) => !l.adminOnly || isAdmin).map(renderLink)} - -
- {session?.user} + +
+ +
+
+ {session?.user || 'Account'} +
+
+ {isAdmin ? 'Administrator' : 'User'} +
+
From 53f217f70786c93699c70ab67494cffed213b9b2 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Mon, 18 May 2026 23:38:29 -0700 Subject: [PATCH 08/11] feat(client): polish auth layout, form scaffolding, and responsive UI - Redesign AuthLayout with two-panel marketing/form layout and mobile header - Add FormPageHeader and FormPageLayout shared components for create/edit pages - Add useDocumentTitle hook and apply consistent titles across pages - Make list/table action rows wrap on narrow viewports - Tighten Sidebar: remove redundant Containers entry, use Button for logout - Scope Vite dev proxy to /api/* so client routes (e.g. /apikeys) aren't proxied - Stash legacy EJS screenshots under .attic/ for reference --- .attic/container-new.png | Bin 0 -> 217077 bytes .attic/group-new.png | Bin 0 -> 185693 bytes .attic/nodeimport.png | Bin 0 -> 207996 bytes .../client/src/app/AuthLayout.tsx | 94 +++- create-a-container/client/src/app/Sidebar.tsx | 16 +- .../client/src/components/FormPageHeader.tsx | 48 ++ .../client/src/components/FormPageLayout.tsx | 66 +++ .../client/src/lib/useDocumentTitle.ts | 13 + .../src/pages/apikeys/ApiKeysListPage.tsx | 2 +- .../client/src/pages/auth/LoginPage.tsx | 272 ++++++++--- .../client/src/pages/auth/RegisterPage.tsx | 26 +- .../src/pages/auth/RegisterSuccessPage.tsx | 20 +- .../src/pages/auth/ResetPasswordPage.tsx | 18 +- .../pages/auth/ResetPasswordRequestPage.tsx | 22 +- .../pages/containers/ContainerFormPage.tsx | 422 ++++++++++++------ .../pages/containers/ContainersListPage.tsx | 4 +- .../ExternalDomainFormPage.tsx | 82 +++- .../ExternalDomainsListPage.tsx | 2 +- .../client/src/pages/groups/GroupFormPage.tsx | 60 ++- .../src/pages/groups/GroupsListPage.tsx | 2 +- .../client/src/pages/nodes/NodeFormPage.tsx | 107 ++++- .../client/src/pages/nodes/NodeImportPage.tsx | 112 +++-- .../client/src/pages/nodes/NodesListPage.tsx | 4 +- .../src/pages/settings/SettingsPage.tsx | 14 +- .../client/src/pages/sites/SiteFormPage.tsx | 94 ++-- .../client/src/pages/sites/SitesListPage.tsx | 2 +- .../client/src/pages/users/InviteUserPage.tsx | 70 ++- .../client/src/pages/users/UserFormPage.tsx | 118 +++-- .../client/src/pages/users/UsersListPage.tsx | 4 +- create-a-container/client/vite.config.ts | 3 +- 30 files changed, 1231 insertions(+), 466 deletions(-) create mode 100644 .attic/container-new.png create mode 100644 .attic/group-new.png create mode 100644 .attic/nodeimport.png create mode 100644 create-a-container/client/src/components/FormPageHeader.tsx create mode 100644 create-a-container/client/src/components/FormPageLayout.tsx create mode 100644 create-a-container/client/src/lib/useDocumentTitle.ts diff --git a/.attic/container-new.png b/.attic/container-new.png new file mode 100644 index 0000000000000000000000000000000000000000..dd25171749d83bae8f9c180a883c759de9d008fd GIT binary patch literal 217077 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
jHZo9m=^E3)?dy^e%>H*I(m;(umQ=23UUs@t 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 literal 0 HcmV?d00001 diff --git a/create-a-container/client/src/app/AuthLayout.tsx b/create-a-container/client/src/app/AuthLayout.tsx index aea2d330..367db4da 100644 --- a/create-a-container/client/src/app/AuthLayout.tsx +++ b/create-a-container/client/src/app/AuthLayout.tsx @@ -1,18 +1,96 @@ import { Outlet } from 'react-router'; -import { Box } from 'lucide-react'; +import { Boxes, ShieldCheck, Zap, Activity } from 'lucide-react'; + +const features = [ + { + icon: Boxes, + title: 'Provision on demand', + body: 'Spin up isolated containers across your fleet in seconds.', + }, + { + icon: ShieldCheck, + title: 'Secure by default', + body: 'Push-approved sign-ins, scoped API keys, audited every step.', + }, + { + icon: Activity, + title: 'Live visibility', + body: 'Monitor nodes, jobs, and services from a single console.', + }, +]; export function AuthLayout() { + const year = new Date().getFullYear(); return ( -
-
-
- - Container Manager +
+ {/* Brand / marketing panel */} + + + {/* Form panel */} +
+ {/* Mobile brand header */} +
+ + + + Container Manager +
+ +
-
+

D;p*5fRoy$~YeGY;f`)T!)}h02XHK{(Bi!}|vxZD}cLf6wk{%eRm+a7Sfl zo|W@ulW>8MSqL{m<`%4zkF6%(1vW(>%qSxuSH#V3r@e*sIomT-%E5r%f z@40@_Z7e^KQy}opzubJ72{ND);5y!P!JX}eYEumAl zpX7S{qo*>J_NyP>RwsJ4_PBmSXI@Yc?APS`U z$|LB2O2hJ$9xQU~m;*fpW(+I%OrJvdT`Qfi^k5#t8ej>m+1TRfhZv=0Ry$;c^!IO{ z?2G(EMW22-j0iS|RC4IZ35+;qer15q5JoJgrAsF~!c;bFF$d4@rxvg|h;~scWd3;F z>3ixgA;wzMbMUt|^GFvS^c$2sGZ<;F6{s~7yhOX~UoC-Y8${|&zkW2g1yXkBSA|#C zuG(X3Mcm4>Gm{aJCIvH31vGj;xpTpxxLAhLkFs_9eT3P$b!y%VoV!=10zJox$K^u4 zd2mw2%8*`{-1>TPIF7Od}R_4QLUZ+SjO5IG7wX(iyR zrswKe??_m zB#@^#7R05e52#QFs;8hGvu@SSV*+AX4|XBQb?JFyObM=f^80`%A@8~^zB|&9p`eDJ zCbzpW7K}~AC}u0yB^f)xoyA>udULZFkAMi1t)?)YC7$p?-o6`N z^{18(ckPFrK+K%}XZQ0W8jN2h^22$4d zSr!2;e48JvpcUCl@t5z+4Cy@Yw$-!pPVMBWiXRgj?Mt8!MFmdrEi*|vV-qdXt1Xb+_Q3+> zGWf<~;z#k^zT>+cdp&I!R1zsqBz-(mmM25W0m)@}+#QQy$)4Afcy}j;_r|_grTeZk zi0|?}LsE`gD<)|OkiOa#fy}GH^0!&~DnW1^nEczm2rBpWL5B%sR#aB0%`9-X5C#c5 ztrCm4y>mu`$t;)ToIl?!wfdsF(v|jwO~3wm8^qgnuEB62pcRTNDe`Wvt{^2&Jsd;D zDzqRaKBcvD%4L+ZH1?1kVLjYo(i$&pZ^uj|1(7Rc{Dt1YevMDaj(BYa5@v$iS~Ld> zo646oe-%3vITp$N;P!|4-a;uY|9Qxaj(hvVI$4^?aNQUM4|!lUz^3D!u`GOoWCB25 z`b?1?%?9$T-TqF8lI1@L2H+{B<(4arAE;}LDV|OFj-X1os~|@vHn5j+74NedH64% zbn_M&z5IqkHS4Zd?VkHO-a>v)Q3us>3%VvE)_dr%M#_WHjVcjUms+-viVfd=Dheb|x9X-bywkD!8mUF;T0 zOF>1lElo2l%fwJ?i}$gCnrnmX!v)&;tW0;N)Uu3mU0iO>gp_Rf<~`XKRua1pvlV?) zM8YlILKLMGDH|Y|9aZi_Gsum{TT6sz$?3M!#oLjba&$j=D@>Fx1e8IWI_G5sAcEP* z_`BGTnU+KNrokKUzi+TS z$4}Qi-Kf}DbD6@$!|dS%I=K7+x^NM+mgvXZwrjKM_J|PAptWRrc81ZgDn9ONAucFI z6Z%|yjR2)*nTCeAaQ>*f?~VE}vl_KUT8qT5$L1+CE}-n))o_ z2%2AiIw2^ZEgo%Wuw0dRGBaB(?{2B^Agw2XbD}4MFnJ-jgd5Qg^iAwhk_a_^a)g6O z{Tju3AP!?T*`iH}GBh4zK8Tv>u%*1Ic4{i8l4gYJAZ;gmWRkxo`qB0RrEKKzAmu!2421DwTLLI!*p&nNT5#W+ zH7OF>C#xX4fQqB6j4d;2r6n*gKTrlIB+X)BooY95?`kL1Z4tfpNX|K1D@ZHqK7fk# zk3e9FN1zX4<&F=bQL%Y5&|m6aR}JZZ&2O(Bj+(6>YL;&x-u{klW2H{y6{9?>98rh) zhU%Hg1P|JM3^NMf*hSu`)?SNe zO1!5~H!I_npeKq@lgLQVJHeVhA0kx}gfl4R3h=~`d-MDF6>`epiCnexWgsT9b)$~! z^V->rt_uw%7AMil$+wO&dH|*|0V#3$+ou$#1Ewx!4Di%rcU5g_UGf`>Jalfj+C%%J z2e=SYj2rJWEFbq5*N>8?6LGy}H>EiEb$q-N+9D1^PEzE2B1`O^8gFwBa(*|ol6s92 zHj z{WZ9T%K!hc`G1U%BuxY>f;~g%8wu{w7o_H)|M=9NUn1ZoR|#}MJ|YKpN^K|K(g?jp zW$Llp4_67NgV*FC`l>N@A``(EQ$Ei_PGesouYzpLI!BT3@~*m%x+~34F0x!PFLC&4 z1h>8a^zN_~2U>%VOFt&{3+963^PD;N{XLC8RZ5e#(E2lR&~`ou0SzQ|7!I)eA;7wx>!V_BT1oF0jl{y19(XM zEi~{C8U!3#r69~S$MUPWeM!7Qn!u1sdV?WGkFbVr zj80rOWRYT16&1*9Z)_*`-CyTwypn~qfVRS!8>NH(6Di9G2{oX93l-V(=dR*uYDan(!ssIhaJDfti{(I)b__j^%tgjxy= z?c%}E`c#;9vd*mM9SF8*F0}SU$BtbG@ht_50QSL6?xPQlH@Sm4$`IwnlEWhoO|Vwy zu}G7)%e`+1c8_RgEL3ghDRXaERJ;}fUGg5B@?j`f6?Ne`&b&I7f z_Bqilp44`OTC!!V9$Jd-EY*_J1%oA0-NP4ZNpU6ISl61d$hslDd>7?77|9GLK!~8{ zVeHsJfCXW&vhwB*qbpH=eK1|M-E=#YylILVnkOLg1XFUxYs}U)HO)k*>2R~FD%es5 zrL39}xVgCj5;Lbe51?V<0AQ}IKZ%xH9=e7IUKs;k|2c}!6!S5LZ%S@_f>$sY2)`!e z=IABhP{%MF!gFdOETe?t+Y_@!^dqlJ}bC+0jF+FQp#?p zT#OeE7M&kCV8_G+|D|1Ad@a(%e@(~n=L_tnTMaJ+o-1KiN-oOsu|e*2r${Y4IeHq^ z`o=&s0hE}CyV@W&@aflwuLchhPZf|JgyglNip{L`RT)y<`?>8Bu5MomhN?yPORDpR z%W0Ga95%7-q9rEfL=xW_<-X-hQ zMvw=aC#^2sesK?zsibTZl?hY#br(d4*=oVIg9NjRS-A|oM{dwd)g-q;WR)v{ny8^5 z$Bsu0NaX$1&I>y4wZ#O3e`*1OO&@$tf}Fk@%kMojp__V}bXR5dJu&Au@ZCTLgPwXU zD9MmsTH}N!E4kd}qU&Lc(hE=NAP+| zJQ@A4*tjj-eV1}J-y!lm)VdD+GACVrV3-! z&JRB(uzhxdroeIOap1XqMI{61w3%WU?G^cdNU?qnd{NyvABxO%6CC+uj-Ue{?6&Nx zS>U6G&NR+7?G3Cgjv}f=M4&5qG%YqDlA+3xk}3Hd-b+s%8MV#tO`P^cRKis^@u#pC zR9C*&pR+U{EbkmpghdH?k%i|hm>>bgg`0zs=o`4`%)t(D)W^<#tUj^T{JPCI-&K}y zSTMTaGf5C}i{ZD-yM=|)Ki{4faCQ;I#loQs1C96AuHfBzbH_I26is0MYB;NkwE7t6 zTwOUww`$C^J7c7RX>)5cB?GDG+*aDaECQS$eJztsS`gwYdnsb=sGKztm?oL3-+>zl z8kZ`^)GHmW$M0fKON;}~hi-~IcylZ(*4~YSA%d4psr{|pv@(GusNV2At8WaNkgtcV zOtzkEp9xZ5k?6@nFd5Xx@XEZr3=^ylgyY%qbLsMNtU9~9tDu|Ago?LOX5iiA!v@@+ z?RkC0s0E|u`vwcQ1q@MHH+$>x=QyE~WglNG1(fLP1b>?7p;!7^>)u?SZtgmsYR=u& z-E|*k!q0KrCLa5VC0i_{pI@9uJS9?rpah{!b9qm+aO|Kp3XNurez#JS1t!xePwT*~ z*QK$UCPV45{cwDs?1~B!{El=p%G2!zod->)?6!w&OB@M)BYo1MVNB}Di5b=3f*UxU zeRxKui6cvB2)C~F#-u$~{-EewZo~35z`=lGi++SYk+L;Tl%Xvt*e6cX4F8Pd$kLIU zL~Tq8T-EimbMsmtQ)XbKd;PwO-cDSDh5>qiZyRe; zo@D^S+kw*&Tp}u*pnL#!B`;_8RC=3AVUM8aw-~tADW?>0N(jOKDLPB@0OlEjKA;+Y z7hGL{EjOTHgq%-1(bx~Th*Jd?I>;P3wNudfcZZUFf=2eBU_$qHx7lp29MV|FVo?1JN);-vgr4YA1g3vh@G4sMOEgt# z8)-z4tHk8<7GEP{--hqvQDZ}9CVp19zj%bsHBn3uEFajMi+8<}Sy`#%q#|CdCCmKV zaJ;VVx%->lcti=WdXH*xu0&TC?JQBz-gJWqPuC^4Kk>$gXD8{lml_gQXE!ap4+l0c z)FXThn%s2??gg>31}oPPp=y(r^Rx)q`!73AwmeKfwbh1AKWFbU6{o+0TF*5b>gJSu zaMo|#I^LnvmwXdy);;L#CL04!xD}bR4<&^l@by${5Ju}k zS%F}MI%AJuniqLTwX@cKM^*SMdEI%Uc2?D#jqym`c!A{v4{nAjp|e<9RX{K-2ixGK&XNi`*&#aEDV$(GDLS4 z!KbeW;hlG&;vK^3oYpmAJaU3s@rXefXVu16Snp(Y(G`2pA*#~=!BAxqGCfKu18dR< z{QsbdeEA{Jmvjj!K2z#(@ioa}O^La3lmUn@pG}bx)B50~hSZT3F$_&kcGj2!9~;Pf z?7CHzt2%dziKUlKUqAzS2D6&HD)SwfQ3r!|walmL+jXnpo9JDL0dohC1&Cgk-7Wj# zZ0|=&t`7Cj&qAfgq+l*X-f3q{otyTBSD^p@<=<(Ul+|AXP12IA~BmKZ?f$f@`zz7FdO){*s zSv2vRd$`qp3n`>1FRe()Xhrkb=}jsy4VO_)ePw_5C}8A`H%*u%pkY;djSFHfRFh#D zj^AIO+r>)SkYwWWQC7{v9NY4DT5!a(Fh$au8puU?XO+J{N`5oxh9JE(&Uei{2mUoh?`v3;C|eN(qiPAR%qy+eQyg=^pLhUCs)0ZW)i-2+g<}(JhZr~Z}y&Il%8T?_C`K1(~Y1lT1)Q4di7amfS2&M&;SdL z5m5mf{@3R<&{xO9dwJq)yX8PBspu}R!(jO^SlkyU6RJ6NtEOTtFS#zh6B_mq-5n?^ ztk-u_B--%K;x{FWRjxUHu4|#c#O2*%x*~!?_nEtyiftOj*v=ev1@i1R;q-N?uzOLH)4dDRPcJMa^ck3O+!f)y-YhLTa3O&n*r*`X9i<8LEP5Pc%B z9!t-{^h(Hw#KLFCY%~6v?}wm$xE|&v6DIc-Ka?y_xK-PXe;0Pyd*qI`*xf3Y zu6zDOxkjt(dsKNWox@z8h(TkdM&+^~x{~(DbsZfY1>tc=Z7A)7^N#O!TiiI}zX$ag zc}eca7u{nQLQUpsm2FAU1+pS0%B*COskfJIct4x`7{W%qE;y{5tC7p0Jp|AqY&q?V zCU|apif8&I0_v-VGbpyIN~xOh!1QQpN%W1ZDNBzNtdF61F^inm>zr=i$x2RU`|-x1 zp9($L#M-||yVTCuJQzpVbIqP;Swzz)_9oeGWQ6cNy`=o~v8T^b=F#Qv-&wWGS77PC zg>?@O0Hp}nH~}8|Whe(ACWQ7{9>B-l{RJ&vcIsaqtpo4dMclrBf2X&@P4hg>6ci0c zi6rqOHy5V&)ads1jdV50*L7`gvpx54?M9qrSc%=dNhKiNM~8oy{hIW%z2QXQ$%tp* z)9J1C3nSyy`R+lfmwXi%nMA;ZWlsS|P8g@=TdPl@2~bfRxCVQYC1N?Woi9m;Uz2qZ zer@}dC_?Db?AGTiPZN5=PRw5-IYgRBNYS!AP@MrRuu@xgmfB5! z#dflhJ$S}zQ!ux;cN9DrJ3BkxXsUV1OT+|H8}<30Zw!v7`Jc+N%44|C$g*>iGMj<(22(-6Eg@Y{#*Aep_%IJ7Q1eJ2B= z*Z_eH8TJJjAFW&ojhM%5k9zeml7x{arsAD6S0$Nxg@+A=oiy(DG{d7t??opS_KMz% z9?x`PA#|*)fe&ZMMz=!*0tEoosS>vKz^VfwXBku%i-FS7rG6vtAGdW2h2#Ajlsupo zDq?Lh!+WBG9;+IuFR40b08L|oz;@?u%bm{EEYVn%jzDG6nlG&@+?-GvW68LlcxWT2hV z32bV#ow=bPYy+{epW9jks0-@vu3@tVQ2ZNeySuxZMaJ2IsnQV|!0SsCche$un!}h9 zoIsl;nAu$4HGt&PN&MpNghJQ zrWZQ+wm56Wj_aAd{HX=7O_}l5uji0)O{EA$3cA~6DA&N|puWgS>b+^ysoEKR!P9$w zkAKQs|Ax*;GS1+yE<`JduZLYl>(j9LN=n!m^BhYK!m*I;L=K3=uwAioLeOD;b_ngk zCeakmQYcU39~_+jHIGuqIIp7R)n-F?cZqaF3~UBiTnn1~QfM`**I3gSk=>J{o?YvA zTDQzbU7xiPg~+%c!)qesFb`BABwLMhdz0~FWWHF51)9uG{ld4_eOXAXDmgE2KJ_={ zg^g%auUHT-Z728S!kMjyDhY-h$Jldgu`e%3<$(bSYDm9nTbJ|7REohx*(l@vJz(AGAcqZTjj-FvDkX$7SUuYtQJA8~6kG>WC_z8TL>Z1|R8z zPdw6hz;2(Ct*XQfuAm8MZ8}W17fMG^X?z0+kduu}M%J)1h!uV~=t=VR=+jrPao(k* z`oLHt_um5kIUoNX0I+xqcR!A8&`Hz1qWji|??7%)>RoxeDpCkp*j93Hi0~lSLSX@k zz~tkLJzY1x`2bE4*lP5my4s`nO>4BvTmxW8mI^ANN42Ha7eJ;Eab78ajXyx^Rie8l zn$L-DJ{bA-N!N8_bo(5C-ulu~mR8xbU7g8tNwfCUasFXhYJu&`1yfmHTTf)h`y zwdbhM0H8chJD@fL8_gDGBQvGqJkLpZ8p3v^{NSS#l=v~(T}q<~<=T_od+H3xe**tK zQ;qijng-e`@r840rz|w*<6|-JziJVQzd|-+t8+X zlEsV6ND7By9?qey^9-&+bga_IS*O~4ZhIla?12wI9u~#X94E9~A2@?TB+J#zF^J7w z=lZ5gvN45yIis&r-NNM2_*)Vok;k?8bG!a^>6&$nNVWI=CR$^@0F5v8*7SZ`K`0S9 zidj^%;!*JjEwip2D%e`Fou7|50wzR@!7a6W_YzrE%b=pdpynW4#%sLLYaO6^U{}5* z?Vh9^_dsPw)dM-srgiQ`yJVn0fN%o@VRmdR?BX3|mkM-VVhf}ny*gB15PGn#Tv^cP zjyL+iY6c{=EscytdHRercQudVGmT1gKUA?3G=hnqT%hQ$twFn~Pa&m{HgeLYf$+=q z;C`7)(Yhp#mXnq@*kR|&#(Iza`)YdPy_3I#MSxe=@dn^vlpmqvSRnS;&7)rQb(7oG z%({&^opZ-$?m)uwJCz11K)Kttq0clBZo9d@PRQkqw_nthhcn8kc5-T!CZ>ohJs$s( z+yRCY9S~W_vj?g@bXUPkHDgTPF*P=3R*bLNW*o_>Yg3x)E6*+>a%15JvaakiY_Wk^ zvY!zVy;sBZu)j*V(vihu|4GZ>O;Z8uuW;&%!>jyss8Z7=WA*HiM+dSH>v`J4AOK{P zKYepbTLn5^u|e-_v|U#!&d5n-ilkFtFKtC$lg#kkmh_aoabqMfe@KbKvCL&UTRet< z)@sM&UPs^s*n&CozmDqoafCqaS=@b^?5_!1t=pY?rZZJWCrr58uDC!(+T7ZL#PQ(c zix6=icMDF_JFl+!B_w!cDUavWIl43;cHem?o>3ro;pUD<*I^?1qoq@+KXCUgjY;_& zKUZlrS{cR^Ov$X6Y0&ic(BWebF`3aNB{D3*yMz8+6e!i)-iq}jt5`ni5Qe(m!hKr7 zB9HDo8fcch7JQ=YM<-g9clhu#<4mUH6RJla;dZI8DqS3Dr<5XVZZ7ySgSF3b3V+)# z{bSPz?6a?JquH|DW^|XWrc@-pUFu}Lavg_`E&+_1Llq8|+iUC~!Ps(`o;+5$K42S> zTQrNwI<2AUKO!>2=du{*b_(#>_(}yYCwpp zu6`n?qtlh}v^gheR|BwC`b8NQ? zINWlB6fVQoHGewMcQ8qLTARZS(OVlEFvFilhtBaoKD)NDlt@@0=5_N#-0sef?YEC0 z6wDPG?c0}<#btapfU`dW4@v!HOlc*%r}i@&i&2x^E?8)N(*IIwzn;%zWe4PDKG?4W zsRZh@6{ys5G6f9>dmN!8q6AT|VDH(-5b@~_qfCwBba^>H*ueGSncQqO&zGW#58SW^ zqE&PlbH2YRPAI?YfI`#A3Bbl&R@mT$S@IhP`Wq7a^}s%&8E4jQ0Te@T27_#gBc{5l zTZPwtm0b|PIPB9o1a@z?`7&uizPkSY3TTLgr&{!_WE|!Xq}Er2b0Q8eMGG3@N0I-T zE`X9;*Zpzuh_w!6$f}Fk8Jk+y&I+H3VP5Ksj^TAqzfuU>-$jA!!7!cJ6*dAHLB#5w z?n`Kg8nvdKi;#zOKmGpca2Lpgk3HDRyX=hCt)o)g24LsVR}V*4BLUULt4; z%zQ~IRMiA#{b)`Q8hb0C8P9a>a&CijztfZjXt>3ovr3+p_PhJcnx$Y`TAR}(b2S}< zy%KrIER78L!EvX>5qF;oP!O`nxU|cUEui^t>p3k`aM7g8ZNoN>u;b^hb_SY8-Vdfl zL(94XnxqF!A`xo+gR?RWcccF~w28hPKkCT;9Ed%sn(g|CTOKXT``i5aE)ILP=am(B zOVhvHTFQ$L#&{)MzYF{7fZG=9)xBYQr2SfN8Zw%4D&ZENJ=S%3BJfjK^#*J-0<%x1 za+)C|S`eTzUIGXgfkJA($_?|`%8Uah{7i~*-|=MTPu5&_S<^MJfygMZKnA#3 zxl~-W0S&T4uU^zcWNOaARKR&Qb7Y6x!>RJY$gaeeXsrYX^i|K%A19V z4|e)FK~HRD`U)m@yE61zMECUu9qyOlzC_srOR$A<9T_l zbLdtX+pGG30}l)ya$s7eDs)=znway1MtnTacmNy(vbw&WuTgBc`8@&y?JXJJ94oL^ z7#zW$ITevf0SBd+-mm}f$O_*ZxaohM#q+gXY$v5ZY)6pq-8(r2VZ!GR0*-i5sd}6F z-=^&?9t9t*sH2r`nySLA`?rrtH@t^bf(%w6wGLD|75bc@;d=0Ko9Y3$T_9}^6`6?d z@1ajn_n6tA6Lkma@p!Sk-1tJZ!^%vz8SrT6mAb?3LvYv@$-`)Z6BZ2x`9bj%HdBdC zGegy2;&T>k2ipTcCf5`%T*12s)~M#JAlTcpvA2Czqrb*$!L%7N!Yi)(MeQQmZz#tf zgF-PqyiYTCTOZpVc&^*NsnTgUdtQ+Y1A_QeO7NB$_8@)ku10EL-xv@dIW}dxidAtj zG6*ADJAB+(;e-3m(vk8uEtPFpBqIB+J&89KOf#g3$ctGQuc$S*oqbm%wH1OJ2Paj< z$r=A;XY?J!%c1=hsWluU_6tG7RjYT@WS9E4E7n){(eIjum}Kq$IlyM1M914_dg$XE z^_eNtytB#*b=s=wX~poY3R2nE9T#iPcko2QnJd)YU@}B1tn%C2+D)ZrWA!4)b|umwXhqE{l5-ei4p8l>N_XHoio-^?z>mJpjoESG`ZMSmxMhxvo*?ct_PO z%&Ms!{|NtPya3q(Rv=%9e{cPM(n&lpnStfO9s%Z9==DTpd^-#WjXZ)gsf?M7ZvIO3 zUYX?g?0)_BW6&eVe;#O#i8DNMmBL`mCLepYM1IFI8y z{(x%C9gT<$%R>&~+{wIQ5#w_%v)D`e@%Pu~Mg~V#+|8QOSB>QAe`;b8|M$Yh=cXCO zP27QcYUdoFyhVgGj%;D+5w$zwGxNJ8#T6yo!DGt?kA=IXSxfqHfr-BWTObsHw(iF3 zvl5q_yU6c9u4|G={tlc!@8BNp!2fw??{3n#|Bpr(#T7N&!OQavb2=(C)1)8&Q;vv) zY|9~hb`8x2(uD_NJP+Y%ZTMXIyb^rS4zgNkTbPU3on|n49nXA|v z^7SPD{e>eT_$=M$J$wsUu9!O6&q?%iasHYj#8e2A!aX=Ve)!k4X1z$F`S;ywX!Hux+|viTqUB{cQxra~3*Sx(yxb1yW{mc-%rgZvt_x#dnwVRs`aJ>J@G6!AMs zQ^>mQ|JOCWewt8JvnaYXKj3q>A$D?|-s~U$W(lbRCuVr~%a#2xOQz>u@Zvuf-s{h= z+fZKY==g=W;$<5&(isYVDWyXF*DpdA$DXv8f_OmIWn>rEJ+W_p2-Ntm#}P}Vo zr}@z#R`V?lk^$l0TB09aDNfr^o|rF9mu}8un*WVo-zwdnm$l>*S25FRF*lCC-T}>m zgyGL|7dt_ypj{EPCdSuF!!7&&h{NlN)TniN-theO#2RSy{O12*{+8!fNr^MoMq`IY z(waYBLtZ&q&0lhuVz-DJpm1ymydU`A*Pfdd?*7CLbnVES!ZpQC(&+j8>$T@}J@dSX z8|XZhw9v4~OChUS^{)eLBL~&KF{j;eRDZ09>+z&Oebvhu^>`LLxQxg_?l^^b5$cUeuf}h>*0Iwg@Lwa%yrwLE*^Zu-)TVkzekQS?YS{B0{r9{P z+ew4hK)(70+rl06@5<}`>*7Br+NoY*ZE6?a{7a>@e@mX=Utegzd8WjSMW$D4J=ecK z`R})l{hxwo|3S7YVuN^|vc&Yy(iri}3klk@6Zq@{VV(yI8m-T-E%}**&23Qge-K|l zB0|A4Dk7vhzq)>2@<_&#q2GafZYbYSVG~yW=HFm)?5RILyC0ve8GWP9 zjp^4yO&a%qMBX?0#P3cGste9fRhn$lkH5F%#nDoj$SC&)ZOxj0gr93I^thYVUvE!8 z5b-?D@eFUSp6dKB{2tAPX*hT9`YHJAITwdGBHOtxOY(JLPR{+`#2*V|yJ-vN;^x(N z%@(GY!q&W~-@p9pZ^XV?GK2*wJn%4i>mLz;^?&yQnpk3yjkMj&dAoP*{3tBk^`DDu zkPeGX&i?M4R`7r4)N}W-338>K?yG%wExbzFwJrai`gPV~n+TeAs%8G;`Yr#v7aWwt zJ2<`R>=%nGe`>iBq2$kP!fgk%XdON)ot4Awh#et&{(tP)7oRVoeR54yFMTc*A|4)6 zy4-(^91$sqGrUE|8{_9WuPPbaRhyj?n zQcEP5EuKr97vBB+7Z;vQjEPv{;;RGV7sA2c9sjv66Kmp~3}*N99}Ts?i)nbYH`*i@ z{#1Mi(Z2m6QbO$ zu+>3kzjhk2lk|Gc^FRB?21bK!9<#vpZ|J)g_~snF`>c7Bm;AMsOi!XtDP^WUC-k4N*ow&W#kw?9smMsmE9 z^yGi)$W+^O%X9o8h?)rLDZ#pp(ZqVa z!ta*Ry@rv!F(|iAO&OG1NQbqejTIgaoNA~)sepfKnW7kL|Qe92G z3%b+^f1VJucp(|Po>%_}YQ{i<)&raZIEksTJk7@WTENu!H}Fll)RS7!g$D`M*el6? z$eVlC-^d|9@`Jgj@eCu=LA4zK3!Pv)?;|22!Zm)(;04* z{2rrci{8`Hr31o4CtCNL>7djedh32jpIuP~nVE|K90~gJ6%9^2E z^ikCN6ckGN&jVQf%n#+_{<<{h{szdPcmWjaGTJL3eV-jP5vhKEKmtD9=ddn%Y`?`$ zUDBGLq-vqx_bX0_v6GrJlK|HJ%D$_}3-DGM-oUUvFEYnB|K3O&#Yk>L0pv+2kek8C#{Ofoanc z2JSl|EMOgX;*$Z+?3b6k&2yB0>blYxkeDx8oO%05G9VsV7kyw>sNH@jr#aR5vbaK6 zeigzL4NcOKx2e@p3I)oc6aJXvd*1@dbAqj=?S+)!NeCdndi#uXF6{oizEhPwwUx1!mcG(H_jdG1i!tLblz*VA51Yucg!4E*EstR*6Z3jt}5;G&-1p z90NS}0K}8PWacv}$@{?N?^+wr3-k>yxbn7>5O5NK5CgzWP}MwAuJCJ?)I`dwIEQ%vtcL3(Ms?QW7#1AzipzwYRofr)Oejk_A7do&qMx{-!X|K%p_9 zj^mwgL*dMJV8&auy}jL+yLm5=MZM*Qc^|m#Qpc>UjsceJ+7mx^{_97x-0F&Q#KJJC zU{CtlG0G_ugf!W){_IbqwifrcbB}fn9m;iK=~{mK^IK_?b=_?zQ}>y*_GL^`GmYO+ z-7U7Nzrl;)kKoN3L%yEH{W=9?X`_O(t>4w@DxQZL?mbe->ZrU5xakuDsS7mvqLev0 z(>9*H28t6Q9RZZMO%|yC-4RjRyCT^0HUf$V{zHN}063M>I{;R6h7b#4DwvLY$_tnP zB^L#^iQtyL=m;ujYXo991Z&w9`byhv_!L4N7y?PR zT@^3P>#i^!94`N%6n?y~eMA{3OtdpcM(YRgvP!y*Rp^8O*t&pL5C~5LJsWjjDY}zh zy-ymWK>4up(~G;%kmhf?$mKd?c)r7>?njKV{b+3&qXr?a9m570%b3{vyR3T_-$7AeX@X^oh^F2&vEpz7vnyn9hZDeqic8U*pE^C>+t8rYy@`}OY}B2U8I|JIU`&)ypCKvZm(Wm-#t8vG*&|=t+G4N7DTbRc3+2!P?g!Wm z^MNF}zKAm){g1Vl0OO0F!;~8YN^y@=TLpF>kM$UfqB*H>8q6&~ z`VUTwn3`w+ep{&`G7&Xf6j)yo8hR#HCtDIA8{Jf+8AQ6~L|ty?b-N!&KB!CE_r=MEMYGyz?RT!S1 z%Ac^vc^Jh8RDRjXRpoa-CVe$B&+s;2xF#K;Fx0(yH(<7%B_wEr z!uVjXoy2$MwcBr%-V~xDWqxPY@EoFA4=%6+<7Csxa5)lEa8UCjRBIH`5yq^WiO}dm$Zq z^*D!el+hcN`+BP&I-Kn^^)0F?TU6d4LhqP8=#L-zQZVJB!IlHNU>3YSK(>zP zwFqlxTeVI2h`J=623QLKs4xa4Y;TR}X3H%sEZD49Fnk0i;8u=G7MgC*T9xvexQI?B z6m@unnZ42f?yQK`0t;gGriR&ezhJPU-iBPZjUg!xuC|LX^zDfLh7m)$rGjOgEUQu$ zAyXZvVcK^VX%|wj%nN-njJ7AUMyo+>mK=HKr3EoZ4PSWzAPOCYaB9XgZ#~1&PAz%) zNV+yCC${*(u1Kh;BksrFRy@{0NuG2BUIial)(VxSKd6{eF0)Su>sMKpqyK5t{od)#%T1;B$)u}-4D{ubO{X-TZ?f{$m%?A8!lMs%X2C;R6-g8u0&^b z79CgoxB9VW0c2DI1=bEvs~6snj(-Ol%H+g5wc+rWpG^4Z$qD0%9NV4;wwy*uK-spTa>amvTC05GC(DJL723Qqt1C)%hHZ#m`fv8Z&kX;_ohA8OXf#N9H;{-}tsd%_g-kU+^v8BIt^dRfy#O%F znu)X*!-r;iD@bLHxT{9-kin z_?BA%%x5R)C;R-WWZ6X%bo)VIf95wc?Fc5f z>D0J_>uh$|Pc#WcEn~T?8=hNfH{RVLmk`DwCmJZ{o9$?~eA%*?&&Tb7hS{Z+1A@TI zeWt3&4`hyoaM@ig+hJPuq?zJkOJ0!Pq|u}azq2xBv_JLob1c_Q27sV&nT*bM8Elyq z5wA3voh{vxy3I88Hs^BcUgz<_kbIRiB>x$VfM&;paR8klAAacayW?Ra(=WUG<<*xi z3TAUd*-B&CL&3u|d81ZqII|LqQ0mrt(7A46i|d1;^Px**&{G5WSazJ@fuLm^v zYUGv=XFGkS2b&xRjB5ZA?2HQayGJ*p{o*_u<~3r`43xxy3v!kTv4fZz*0dxphYjV{CKI3GMRRIS+-W9Zc1Sm8i zIt(%J=^~-&$x-HmdU+*=L6j2*j}6`IJTZIb{iV-h9T|QPV~YyDw{yX`1xRv8qW|D5 zjD0+LC5<2iXTF^{XJ6UZUKakuC!+Y#KKAXY8A27GSh4-s8SzJMPV4DLJhri6H%O(} z{r=R6j&hJ*1^Hv%rq-Ws{4r!0EHLD=$p(QG2inm>u0*vMm9Dw{TRE0w%*pJA$}?mOU-eVZ2IO`L{$+drEUq9#*5PvBW$mxHJ)3x zLZu>7CLls2g5X<&6GhNGHc zl7YY@57a!X^o2skervS~X2j6&S4bqf2(51pSf(iVs%7*KVLL4`!4dGeY4s?M3L2!_e_tDb#XZ)9{pK< z&9;j#&s`gpiu4}HS2{iQkNRC`?)b+GjPd&97Z13u9>!e4bM{US7y>*+h?IT(r1bMvQ=H`F@&2R-LcQ0qzl!TqP@j*{blVkix%13x>nen z=TC9uDlrump0)nNythV;rB`X&@%u+3U%7POsf#~9IciqfCNv#jx|SqHSjwBJn&$-Wc5BWbK*)1zd@sl13f-yNCM2p_zL(Sc*57ysF*5R>1-Gan z3)df0L#JpPtj{zyA!Z>@gX@C~_XRQ`17s=~-UMk>u}XRGBaPUgjSAvbbA`8@$TAvo zoU>%>(@Z17tV_ALo0@!BLK71cUmxxe!K0~$Et9*=I$msArm_G=Xi+6K_a}Y8siJxW zUw`gA`GAwt#YaR;(dvHAit=a`uB`VQS`{ppmR z+Xmq3=pFUjk}-`i0N`goWKL1X0jW0_y0(+!P=yBRf>FMFtGJ&SGn6wE)^cWC(_b1ECp7v5 zh3!6eF3&9lE>W@oAj`^p`}VCgOhIxI$>iD{mt1q7biJQXDQPGYmyG=^CDX7G3D&Zy zSqpn$GR@!cuj9CU#drg8p#6u)t{PrgBflg>9Zo_76S}bwACD`%PP-LcRH*-WLnrhh z|BtJC#*uIldmA+>i*TkH*cUgFo{JKqE9|(s2XWls8l1#y6PX&!hXnRV)JdRN>hP?K zI2C(wLLx$Wd<=e3(E(L}TGH4~qFQNE6-Q|Mpq>)FdcMNepyNh9Bge{`DMC98c=)4z zazQVrCtc5QQ(Y068BfV*?$?SqVb3NOt2&)*+p~RyuRYjR7MY4>C5~3*xw8eRK@HT6 z^nr>A^;hJCxt0C$5DM!D0+W6NXJXk-6xaG{hmg}QHys3iodw!kA-T2=t*_x%C5b)j zpJvgJ{^aG#sw|FYL0LfIOR)pWxnXj_tBFF#&<($C(7Sx<4uAi`0Y2VHf7Vi=3r=l~ zcFS#gM!mnk#p(Q0z*zRg!DonSP7}lLCzkC4h8Q8a@?B4rnZJN_I?`EHN7v7N)Xc9_ zp91)4Jk1ya(*+Rn@Ine42UaQWaX?Z50#J-_4ZyHBN0LyqPzq!*v)2Px?22V~#e3oq zC0Es8+gn2`eBCg3S9z>ju88xpmlG!Iynw=tgzL*m=HYRqfYHPXTG#-v_~}TN9YWzt ze-CNuXv8|1pG_pY@=#CNVD*YmO}dx;%fWB*Kd#I&qF4&Z?swdR>qwx6^8xjFcaR>@mB7t?x%*J}hOxe4r zQ1eHaVWR(r6+P3U|BV_}p$_AJhiVBBQl+-jik*?+Tm1PUkMZBLcUT|>RU&_={% z>Uz#*Wx=zupT42u0Lqy;VAv?IyuUl$k-y`_^2u*VeW6?WKG-!O%ce^oCo!D1_R9qo z1dGJ%V~;9lyidzHs+1*o5;PYyhGoiCCJ%&EV1On|rKVxM!AO&&Oj3V^GE8**Nycjd z-R37g%PJ>Erju;5YD>GyXVrI&dTvK`#6WZp-F2pqj$8Q6>UcmzN4j14(sM`O<$%n? zIUDvy#DPk>2UUy8cj!=X|CInlBAE3el7If z!C8eM+(fc;Vz|HYQwS8+;U?mO=%iQ?sm@*#jih6*NksvQFhx=nS{9rFrESU2Q<27tpt@A0U-Z~&aLSFMUR|Cf)NrcUQan(&7c*Ti@O>+Dng`NT#^&y}A4yU}hrvz{C8`6hwx62fFWOSCt zQ-#id`G-(&(9Qc45aN94whoCx(z-Hfo=i1?W|R-agFiz|t@{EtFTMv`kXEj7s+NjJLIiubk0@ho3KZCM z*hEcqGONmF;>~%~y&ZFm1`_u{8+`+FpI1V~C#~sAeHy7olxe1>f_2iJjH+_G-i@$v zB&@ji;-Q2cA%ZOsDzK_6YT!NnL}uCEr?f^3sMo{vEn9w`nH6>uEt}EEYAe!9GiS%{ zQm%b>D{e6~%S!aCw13*kmipp!oQ(h{jO8r6-wKID7xBoUqbJ0E(eNwlV0`i^ZUB z{&g2|Ltvmr$ONPs7B_=u0l{xB#itiR9!7k0z`g;Z6SpQ`vvay7SWUaeduv@A4Uf%1 zkSW%*%r4$09Doan;x_<_T?`ru2|rsDG_houvM4+Yp-XufDwWGu_Y0vCeJX!hEJ3mb zH!-tP6}B6fR4;<0nxRR5^H;ge{W)h|&QBc3byfVdQM&u7> zicy$~3%9!lWN9I_v-!k2kSfA@yxFTV&x!3!(x^{0nV-1>!9u#$6Q z{tvul1JH3L%t554i`tb-NdCT*l!3>t=(v@``>x^{>DEe7JtXBHUc=%E;n*=eC0)Y5Dvh{Q7w^ zeS-A$Xz<~(`H^0Dyx+hXl-z+=+JqU~|BXo<&HjrqOD6vh02{?~Amg6Y3$J0A4_40r zQ(Am0b5HOSkkX)maSaTn{sV33Ub8LlZ=f@cWkr0>?)h54TFeJ_Cz_RBzXoG|GAzP> z{u~iYQGkxd9Jj{-Bdnjbq-P-+H8XI~Yb z$^Uw#cu>{(@t(icUtfSwFh2V&`kBGv*Z*&J=y{(26WrQL!b|pdiSjStl^E+N0Wl6T ze0!W$EU%GF{VxPEUdwN!7ON^idhZGg-i7{xk+FUy^T(85cm+TIHa8k@x&u&ga_yPv z-w%PEkD2u2{Jfw4_hLH{Jyv$)fBtt=`fuEwn^-R$;!I$%m&#T*u@jU$1Y~q>QZM|m z=w7QpDtI>`_0K#`jkTNH|39#$EypmfLBo8t3*ZOh2aiN7IsNy-r;)}}FF^Iamt>_>l)o+Ws0-0#&r)C))^! z3<8LJnq}9FYkpE_xN-gapt=J;O18c_=)fO= z+ntrwlGQ-jJ55>s_!Epq-0PpLC^x|WF;_PhKh)mhCU%rG^*^?wxhMYo7%}Lv z~dzq2fQFtNwP#mjwyKmB|f8b7icuD`E4A1}6wkr^@l z##2zdAtf2O-{mY__03}9!e?{cZOqSEYyWA!>(kijA+e(Td+O0$Dl4jzPm?5c?7oBd z`{#y0vz+|%NwW3JgL$(ojK~jP^kz3MlBEJ$i7IP;JiE&2>Tj8q|G>rz`(q z#YAzFCbe0#JF=P+0i~~_xSw_|=^jkQd;YSCWhx^WmyMD8nijW(<^vrtgq1dX= zX;+_k@i}V+P??-hnKf;Aj*9qD7VuYS%;B^FvBN!z4CBOk9{7~P+54m*=As|@<<6d+ zmlxLGW{Q&ejK&9Xhedo)^w$K2>s&9#g%#VPDmT}*1g}*#q0P$p)?8^SbSNOEvNiV; zuj?quV>oX)yHVJ5>41*#DM z3}IOE6*C>#0XPFw_prA16#tLH$r~&81GFUXz8QWTou7G-fojo8mMv>4ch(;&aLFW1 zSY;AW7snMCmMI6YR$wGMEJFaF=xgYAkVavdSrEV(!Bk5EJoN~Yv$^7EIOeYUtZ#ke~O^4b@ITky0%DJTb673T(et$=|OKUCtC`t+nB2VQ9@?;C@k7#3ge_@AhGk1)cVh{VVHOP8#^uYDp0UKgJMl2Nt{x3UJz49o+erD= zESz-Q)wj0e37NDJJk{zP-z|lu4$qIHG8I?7+&## zcZ6^4+z!B+KzYzhPfsKCLfaKu$tkx5 zOTc-cUDGm`=(^W%JbChg%j!FsujZSgcB#r3@+}OlM=Hs7`(y!(^y>0*A)5i%sh|YP z#2D7e2c15&-Zla?fqn)mPOhCLn|B#QO%&tq2t7+M-F*zB(2Yd5j>%u5QNef6pUepe0;CLQ>MLV{uzWItIM~y>wr$G z11<&iR+v2sqm!~)WIXRj^LAJQ@*9^Op+JCenjWc!X~QLLahO9h@|yz#RDoiACgNpN zQxkO~U-JI%fOT^NU+jnO3;LlWOTEQeCXFYCyAXs{jRXUB3x^q?6keffXY>lu+_&mS zZ%T2U&^4XvN2tn0Pj_pinp7T73K`4Lt@JC&b6<*bc|tu(@vSep%LU-ni8|p)$fp7I zic(?32AM3|3WKzE6u@8go<)ln@3K-YqN=T95iQSaEG{*uNW;PLEF9Mb zypmI~D=Nops=O$IJz~U(hj&q~)QeOtCZL_bXu(Fo0b5%@J~GD!R(`uR>Y7VL+wKI5App!EaRZ1g`IJsZ;z1#&HR- ziG?!^FDq*c8lUK;rS7{!c0Zpo1n5{yG)$^k+xI#OIr{);0UVWM$35TaA!)wvn6Y?? zp?uFEX^WaWVfnt_h~-s`=@mf8ze4rh3I1J`x#+M{$?3JH4j zr5gMm%B7D+o}}pt--M|L$Hx`Xk=9wy?=wJZNGyY^v7(~LxgwN%$J=c9RP2WE89H1 zo?TWUxmwTIX|ckt2Wp@4ze|5raP-~LI5?{t1Cj?U|1p1_Q%U)nURXCLpXpx}PE6j- zVN&>5AjK9!oW<;9$>?wx*Cd5Ze{3pTI$sM26G3kn?ZdGhO=oT8d5mW%Zez$ejwL5} zBz}iO05sB{AkGvGD5R{8@*jjLge&|-E23CaP?e5RC+tC08)FcJjn#XU>ASk3E(lK} z7IXen5iPC|;lJNZ(=jZE2Y}YNr|~G3%T0Coc%Jv=;M7(cG+=r2qCaW>F_SGF=0&?a z-F=X|+E_@#b=1pI5fDKCfLhX2s@^XS(Lv1)@BB&<5L4P5e~?yfR!K0 zah~KXvu>A+PjQ`{fyW?Z!|ojI%Q$f5!q>Z@s`N_yQbR%M#UhhvYM4j7{c#c6JBSz> zedsM{!$<%?+BgO^PETXBZVB^ik1_$YA*j`SGX+w~9J^m?bcEQ1jrQN>fJcC41>BRn zYEvRFiH+FnAM@MYh|VYTnt~lo64!WESpI>jx*|c)lmO9?4Hm(}YiYWoc#3}L)49Qv z?tYT#Nk79Z;-rTP=Sy<=?Q~=~N^P7wAmIEh^5SN}jLeZr>IN!9Y*c7ExaX_Zry8&A z)hcAleBlyPau!;y)B^=&eX!8L`X+jzk`QFam_lt(I{Il{UpN|G)Y0|WmzWgu5S2JA zlbh)k5lnmGP-XQzMxAh7p!Rwd#OV&f!W+|N&`Aa+*wBJf$+@#6gnFr9gA2shfj2q8 zSNJrwZ|x!G1d(s4n8qO|9A;;xVV7KZJQ}{VnTQ=Yjd<|IsYYpC%8qffMgQTrI_%K0inE1hK+g}hJ= zWcT1@1s1}Eucg;6#YoT-Dke52g=mcn+_FORk>=z zRP&#APhucsK z<4&WQVzEMj_huG$NX=n^rmGcToI7M5ooSc*`T#qA{njY-35BK@#K{j&jMmstPuC=$ zmA1Xm-!@ex)*)h|;P7S8JOxJ&!U>R@*>+!?#Ea8O1G)#%xjL`WcT{vc!Oy|Ivx4}-sRvjn z3FoU6@z@y*u6oj^9rijs@{AL`;+t<0*-UFJ)$a3aLFg_k9ccmfd)nljQ5+^lfOd02 z#s+pm+JwJnSu@bx${`T~?Z1m$G_)lB5=-2#6UlzmG@jcTLcw8@&QJm7|LNwFd|WJV z&1#BS!cM}EM;Xog=o&dufgY(sY+5rwMjK=QNz+h@R!eMx6zKh?uF5Nz#jq|G7ZoMi z$53wj_zl|c5RCO%MgRm7a?mpa==E5jU|Mc&uHY48$&tu~Q>el=z1IO3H=LI!9pA(g z_lj5EJek?KEfYMYNqs9SFq>{k!X^nsk;xZWn`5&z1A-Farc|6)S74&{kl$;CJq90* z8nd9=1zrxjk@9%VQ436NM8RZNvZykR>Ghv^Zr(k5*1&V6Z?HB{<7y*GXniKod%~jP zW;SrWUd%$O<4CIGdJNe=C2+rn6L31j%8;+%vC7s3F~Y288fgT;%mt)K-drp6xp7kH zhMGPh1HL%_rBNa^Uq_Nj+PcAcI4qI$;Pe|2%a%NBDej5n8pH z;}vyydzTdbHEC=;DR+-{T>NOWm#Y_+Hv(zi*xgo6`;lr2hed|29|2PXu(Dzm$_tWg zk0$Qczp#$XS1|(>&xYFTv4^gk(0tust{U@_^w153ivwO$lZe!7kKdU3p>Ol1Bgj#9 zXm&7vHRP-SyZ7jZVOj|nGqx;;f$Xv2%g?|f5tPVvnI1l$4=xExL1r#972lAGKjbsM zd-re$%K%0x_T?a~=7U9DXE6GN{j`o@JEN85(K&6qrkk5J?JyPfny0_=^g^u~N#>b--HxZ(Hnmp%$jsxJ!6x%D?%Ydt2m9;C|D0UP(6IokBno|#cw)RsCEes|1eSq;7U-9jBm z=<2kXn!ZF<3OcF9oqP8qqe^2d5PJy-uNXq=%>;vpOC{#%=IGqwgj`E|!x`wBBU)L88zwnpzK^}%5lrUzzNvGnclgL-(X2|Bn<}$7 zXvJklcS>z+e-*K*P0E=z6L4yX9Rih2EsVr$pyV~j!ism}A{{=o#Qh)o>pv*uO9SDw0ha%FTQy|!2Gj@hJDh;w6V1ttK_zXE`U$1K-6exE%AQm_{~JLUUf zn)vw*r7k>gluzZ_Ab-JbSgUoS@$Ci~!^IvaWL!LYd2Vo-tao|e-0X_|7@*E%{!AS^ z?>jD_wM7Ow6#CLKiFBu6&qC!h#a!7timpUZ1sI(xaK%$%t0_a8IN3Kbqj4u7zrF=7 zuk^jyCOb7#I|1E^_f#Pm(>rolin@E{6*Rj_2s69tv!fWUbG3V}(Vi3yM3xUkv-m(T zY`)BDo1_8=%4zSekP5=r3kVJ&8>5P-JL1x}9w%s~an`UzU(!C2q+x*M>vG?T-zq;u z#h1p6l$VKw^P`fD;RVO>L3aQmt|+6_6yqP1$)=W2aHkcer`|rslHnUcZQywE-I+d1 z8Ys99dgc3?W@cA^QPw^SPZc0=kKt&D1Z&++^+CMVPWt7O+@ne?z7^3*J&>(lk$tiC zBnkl`u^ft19pb6GbzZLT6?rXP=W}<-5_mC*+9y>iKL1* zOa6N#56UD0vLBQR3aSOUw22SZBd44P|w|#8TfhGVd@dKM8P{4))_i| zK42rps!8rYum92@Cq}D{DiBjuWt2h~_T>Y7BHt>0I2x0y8EemkU6*Z^t~WdubnwOW zFlrr5$!Bk7OEg~$a6M{hp-kBDBD$!&rSp1e*?PE=+2vudkB^UdMWhHPjzDYV-*RGa zKR-^Jm-*%RTMbE)04{z_=VAXi2X!vc3SB<$`dYx`m;syRI=NtVyZ(WLU)L_b;YnSz zVudq9$MSgY))I?WPGSwa-CHt0cr@<`yr$+r%?b+jm@mb?f#hW5?uR(^z{HM&LbYV9 zwyk59Y*?y)@bI3qg>(};LDW}XUamnO+ga)9=H`Y%kQg^Z=f3;MmI*{16bOKePM?5H z_Tdp6AWl%V79-&YmZ`G9_jBQ`gj?djno6PSZmmQ!mK@#&Q`mKc2-8V-Y>YNWE2k?2 zzC$AYyMf;<5rCSSW`Bfi2eyQIuV5~15(bId*=7Q(uiMBMwyf``HoXe;nNjbHNj+Cv zHYF}x84nU&AgIdfkue8~V;d0lI{U#3cN7>arv31aU>=Ur=AD9}^`|9~M zY^c~gggeiED^w0|S}mv?CXq#f&sRY?)e+eq6|jBy&T5tmT{7nkXkt?o#me=)H>U-} zD%kLeDsz#3#4?3Q3TB`Yf|C!yghAo+}2hbqmVk15e{s^7e?}GUFRdL)K1)}rMOYKdmnVDo~;XQSYp<0evq;^lu#=y4B zq=XUy0coVp`C*h7x}(f(bksZdc3TPAKs0JeE8XHMaHuYUtL9}Z=yl*+JEl6u{+YGENx=gslj9a!p0?f z`tw8Kn4k8lQ31ww2Ob=6s~*` z2Y_1x=K^zWm39CY9bst5*}9uLiLqc}!y#KW&-_FQoAw7hBM^PI>l}FU#j^58#mnNd zri9>_-qo*NI0So$)q0`hnlDVSv0P)q{*>^csU?=Hfkd_me0t$!RHKYVPgNN51HbU5;YNde#Ie2BdFyuE~yU$n$o z)CEXLNEk1m9?Yn_DQ#z1@^ikk#_mzZP&Stxx`D3}| zC^cjmT!c=#qEvh?XDr>vpt~seeb-=w@U^4GGYn#0aBGoq9>bWXN=@$Yh8fAp=3p%~ zdT*Sz>X8q&qv%b|57>HR2-~pI+h-wm*IQIN=nb5PH`1JBQPqeOH=Hhc`cG8|?}S|7 zI)(|x$ITxdkQdB{z`U&_^WuCoSJ6VJeJhOeG3v4xk!YgQ=?$R|7|RoWpX+p;o>{iO zM)%JT5-3Ornw^Hc>iYHqDy)^bwIV@V%MS;*Z`gj>KbG$TPg;9k8od%<9S$DD zO5yKjktM^Olc*gKeZmcUzM$AZ7?^X-*6csA_mTatba77TOH7&4rZ9Loe+`E=i*}&x z&1~0bK9{QKOJa&*(&D}#F++{y5WOSnVY*=5_3heRiK6-!%lFmQW7jOFe?S<3>Ge_5 zFDNww&)S3;3*Th0NmV2yq|;mnXMk8|*s1>Hf@3Lv-)K3yCVb`%HL2`V*5mD$;B+bA zI`i@cV}Bb`tsYAYqI9i!Sk$YVqQw3xVg{A-F>1BINgE59Alcj;?JNru^+GP*HD>uGQTPGVR| zg7$hOI$`_`~Z!C zD>fX8bt6OG`3FupuX|!mb`i|pYrHH6oP%Oq%+GhO?KMs~`Qnn9U3h7$yT~_&j+rBF za!11cC@~&MBVGcio&E=`PN1+dxm`ONvKrfKHj10ro&88L3gP1joi^0+#1SkYonoc} zYK8b!r&loTDfGpTn>}1c_??-f>e89Zo%|UlNFBvQLhI}ax@k( z*~hcj1Z$Rh;N#w4@<%+UUgH2$w{Eb}uDMn&K2tm^^!U)BX#=KMqL0N)s?ek851aWo z%T8Tve3!^q2=aN@JlZV2jH_*Bv>aJI(6IeHhp{m*VF3_A9OBD6cmtbZd^m7`@O8~^ zNeK4nTIJ_?50LEvo{L9CmkHWnQS`F>u@@?0FBI#O=6K@W%D%aAy`tlR2#V%iR9~Q| zV-34BY=$omT+-n!Exwl7gZq+u`SzA;e}jO^rM@_xJx`@oXL&vuHA;{#(s2$IYw;rW z5$!6yHQoIv<@n@@#vf7A-Fg-o%_98+T-@<9#RCRyZDM_@6XxkCWK&zzC>}K!SbJ_7 zZwfET>N#-|yY}FEaJOR%UTkdvcHVz*25NkAXzAn*NzOc!MN+Z@RN&Uz3N&`Sk_w0@ zZ{f-VS4Jt@*dv8nOZL?PV9&fq8V&)d@pj$i3P%CZGvZ{|hJL~r$8y}u4h7o;g)ons zOF5ljAaO_BkS&dR7X*|njjUV-o`^5E){K(htVCJx418GZD-?e*>M>%EtR8T*}>ult&F<|WfyKkWYam{X6T%o!OT%b~ZnvrXx; zm*b-@XKMJ_WG#Cp%iOd=ol}OiXAM1N9HI>To@1`0O`!@zoq_YyR_u_}UbLct0@e}Y zM{99DZ08@nQ(53*lE@YR82#<+a8$2QLrLCY2JASIiuO6b)`oad&S9x*^{6xGq8)lo zvZI{vrhIeW+^2ydJO<6u^Xrw0i$d6*mLp4pCVjAOS@b{FC|^)d2SVhl$y6N$!0Tl1 zG4D*uBfRpDUlR6W5uE`Qtku#O%WcBM%IM2NN_D-B^c?0?s4lRt6Q3R~!XXHfnVgwv z4!D@E^L%K!wI|_Y4E#GsQdft*vvSp~z1Z)~88N8w5 z_~QIomBZXP$>0!mp>t@cSd?qr#>i&INIpEBs);GHRu0DL;uGx@qvj#iX$KvjZ&Wlq zn^zP}nu#O*?TPX~^#P9rh}lf3kH7g;vIy?zesmps;JaeRp~$e=WBzfYdR(JLAjcYp zyG4Yat~R{eb@Um5CojPs2y={~8_`vjw>cr|07oQ`e3iEc(bH2%?~3o-#{mx9 zAvju}K~O1t$0T^*vCCv%#^?iRd)3PU+w^%*A$T-428YH$urP2uJ01|1$yu~f;q>zl zsGlMFe6`Gaof{sA?&T9ngP6F|_%?rL+V|g|p>hqSTjtHVF$3 z69y1Ah0c99t0|x*q537-Qyg@{C~;|zl^f84?MI zO?c9%aCTyBz`{a7`&dd2t4atNx7Z)JwIhB2;MpA83-IfK6(v9A3B;7C4hDX#W^|dZ z-Qh$QRjEYpXgJB5dBi?64`nJ)i#|{3B$QYvTZXs1X)36%cf=L}O0)P;4OvVGLZ||H z9ml|X1jH8^=vIeBdK4Pq{CrkWh=sZ7OGm?@UDTUHHRHgZtHJ`*h`ghSG@=lEtH5iD zb&+&3d(CT;C5|K8m_!(d*jRPaDe>4L^dl@P!xGBEq*h9c z<5@it(dFlGNqpd6YpYuw{hBqQM+2zRkI{Yx#}S*fDGn$&lsr0UpZq$Q z>QXnIdU0Q0b8hqKZFOI@nIS2beJZO$Zps|`go74D6J-2?5BsB?cxyOuSAtHqrs<7J zbv{W{9Lb}8vktCjEZLlQac$Y0uezB2iW1SeVLrZA9n?}Vg--7?eXEQqs^aSEniY2N z;K5BUD&}pq3{!exDE*w=reWoYMShg;kkQ3gXrsfa%u#qo&pz8Rp8NKOUoqv z!``dt-kx}RyB=~6bY`d<`nEk$sQf3yb#_LfPiAle4ddQGyC$pNs``2@!x)^WP=_Ym z(SW3&SFk=eiph*dYmfJcs+WGAC$naN>SpZ2C@1#vUzekpuLYoF2XY-@Gs=5f(zF^u z68Z00vv=uFo_s|)I4+FN(78K0lI{uyG<7#DV+&toWobFazlKpM?}c7<(|e@E<72A5 z(Q(^4*Cbh^=tiS=*re>YQ<2zf$~PI2u_R-0yQ$7z|1#uYny5WD=OQ#JAJ5L*conEd zDs%RDv4zxBO~PSXy82Hw5At8XhLG64g2aZ31En@2DAiyuZ0O&3D9qR*i;JDz2z4?P zvvSPNK1I5~ZOWRZP1UQh+NHMde8UvZ>}g_I4$S@IbYa>72P{l%sqUgyDt9QNQ|Qi% zM`-MDmhnWkrB()1nX4t9?lY;_Il+fb2to5*Ycc^7qHhM`$z*uiRD$ZNWb?baSg~Xp zG|J;IsYG8|?_02~G21Z%X%2ewKF3a%8^Gk#vx0NYx&80|XqtQ_O+B%VB;!c8pZTJH ze^kdkjQ?^xWyQx4i-w|@TQPd|HR)^1b4(IaBc3=#e)79yvbTLZxjS86Qs69(Dd+gx zsydC6LSE~B>hb%1hFcRD`94VD1N>^A(W#SHsHLQ>X+eg zb)&mS*eu{-GRk_5m)(G9LvJRiHsB)BTSMm4h7yB0HXAg)b1r@hrL1`0jj#i)NoZXv zAqB1cNC@<;jL>Vx-jH6XddU45Cf0{K`?3R&gwNeQi|;jWtb@BZzF~Abg_xc;EJb*nBqF#y1ZvFib|2tlmm(K z)HMz63Hh>_>9{YDV?yM#Zl*?~S^%&N(Eo8ZIkflN(2(`P zFE}QlCah2ri-3jpz2SsCjSU9R2a9Q@`D2kgI}!2n2lGexuBiwF@% zogjA5r%NW_Mj)|kV?RzeDm|=F{Q2!oQMzwKn3r5F6F3@N_3+SK%dYtJy2}2mQ9%ky z*CuCwe6Z)d#kj99)yFJBULTbadJoV?&6QbEA%T9D1WLm;pY_iRt6P_R(RJ)gLvv#a z7-w&}Rh<3fIDW^te|CN3Az*@X0~Kd%T*^!MW?l7E@wP9M2ycH0-sz(x(s1wP(6-RW zwjxMN#aUk{l>QW?CjD(>?h{GU9wnS-s~mM0x1Qr|aPdLL?CXJbx=_ZrtT zq_}S3ETgSB;T5l)9vGD@_u`|9^~@w)%hY9lm4||52o2w`gmQ5b!+Y;RdHpGg&w)4_ zEE3cau!pwalN0%Y_63*h+I(3$3If3e`#O1?Czq{aWM3IZQ;kw1AOBTvqGxm9o(Y7J zuKAA{3#EphMQ#+2R|360x;&-p|6CYw{_x!%U4MY>PQ}LpFG3~KIClGu5e*ZQ6MCaj z4i{FE!2M9T>>cW6ieFBw%lm(1eFr?%`~QEGhDyqcLenggy+_%Oea^`)vR7sAXedHU zLPoZOW6#q;gzS09PBt0g*qr}M_tyRX9{Bkz=|4!*$U!4u=Xel@`>cw^*Ft^0m^_4Ujxw8VS^jg_ZsIN$Kzy7|yUx2*MJAP+hQ z*SaXhA3wRW5Ri!nEvG^BFW#@Ka6SYT0ju5{?i^UDxCi4Zaz;i~=(GpG1O?a*{rOj^ zjO}jJBXAojWGoH<7fE9#i>rI&%1ec1B`!5u|68Mc|M7!d?$qAiCcpdiZ;3*!#NuCk zVla<+eMi5vk3M)wPi9VgfB$Mx;2LMdMxt1$-C9bD;y(Kxc~O_cNg8o`C5x-AGB%ZN zF6HDjTloLJ0^CnWxMdDsyCXS$_0EB*zf%DvYX3LIkA}Z3$EEt!qFRbF9USDhD9!H) z-$;u`AZ;Sj`AVs}MpAyhZ`E)K+b4#7#bimX1eH7dXAIOxnAp|YNP!x(71$ng=VfJO z;mH?decTfkT&mc||39BafK2Y;a|PA9-|s4yE?yFD&6kJCxitnNUA{lHuM5vhMHVc~ zKHUf{;7Upg5JFn_D+Er-^haSt7nUz46oqhJguMudIV1Eu&!})^KZS)WP%T&jTmnn)!pjul1Wnpm*T3z;lYjWNdsQ|C2~_ zDP4x}!I+g$zw;pd1s^Li#N`=Pf#luE@O?O5k}hoj&MHs3aGYsDBMol-*VTg7f3nz`4%f0Ff6uXVq9KNId? z5su>`wVyu>Qeafie&x!Q4lFbny5-&*(13xjY9VwbP`R~$CM4-9ESF8lxxQHzyq8;b zj`6_hQ-7y5=adh?<2b_qk22-vWtrbA!-_npmB?vJGJ+(-11-|DEcH6qvYS*`FVnr% zofbLCj8(Urbu&JqX_lezN)C6hn}-sJAi(c7eSf zmo?kLONggg+T97hn9KQSipxl@vuQH%uhWG~#k9~Vb`J!HJTAAr4bLDmImC5H`^OQx zp--hi9e}T65HLqMPrkECvR9bb2b@~*Fb>d?R6Jdc)pp~5wF{?Jku5;MsdH@R|Ghql z`@;6su_eMAyZgoRKi!7^&686)JWd3X??&p8-MLSvsCV2c4?Io1A#5L=&h27lkSLNc zmojMFZ}j%05;fVA^m4Fn*y|Z`EBCx}K#T+^49pS>y}S`-@a7P&s=&=fVq@$LOdt9y z{3^I&xI{&o>S@n0rPYao$VyU_ujf@3q?hQCLtv%Yyg!EvQ}UJjK*sS%T6% zGK#UyZ{DQImS=$^*CF0@OY!6KWR00Cy&J=dJ6&1!XKHA(5H*Lhu2UR1h5Qbe2te5m zfZ;-bIdE-5_6mljuNQ$qmj>?_PE}x;K}4dwJSBkc-ysz@jb9lN11GQttZn$;P{DUV zIo28@9ubAA)2F^iPd4$N81&^BEb=!WK9K!ag`Psh+oC6@&4k>Ir ziJNgsMZC}))jy;ybFThCRaKR@j}PF8-eX^1!4MSaTbFy=(jDX5+S*>eeA!jZ^RJ#( zCXe!($nQqz0VN#d>OQr9rDQCZ{F82FROfp}{NSJ2hS*>QO2*(m=~ z;$(v8Z!f7|kU89@tbU;LzXSAH_TCXuG+ZxAs_^UJrNS?KI(2*PNgPwvjG|XoUTxu^ zIY9^hI0FL%^&hI%zkBSpG4vxH(b7{F^L+m-u5+&rU?x;-iJ9whb2^?VAG@PL%X0-A zRT{X|9Xq$0BFnyiRCq5NC~p1Et@@{z&fEKJDBpv70za0sWT-u5)TQ!=b0%bJmF;#n zoBYb{pWI);}#AVQl6+x z(EHbp;`gU4B7ggP?S0UCNFC1~OE>YS^f{b#*MBV13mF}_=-MX%ub!Vf*XDZ8MT+#j z8QQ{d-8Q!WyWc=tcIf6qbFtEW=VjQXW!3&mZ9bSupXq}lyqo0@!CgW$|5?9ZO0L|#>C42MyQQJRUHi|YJ@>zx zTc(ne;*{8dsTrn;5xSWeK7kuWUXc%m8b1X8{!lNO4qPAaI)fV;BC;*~m|+@p6j25Tii+U zt$It}y~o@{VQ56G#TL8a>jNtYUY1=JO<^fu`zx`J{K|h5+TYqD_(8j3AGew;`4JlO zW5fpPsPoZMOzN`SmYCmP$bI^@Gv!08Z<|l{{Fwz1Cs-$0M1H4O?fvclKdk7Uy8rsq z-|tW7drrah2PcmvuIChcntc`yGWTK%U z?{SJ&o0&o6VmsF(&;33$5)MzHW!8Dg-NJ58$Eg3$v-xMWS3W=R{?utV&`SOe7m z)!`*wBTI>vqxf->e5K{)Ht&;Imy|sz;-#F28Mg0zIWOHybq0rnbdz&GSBYH}UrVsm zA4(eT8)`%R@4r{)zbDqi<)dHt4xF9|_dg`{f%KED`55C~efjdCFXt5c5#^qv$GHje zk3B7QpME{+KK0?y+>3nUz2Brbd`bPl(5iYN0#c>F%Z zE~Utvt~htnWk2;TSLe_P-$cMr z;NAff^P4jrh%W?*70pt#@iYPNWrFL<9pw&Tl0+w~xM;K%w4X4Em-##y=h%r`v%RSB zqDQZ-w2jeYr5!UrKi|$eH8Fw04phUxHQ$k3_w^MGfYF`c#CBZ2eET96i*>E@^zg8K z+A7ic1p`h^P)qrAJwnnTKkBAmw}}*Y8%kr6_)dgB2(=VwDm;oEvS(<)S#z4~kaBxt zHdxuj|$D^Mq=-#9mxjZZf_|2#J+sZQ&qbywPRh*Hs{=73;m7>z(w zc0__Aq>sbQyTa5TYE0D^;VF4iHOMmOUf^l>CWE>OfT{{)#%lG`5b!j%NXh`LEjVq9 zct#t)Ndt&55dJe-VOSEm=L&K9x1ZN(Pc^ zG3icX(b+xV9-nQ2;?gfDvABhEHOf#pkB66oIZQH`D58?>KfiSvZ%Cvftq!UgPR;nFm3IGfj_5V$ume`vESdosVbA*UG*dwf=_tN5z)PD$$K?Dk;)|3)Exhd zZM@{gU;CafH_vsSue`RlwT;f;V+9#a2eu~IX_)ep8@99Qf^i@L<%cl3|!SxMzz{M4tHh7@EoBBUG7^1Cok<*fH z`WA6k_~)=+T9w(WdBm#9+A~X{@Aj`PD5dqLn}yl0>I=9LFo5@Nf@t>__)vR~y;7V@ ziIVp72TMB!Zqx+<0q3pNEFhiizU4Fn4(n2jcz(sYDFLFHJ5yWZ*9QPvG;@m=S{|$K zuOPjI$~YrZAyU-o$JbZ;+0{;`%en}`lEU7LVFFwd5rTN&bhLuT*-6z*C>+f4pqG~x z@RMH`cxQ7~2dI>E<|`{|USM-Ki1Tx?lYv>l9E6CtOt#Er8B}yLo)vZ|QK!7ZiLN9F zZc95j1-s*u29RIeCO9|NFRh2)F=D}DN6@M&-KKw-z%MB&svNgX3p1~|h21Jl<@>0FrhrB{PNR&$KF z(MSdj`>yu4h3>3q802n%RWUR{IhL*JCr*R~vSQOL>Z4(l@zDyri!Z-%V`>kB;&%>u zHGNpt!Jt#_FI|oR4ki#9-h}$>ZWO_9>wLb{>pHdg000mmw|{&;yh)5>70FrHbQylQ zj|8nQX{B=woG?jq#e=XrVgDw9&US)>8|?@?WBHdT44HL9Ab=@`XD z!{x{j7@=fJU})*-i@Y}!#ZV*28m6ohg;}dictmt4fJ@q~tbU)?VfqFwjRNQge}G8n z_>m)#!j2OY&9hjOH9_JSbk2t4oIlVC4@R!>kgw6){$~Pj172x|+b$nJU)gov2`pu3o)wWk#yx-*VHdE^1I+I`BcZay#!@DSuO-=s!2k z_igf~YvD(}h#fdRvAW<^lXU&)yWj9MwI7);;>++$7HaqS16A__hrbp5@Yi4RVE|$A zK(U^jU>d9)=jo2XCrLEQRo3FEy)6g3Od-gfSK43SkQNR zuqHk}*%)f%=H~2iF&#i9VR{=tU;_?tiudlep3XCY3b5OUalnN^bA$9!&QqCMHa)(Z zuM}AdUBltY_gY5VEW?QqC*~qY@j6jv_2TvsSA?XN%m??UXgF`PLv3_VHf*AtQg zZ-3dMY}>11p1s;v!Djm9CX6TBq`64DX!FYAxXL@+xBW}Xb`f`R!k-%_!a8<-)j0wv z{sK7(k{`7S-$!&(&Mq!jby5bFTZ_RVxJi}QC132mlatP*5pI=*&vg4epAIz28K+;T zTIy%`BQ;MXtAqn1z-+kuQJ3}{48nEtEv^y_>{Z_BKBBVkckOzH$u!=%A07_f8Nl}R zgj?zNMiO|-*E_y{c6@P07{Oe=C}%+PK9}jH$=COMojMwVZ3ThH7$qGBR(g!jV6^fq zTN!t>a{Ih^e{c)XZSX6Gb7=JeaHCc1STSg4lh6Q-o7dP^*|{vukzvQX5EG9M(eBRd zeOo8Z48En^Stc2AsH{GiXrd{I;Off|Lq);qw=sT*@a5VxljaLOOQ*4~N5ryTLs-&N zdTdv|rC-ebeqU$0g9{aAxgjMKvYBElYfv~{MT$aoxBBE$vh0#sHRmqEHQ{^`!sp@$3m@}aTO0osUO&WTP)q( z3AAz0$EBneE1n4RW2F!ZoRTA$pFqNMwC!Up)-qb86GcUjK8a82E0#%|S&NW8xgpoW9 zmIzzVr(!)ZTqo{AtfqZ&BY4x<&uycjUVg2ifM{ zzB{FN?&qdFiFlO8<0Wk;p5wslyPbrS#X~0Z8&2%3HB`ueB(t9akSZ1+7 z_0#;$KI9IY?Wq}?A_nbD6!H?1k~`bmD|(-#B#@fye!BrFA@zb}H4n;MeUNdLK=!*8 zLU=Akg({}3oVsD(`7^f;gTxxnwJTRL*(707u)tAZgYzeUs3{JaBOHis@0u?6T{N@3w1NO0q##c(TLMeOI!)=dv8 z5#Nj|mrdwoY6RRkPKL@wLTluTJAt9mxxArRUt;Yl6bfGhC*%+qJ}QMn_?WG9j%dO?0ZND5j`k z&-mlmt<)RYh?-Hsn@|=O+89~)oq01hF+tz&cESi9R3WvZDQYAXrCshl5@Al-y#d%Y z`(0a2{PSsMHGdAH;vn7WADmhVZ--bwg^=NPJ0p_n`G^5_YK2L+laWO zb~=|p?bVqYhL(nk2i2T{Fu`Od$*wK5ybOBuXBI#*5>yFdDYR+_>ajSe9X_XNJpu#Y zB=4G_utHVVP%x2EePRXQ&IVD`{r2t3FlmR*g1r~>TzLsXK+qv{4h{lThH6L@m3WGy z&0bnjJ9FxkH+T>`J^R7PWmv(IsB=4Ny4uS2x~!~!w2;q9dU{me-N=tO6uCJP!iorY zykC35mtJ3Ji>9+UBjNtG%FL8BmAFPn7UWSJ#6GwghWkMeyR?SQ}{`tM>5a7HQG=)4snM`L00hW95g8l$m!6WRCV!H3CCJePZR6Wrko zunNm{OefBMOU-*du}2Y(tPNV5+N2$Y%qU^N6`TF~3Vz{ScV_12ccH_+Y!?+d(8P{m zh_E(`h^C7ixPx9-B{)PFp9Ew3m0zOpunn6P(Oc1u>fOonBO@QJ-Vh!M0U7Ee$6I4_ z@}BGAD+%C2nu8Utp~!m2b(4SYnQs|4Rk7Dvs9t^w5Sg9FzLEh5qQUEp--dZGwmMPS zlOOg-7d%2Chi%iog8O2wU1|QF{zsMW#i-@?-^@U$x zmf98^FDn8XwKLwlnTbWU`xdkV{7xdi+u)VJ@6u~E0^v*GpZY9i=L=0Cp*E?O&$p2v zZ=Bv?=J{v`zFOi$rd#QA8b$lwJWdXdH0X%4geTo&uN!@X6umaVl%8x~@fVCVzO}>N zP931%kEhYiHon1cKYricx^8TYr(!t!m~e4LT)txuHrTWhR*&~&Os{A4aL%19i)5== zXtV*K2bp8)#j!&wzVic&p7m2n;An1B_BA$w;Qz1ts&?AGPW2k{5#jwUr4q%N9ug{& z6MDP%!xHEWJ5!uml&^FD+12~}MUGB6$v9q`9fr+8Ij)DhgivIk3N220AP{DvYA|*!0RZAQtRZ@`NrP9+YW*r$mO-zt zEg;WY2$&iOQzl^I!6;yL7fNP2C+(77JWygf^BHmyQSZ(JkvHDdhqW2H0EKPcnPC(7lgeHouW+cfRax z1bLlgHi?Gjmzzy?(iA>w*0?-Qzg6}5kzbns3|*Uk=~5>gY*N6!gqRl)x$5_&D47@* z9sgmd9>L?&{>+hskc|EI+E^#5HCdJ69WM!T!k$AXSmcRPGrGdAzMXtj=6i}Uy6W}i z1?&2trd|qO!nqFX2rlBwz`#^v#UOXij+7Bv_1)R*M9&fKnbp~B>6uEa*Tyx#TUuS2 zrYW-DQg~(UgfuMozTX@*E;cg~$er#)XZPmFjFpI}Z3#ej%;LVA;}MJgFr+i>2O}B= z;tX(fwm*3|+9U@(o>qfQy9b{G^rCW%b&5g~x#Gf_af)#9f(3uP-n9AFa%=zPbcgWS zzHTOfk4%SNx_suQOg=pchUmjyj^(CWo;n=^w8G&}JG!$*9AAqGp&U!c8>KUEfGpLZ z2D6{MszPBMCJU4gIS)cIt9aSz0yJlAs@j4yWmmc-9uK%s%Q z)eP!a6=!2dfAS=oF}Fe`3A2WWI}>dT``_}K_(Lu&FfXUUg)Hbrlu_9d@!bQ+A2mAo z8qKZ((;f~r_tl?~1EtGQ@U#?M`s<_k!LX9?#=}^1TZ?%?@!85Xeix?b+_^=yY_{kMuCZe? z##X+cHe{`fma}v6c}D7Y#Lb0bpvfaZxI?%2NsXWBcFB7SZoETiSRk#k8p4&3))6b| z88*<&eUxCEhJv;f=LI6dI2G49Vta*DH!l*Swvw~?coUSMpFhjG1|B+e$mEl|Y&f4^ z*?gpBlv9$Rl4xaoTP$LdpEPt%io~{;oVPn}SMK7uJ`L&Jp585K# zSV-WL0$yAlwxe%lZEd*Ze%8C>Yjy*Aww!qjYlJDfZf@Z!pg^(Q~6ZilnH_YuJ6a z>>~#KqcC!)nF{w`iwm}e{b#D1Pn|R&rprWB1-S-#Z_T0M^8`@?Q?}Isoy5z76T06I z)oO;slgzeEqZ2`|zV_X@Ub~>#gWl^v>TQNag25{JXL~Vje*t&yuv2D~gPQB_m&|*4qHyIXMl`5paLBS8z$k%L%6=NWv#3Yue}fF zdYp1NvH>cKkGp^iU!kUuMQR2S46vdMp{YZ|!;n99ai;lFr`q!KYR=^qHOQ|<62f>K zxzkb*Z&qR*@v1Oql#589_1x(RBKZS?wI?Qh)G4Ti|WSTz@ z3*qITHAlwIa4(NKKHA_Z{m%8GGb6Fm+a@DLuCe2BUk8>E3+%DQT%82_)T#K_$Q!>& zNXVSZ=4_4A|K3^mmFMfHhbdZob3C=It z0^GAZyJNR>Z@jMuP2Oe9L095tn<1-qKby~&%)|bsr@*&rpkP|nC)1Jnf%uQ!I$#Ou z3AWQ_UVfiwX8^uy;0OYlr_-Q%SzqZq;X1cTgrEq07bvifofY}yI+toj^ml>H+g~`F za094|30lBhjR4IZo#}f@`R$N;v%1|E1v|e+6)$F?$ASC1|3YlCPOl1Sg`TA%mn$p4 z^gLIz5Z4z`W^sDum&%qikV!q0fBUms!|1!FfhLJL&o=o7yW65j`YWe*(W1zl*Q3FW zeRQn>#!s2h?8Q!#^Dt@GIp0AA)PKMrK30B%%l_;c!!p|4x+_j&IV9gfrBoj3! zw+tc(`8+nnsTiz7HesuDVw)oKxrz~#16=xM28l~q2EyUo-q`mUlqx}?US3`q_g1Vf zp(E5IpSBIHU&6VaIB^0Z&8y-9fWjCyg}aKxykIiKxe~V74+EdsbnBhh_BtQ2vtW8T zOk&AOU;+C})LcH8URZM2Eo373{?A*N)}xZP!{OuJ;(W^9W5en*^1ixX7Yv7*t9xG= z)74!V;`sFG6V0MZsPEIC^5=e0oc?FM2p$zdAHHUmA#2Sod~L{ot7ve?ba!rCXQzDg z+`98RB4Vafax2fCR#T8gp8IZGiE(Mef4=hm?uRe6e(X+dx&7>wy|3uGy>$7HWtQiZh=(w7V7?x93AViy%Nuisr?Yve~v zhI&A@3b0GpKuoyj)J@O3CGWLi%EGq ziC>_O7PQd1B|jnHdGDU)?f9KeU5v2u`9PpQV+}W^law8s;z%iBt(@}AAn`O#Ryp4J zy}YzQPd!*RqjBNG^6Fg2ye@fuVs}N0(?b?Jl3*u|iL9OOA3x zIKQBos=Rf%PI0AaBn1+yM4s*gT;X<0RAlOOr;u>Oij-!_y>^He1YTmHAL--F9hmQV+DC~R`1Lf;Ov0{1 z^>RIaI>c`d1+VlG&eJD*@h~_6=6J7pv~5+NxdDxjDBA*yXJD6pSD5Gm*Uj+jieCg5 z`qh}DSXQD6TSAfdyc;33mkzQXdbH;H-57-(`k+9@7;RbCS)shu^(LA|7HZF*Kfgxc zKK)DKWPgO~F%9gUQbz~!>Z0bROj@?E6--X|`01kJxyS#~iCTCokFiqwSCB?#k7W=& zKb2ZO(T}Rf2d6slO!--kH$60penhoBMXH`LSU8Rp(Zs!_r4SfCJX7qFPU=p^Xf0tI z*K&gW|KkQIku7_qrCSeiIcQ(;Hseqni0BlqW6DfgG(CdZZAf?M6NK`-UW}YRKgXQ zJI2OZBcDlu8pGhYw|CVEt&#Hjy2&n3jwTykZSFaN`TqW=O%uQ0sr9|y=DhnCR`%s) zNTeZ`ukFR+M^i$P$r0!=Zk6b6Rj)*)RrK2^Mn<0{Y@XS5Vr*<|D9#jVws~LTlE@F- z9sH-NI69{RuVE%zQ!S$3g7Gu4+^F(uN6XwM(o#RX6jSI70M3Axp)3+aMRPprj%~NIh(o!?_ z6e5NE0s^`oNt~GciEkxZO+%8Ib(D_+epWbr!?xz+Eb2uzwmL-2rHCRhFt3@14hH1Z z00z|^IC>gIv0M@2KN5aF&~vWmc+F@Ad zIBO+OhL5xQ_)aB={9xQbc7S3#r3%-}AG|Ur{xB6}u2W8RHoH0>5GO|b^{Yk(7t z!l?t%q3^}ro+coG|EpUv-(y1BvGw4K?0_zgpq7hOQq)bBY5{Wkg5|@m4nEGq;KU^v z6AIo@pt)Xpbb>z182=M66vuBl-QJ8TqvwxkJ3+XrPy7dfY3f+BnSBC5s^KyAYO$i( zqD6ltBn!X!(5p2zrVrd7QkS+;pyA{?m-J>G`Y?Wte&BF%(x=^0P1imlxn=ieYc>9$ z)JfTfpS>4Z`JVD?NwZBaX`Z*G9UirrX?|v7aXCgM!;Y@0SV}*-h@vyhK7=!R(`nMX z$--`Vw#Nq{)3q|{?hY=*y4Kxka^BjR@6J+xudweIhF+gV*a0%vc!zLq5z)~Gc+XZL z+wP90&!19Yb_|o13ckFar4+=hH;lh)`Ra*c9U030m7F@YQ#HxdUT}hnxq6R7PbBZ> zUiyFzoQ@N3k5xT!qAl&R+cRtc$}7j?^hWVbv&3Pq)yy!ay{Khg8M>dZCHNI3+APou zC6Sj^yHA&Ut#29&`mwZ1oB;?9IZE^ELYX7~@U8N3j32%XsDQBf7r>NkWzcPM`ugEx z$F88~M~3hwoyPsN#X1%|7WnSBc3w_bqTgxZ1=R5O?@0*#vT4e6Mo!Ug-qGDEO~a z-jmovDl^G9)tZ@1#;N0~i!Gd24c0=DkqwmkQS@o%Ho4s$c8w@=QwHz_N)CGjj#&n& zEuHOQbk)10A%bstESqx7B$L)n^{3^ZtKSo<^f}r!SmmK<^S*iCrJQ`LQW$m*%V+QdeI3Mgluq1O)@^Rv`m~q z5uqgRFbJBUo{kWkXhh4fD>cK#o1n?g=e>4qvP|>iB5z|z$|S#K73(VU)4(U+L>tD8 zVndwzio#lti6YSyJee@Ji8Ie;@I0v0J&&~@PMgd$W1T+p*B`I(^68jGi8kS4h3{9+%dC+n zAM^O`&x$zTA+fHs?pKa6n%@ZGMpH_ankYZHtATZ}&XY!Ev@P>S%!`+I6lh?0ZmoSS zSyQPHE4m%r604WTzP0lHH*0b42kOX&G=%A89a|wTZVEeN@6nJHvi_?5C%Un3joThn zF%){_(x=_MT32L;WRo3P8V?@!BZVCF{PA2YOcI5ls7opZ?8bh5@qVjRf7ktNv*v}0 z1(Z1qLoe(&8{_qe4i4557t6<*CFVXRs%b|9LU+v^XBc-ggAG?bYHra68I-gf)GXB6 zXK-UK6}%4wj?)S+Ev~nsVOQ(*-50^NjFy)A?lAfJkpPWx0rEi^X;)Gx!_pu8iFKmS z2>V+9kmTZq5vgZeyjD1uB%6S}7+Z}S&P-Ne{sSFhjsS(avjYE_7lI>x<3FrP1?20O-ILmGC2^%vu+yn(8U}9gIm!BV<4fG};CX*}Bd%bE3L08ks8W%e zYpjOZ#?^`}9tG+#n;?PAHrkqo<_lB&p4});dUqYKo{Z(_pA4d%hnZjMjN8xk4Va^# z0w)r*<8#$x79Cv$iDKa(BduWg6}YArV&uC#ar_7^=hB*=9R|-dJ09VcP6B71vq2>i zx{QGZK3i^-E>m4%| zFPBd0a|ys@V`-Dn+I(z0T~)m@F~cPo5*%elT*0l#Kc#fwZDjNBWxt1GCK3{w#Cwy(7TX09a-&}_S~*`HvBb3pWNIYoZ# zDCIR(7sQ!C6Fs-(-7TD9RRB4{i(8}Cl5APBe{+N{xP##cDgN`qK)TbJh?sTv7L(xA zn4KSq+|rImN}W2JtN9&wCh0B~Sj^f{O#E@jI4lzOkSLp)l9Z?YE#?fFfizw)aha%v znD-VDyWPL^UE1x5u{Z<$RJM@%djUR##+xo(!8-_pH$A5csIMy4N9Ei1V$^=fDJgx$ zEwuFKTL#ubKIu%Q|3x-+Z*b542uLyDp;swd!MHDF>MQs(`|f(+`UZs4tKnJ8XjMXJ zG8K!23NzPANAbcrAC^8CPQVO{RpRt6J|cyc;vC(+#t)*6Id{wi-kGYxC_`sJN%mxC zB@byN&>k}Q(APBD-|j(>E9O4W2O>ft@ef6m5DorAo5iUUH;c)AM1Dxq} zQ!tw-d)EfI`g0mfs?*mJpj%`5m(N%(N6uw1J)13@dFxlM8W07};oB40!dVlmrIWVmTcOZl zMEdSk$J}yzlv<#<)aOHk3r63WX-}0B#keq=AowG#_B)JB$-P$F4JYB2;PEq3!lqx`jSfQgN+$t`k1^nxkS^76Ng1vsuZon~lRIHBLwKnm;WnR% zwUk%9Aj!oR4l`rY*04B04_8o`Uw{A%k{Y1)vBsnq*^7I+yMqgp#lnyg-!yxaP_F(` zoF7XEw^C1O0-~%8QrJH-q+{PTglBM`vJUpxSh|cq<$~n6Dd4$s$sa0BqmX0_$J+}R zAb49ON>2|*d!rc~K!4ovq|szAt0Fx50BY0h^W{6P`Go5oRRCgxdOO{%@fmvOw2^Ai zdCpKG0Jid^gwO(EoJvdt2vXx(vH={xyBhnrJT!hNXe*WOi6{0JhB@n?)nUf`*bW;A zwn#^G7R*l0GG+C6)xS>WH58egowZfyEq2WA2D)=f1g!mF9j7yroR#rZ&W68i4uYJ5x=RDVEz#PgdedD~b3$cbFgLXh$b`MrbCHj#J(%fD{MLUyJ zvD&9;wCpK7-<6&LO?0GfJj|g9Qbn9mCXDUQE)e3d%*rRR>uz7Ks;nHLn5FM>(YWO( zukk19<`GZpE)3CmbA|CRnL^?ho`2y=C8oXmc?h??=4U&hb$xq62vdV&*x0f)m^Tu& zEmR(wBwdg2DY!Swf}NkIBr?Ye+6|w{UeDatop<{?3m|p4nHXa%4~bG z_t>hd{eG;9or;sNFhrcKDx^Gl3`?xw65uhGsJL5?+kCO!64MVn!mhe;R58~93JOTY z0NVjST^VkJqO(wQqVmRuPaNd*T?NJkcU4k5uzqw?)b!*HVM+^FMH@J}5Bd4|8!f8g zdoTO+mAlS01T*ludwGG9D-n3Lr0LWJs52%;omA>D277)OKeVsN&M~_U7AyFdS$EcQ zo&&1F<>RT}QBZ9T4oZ@fZ-bK8WA&%?-fYSf=c}7<+1di1-|kxuikmK&%>*h?d4_;T zkHXH1CT+S6$E1p6%=w$1prY!q%WCjile>%a-U!_M*^hC)!f{~_hwXf8XvFN+*TZo^ z+`5B#{H;+5g724RCU^Vbw21@6!3V29i@vwKk$L7kfIy;u~7Z!bx<bDl*jee&spMY!z)vod0;4dyE9=FU6rl zd(7blFW^N~I8O)A3px%(myu`1g5RGaJ^-Hi==eA?N$Eabxvbs&Lx8Hz$1Qyi;0n%`7j zcWR0cmk*rww#yz_!<-x+X1L#qmYBQf(sgBQz~o~OFDCvi)s_^t+}{wleN1_yQ9rs{=1VxJpId_89Sq18JGVmzmy#OY5B{ggbSiGY_zQ& z7(O!!;kTPHYX-2|cQiy6*iZz^ls9a6K8vQ86h|$5c8Cf-K{mqGVN#wZ&9usS8O1`6 z9z7z;=F90o$d0Cz>(6V%q6g!tn`wPEW|W&ZjrpVn-;23q%H0HPzDO#Jii!$MW10*c z5q$P`PBr_%DS{7pKw#Je$Mz498W#;_D1{}wNK`nk>!=@~(4*MDd0IcNwofP+lXpf9Fmk;?X1tTXk#!z+Hh2^nWa(bf$GyAeFt7;ZnCVVeLg3S zgnp#-Mhb~ubYB{4k4g>DZg2?&V%WvoC+E)w1j79*Unu9d|Z%4kB!P5ba!5 zSlu4>0cORZ{`jq{;B})KsK;RX?$mdOHhe2J* z{c2~)47=CHvT~s-a+J{jp76AM8E$PiPWdiF!?ybhAU2D|OvlB5I#)atl^`G4^K9;xC&ixN!nukSl~92zqk!WO&+uEIIx(A@e$Fc8TwH} z&6lJfjLA?)$=k0voL!D^9Z<(sR17l(nlL_y6?1vPb1(#(t`L>E0s3}zYv6RTdgo|X zaLh49V+$1eu`4zm^@BO*T}?dgdibON8CYL>wl7BF`(9oDY0~TbRArbLBg>P zDb~JaWp%5|&$Y@%;Wy&AN5z+^L{(C*_oDjVWgS#U=EQuo?~O{bPo{YXBw!wp`>bu0 zk4o&{KUeW#I5)&*C4CBp+dJFyn6n7VqeperltQwEHVp*@x};pq=a90jKZi=43@GaE zdUl@Dy|;&JTt(FN3F(B)SqO~3FMg3iBMML|(o)v;1&|wXv`0$Ze+5WQxeeGFK&N^) zFp(qJ{*hG!pS3j$F}Q$c8J_~T#ZH`yzCAhl?(Q-*3XwzsWu7i)m~*yK^4TBvxLn>Q zsI;5-y6%r`-iq~!#;MBN7YI?l{VCft!9f%KgtZ#MTdmv>89x? z*=OfHve1oM(ki`^A5<`H9uSJ!uk&>{Y4FDWRF6qcDXiL4tPN%U!k~ zr8?;jvm;DO?mkC9)jf@uNt712Z8I;17?9}f6Dn%ZW5qhK>D*sB#cW(~qhf_3*uabP zUs>fo^5^U?wF4|)Q6^2*E0$g0I{D1Fva!)?pu{@6TEL#Og|CD`*rw zDE5PbJ{YM1OZF7{;n05~QWhmLc1p6R#ue^cozK}Qr?g58Ym2J$@VVEwz7X{e@u;t< zG4wbyx&#EQI#w*#c+aoX}f+oXNt?Ypf{%}u`1^%nt>f0u(TrO(S6;9|L z@>Z7y?fKlFgxF17?F4D5+xAs@(?yTjirYGq+KZ*3GyICg)PMT#qfS!0YELgD3p&4B zLI$~b;(vaRbYO`snxmt=GURbPvWG3Q-fBclLSxY4M3`5x?gAea?Rz#M4x*at(hBh3BVGTeFm}#VwG-FnZW?y%pzxb0&FT zv`RX}rjV>SVQ2lRGu^U+zb?E^h%+tahGAz(#Lo?dzOx<fywW941(ImrG>{{ya zSb8#o_-f{f!CHxnGpSx154lRDf$!@ z=J4FR9&|i*dOmjTNf{NsUTaPF#}Bcs;d1>V$ODs2m}kSqE115pCpVg`tmf{AFQBZ1 z7>twhGa~Iom<6-NZg@Z6Y%)%EjZKZ!`J7}ICWWmcm{f=^*93}<#gdFFoY-6K4a+Sx z`|*!Vj+J>Jyp;bSrF-bYcRTWW!8ovlcyx%g{{7JtsAZ&lx1WK(sYbMr@i_bpQB#*n zE$F68U>+dXya<@+tNG;rOi=c|0i}=R>Lt> zoaf^1O1;a((Z^s-+lCQnJ|*s^5%Kxh$scb0w06taci7(jluEc+Jc7bpdk|~ceYVJ6 zZDV_auE&qGm3=0PX?){MvCh2PeAhJHQDly`mD1vLD?OLm)I_9^W%EIl&#|Mc%~e&q zhSzwmT>4V9JCI`T-%~}}|4q#tz$N*!_+@(S-i4pa<3-AK2aPIh3~v12*UsHdVcM*bPY-O znjmT(ND$h(S~8t#A7yovq9mqLvKhZ}D;n$MG{XaEComgi!wR47o1YuO85&7mw>-)V zjeHO3+pl3th9?7`mS{D{V`u`a-VRxbz<$^rZ_Lf^74^I%e@fpIGE{^Y(k|JsQ5y^B2f#~N^ zhe*qF5yquUqEycIlSQ!Nm<l#e{Nf$g`hHKd`jN#8Vj!cBI`VcjWq(T$#au z9F4sE+L98Q@du>QwSGM%A}LGR9= zwetRVKi^X!WBaRU{(GgQgFxc~b-K(_;6A$g&ZYp3d9=I6llPanzgvb9&Wjl;Q*d*n zJupp`8RniiDYoD(Z#eklJfj8aL!8%BHH)S5ZVr(FMl4LHGNas&V@8b(<>nQZ=u-M< zTo)Fy4aa?r+;_dOeNR6OurB{U!oC9@%eQZzI|`K)8mN#6(U6gpJ4FZ?H!6}; zGO|i4n^MveDSKrT$_gnJ4G~#og@#Zs@jbue_#Vd? zTZfjW1O3Qtief0+Qag(e{T$L3efnEhyC`^ERDLFz9FSbz^4fyQ?tBiZkn&}9_nB~^ ziyj_=H-!%D`@K~{F=%_v*shUZ{<2Ia%nKf!YW^DA)3?AR52so7`_1f;FD+%^2{2nHt^NJ$eOWtVI@hw4p;KT+-lo- zJdt1Ug8N;UvD&q~BRXA!qL!w4Tdvb7xjoS>?m6F#@U`q{VEQM6t-s@H$K7xK?m{Q9 z?Wh;iuN7j#S*o!02DhB{#hcz!kL2)wXc3xhc+Rssx%9B{nM{;^e&>D8?SJ)UJjllI zl&nQew^~?lbGs0Lm-*Rr{tG8>teL5da;DXxYQK@clHRN!UE7x=I`Gwb?fo|oSYL67 z?~StUuYBKgI7Rb&Xh7QC)te-pB9;iflJ#hqpPjjFWbpY(c5~^z198W7dzKBTaEb*F zCOWV1x-Ai;6WU)>jp+5hYOed9nDyCVW@fqpd1fmsZt9%KdTUuR{yD&b);Ynpr;DOC zRlyQqF=U#WO2PsVcPqE|5dD50Qa`)37fU!}OT*b@yM;-e%;KA#K00Ic;RKgNX_A5C z@JHH}{!a|MnXDckf{PSd~)LKj>2Yr}KR&f`4? z@}x$1WOYBlo%6~5c`qiL*KRNqJ-7c$!TgWR&+Vp z4qvS*Gs(Vv5pJ}#?Ak*0374fuhFMC}YZ5C(yMMEYGe5Q)9MKilRxhHoH+%W+P;cF2 zQBZL?K|^yK_pIU01 zs;#T66U4Xf;w8G(!8^L1->KqwD}LP}G^a%Q@n*_{e4*1HD1m)SgUzSm1eFwYv3aG9 zR@j@ZR%OaQ{iCe-nOWzpt$H;(#S)73^lUzluL{aYKvDchT-~chAqO+b^ui%^>FY-v zwjI)El5mJ`ckkuMX@9=Zx54rgMBEj&!!>5=xmnb-nz?`Unlm_A-2<3&tx=KM`tZvB-!wCsOcOR zi`|CZwAoJgat#K|w987&?Y1|}|MT>L%lG(btMMJ}$Laf$@|eDOcwCxmkjvG|Id)ZMZ1FO7y zx?dU<9@`pfHa70D{WNG+5t0|UuGp?QNZjp%XwADe_%peO%?F3O)I@KALFu4S(^)t{ zhnxcAi5ybr&xSu)5i8$W=*N7FFf|OC*Z2ol=htLs5$Dx4#~V2ho$YlQ`{U(dcHXW* zz+rBthIe`918wGoOfsW2kqu5SZq~|1^#2;$z~x`xS^a(#0T07tdyNviUW??h@#S^9 zl)e#z8@#N_@jRVpvR}dN*b>A2|89I1#Zgici`#xiz7q`UtY>^$LyUb~1-ESEZu(q< zy*hYU=5%S}hQQEWzv>q(WV{jMRujE0Bq!%|dns~aG5k3)>x*G98?$|-i_Bm$6SQxn zhnu#>Z4($@@Wg3N`COgjuS4|CI*n;EWjoX*L`@i&ssAW#$oKr$`s`kd+0T!Qk$2*v zlcFO$VCqdj`4zz(G|LO%o(lr8qprJI@q76J^!}VvHf-u67-m`9pJ0Z1`A)bh{mJRy z`|cNhB~0|h!?~jwHy-}?lO`udYCa3;n%JSD9bIKXyPP>|n}06~((K#pPtf5_eaIqz znzu8|n3K=sCq@?e^|8fgnY2^~9$H<|fyaajVw>|OekDp7#1K(M6-ALgTCNZN$C+HBPf##?D05)k!6;2_7Z)e!GsI(k zJSLvPmqK&zU%!9_f#qeIL%-->oshY4eUisYP{H*ut6&Rdlg7+ie`hsRm^CAO-sGfR zuE{*0UVI&DNle}_Q&j$3e4LvA5yMX3k;GRsAMD*lvSFZ|%RjQdb%O4SJA>iTC9u+D z1n;fB6->HDFgVSg6oSAB9-ns{=>aos&P9*xxq|}j!L3}xCg=8Gx;XC6Fn@1^?}=LK zU-9MAu>O4v@M{rtMypA-HjLvJKX`=gUS>#8E^!w!cFpH44B5<2P<>KA(HpcJv$llW zZWiz`bs2b?hacuuxg-zedNp8;Kk3P*;dQfrV^FL!{;G14p??)sXMp`okOWLyo z!Mb1pAvVF9@WV_)Rfa_%Kw%M}WuEdarYCuF18ye3>f*Ax<9eba&jfZ-4dvZ(UFi3g19$!Gta zxJg&)4KGV0OL#!KUVcc|KDP@aCHz~7)giYpwO;u9L*0{4BU=5N53HoF*cCo<*1yl; z%OpR)NF)BX>lN*&Rc!W|!jjHAJ2Af$ypUv+K(k3Ld+rXKaR5 zv)+I5I7~RV6W@Nb0k&(flvZr=Y`Im6<_}G-|ICS@TD)LoF!E`IF!N7CN{GkB?{W2; z9?9fKfddF5Pz$~FKb|!6?O-yV$r?;Yl&9nMT&w;s+bMMyw1VHIR~Lg9d*TV2S80Cy zYZE3-z4bxNteS(F|MDptFfp?-w8k@5fjVQZVg$Q!eHyv*|C*HaW|oA;tzfC=AIsO7K16?qp#Vn4%ttHHMxxFQ z>!tb1Gr0gV_fEPbJ(G%{jCl8;prBKeDa1E|`5&pH;fru>Q1xy& zUft>jx;7m}$=x|3oBQ|hXXROKp5PtM$E0szhP9+F0=b?}N~oFMcD1>*_A|b-XVD>@ zwoHhk*q?`TGP>dR0&xyLy!!fq4@nWcvkjFSML3*ub;DRIzhahL4o|toEz29WSED%B zo_-Zozy6buzv}Oc?BSAhn!z-y1EU^ky}UR*xA@Cp?X@ENvX#)%0on^8$cLf%35Wq$ zWFU6HR&oYx!AKtx!bdW)41B&cFQ%IqaL%@n2)6l-o0jgj!cm;O4NKcv6tfg_G|D@C zECY)hd{6x_rlxCg#*QS@+Rs=X&sf*&!`cEd;WEHMV+u=vPY^nD&jFR*h9UePDlh_2 z>fqzso%N(vev;?1jcz$Lc5&ldR(AcSrHd_Vlpaq5OIxO*1Mxl&WSDoyj$F z!eZ-}EQTOL1eunY^T;*OsQ}lV1G1?_+bK^BpFI~c(ekUUij?OR-zy&LMs+@kGrTC! zHG8qPr6!0K*EV52E`J}V+j)wgwq+)>>Plu>QYPYOnY86d!?A!|k#DtIvIz>#bJldh z!gT%;76%LafUGsaOF{}%{tQR@-i6RHIHO1@S#mFQ$JDV!1xY!--dzYdklZo1WzZ4; z(f!5t;7J4Id6#A$)kpOOce#mOb>P8W4Vw-qhbSqX35X~37j!#E3a{(j=(IX|= z-34466lt5!{t{KF_8$CU*B|#Mz znv4|b&1%xg%DD5Ho}J_M55Axw{=jL9-JjD@UUJv+*{W;uuZ;Y*$a7cC z=R3`idNAYF!tSg_O9^`4Wg85hvt^F-Fof60ILnNb(gr@Xuv}?*#w1s=y9lB@AYsyp zHgVzDV$$^Oo8P(9*0rGk>j?*A+%T4FhLnn8f%U$*V<=AK>(QIrt5E@loQoaxB#rc&GpLMCmHk(&;qHIYatl zE5|fPzdYVCvH0+CZjf6~7zy%bhK+pGm5D!?UvJ&kvNy}HTF&CF&RTlZ*PKW$^Gtn? zAAxpCwcgE3JFE@EP6@MbFen$=wlwKc_@T5p^PF|TmSw4}SQNz&wy#nas(Cg+D&R&0 ze6RawTItJKaR%hfHoA<-8FS!piPQTA?V-_@STqvPV)`&JAn(=q;0^UeJ*}`)r+Z>f{=S{- zMHevm##ew4$w7N*LKb8ypp&NNaog`R7o*yNx5qLu46+xy;b$cg!!eJ0x#|lY0ek#5 zJ3T}kRk~#uNK4Dxde5~3EP{!N z75oK4B?jVYLLkJ8`P5#0(kGVLEj2aOWBxL5CwzZ>>mE8W;*V-1Q5|Tvd0P279V)*3 z*XfE^#^ag-Q~lzZD~Lt0fx0P8qpLgoiD+1{m#AZ;KY0U9d%Km=k58^G?75b!eN=a& zLsfC6L5RP7qF&mH9+?MvJH`gS04pjAuQ_T?ynzGM3zXne1C+LaQz8kX5N2qNT~i&8?v21g^`-JUFqyf7)M|yRb5U6F8d*9S&f2!R&PN3G|2Hf|}YxAnxqRfK+j` z9qBzV@)%0eO$j}fONmpDz}9&TJ-$Gn7FkF@LrUl6$aj%Ukqnic*H?%s_`!E>M`sI| zDIt0#R<*^vx)=b(US)Wd&e(=9ElG@=fd_2h$<-lxAE~?uioy+`A>(JXfyP zOftyJ;n#^)W>1`_dKCKA92D^unH$ihglh+FmG|-ITO%%0I{o2D)d1=$qIzNkQbq3z zY;PoO-If_U850Vym2KE5CJZSKWfw8{z80YY6i^fkiFH#cOu8j{ zAKeHLz4YIFTp8u!4vsj5-db?}t?<*!FpD+`u1yR8Uvm+Eyn#6X@nV0C2Z=Cn@dwn^ zy+1uNhwZO5PeGrz2<}L*EwV3L57|Zl>L9@b?CDoDN`8HNci$12G~J-u#0Yj-Exvt6 z#0mp$v(4e;=spVev62#qGdnY#%>(Fz$s~UD^~9#=m{^v({Dy&R4V;_uY0o5E$24$c z`nnq{C=?w3DDcolmDANSXj0OCd=709Y_xvayx|G&%h?>Y$2V)x z{Q;zYgL=4YPPy_KyUJ|41A>@+u@mnq6Aqti_JGXw{vfcdilFTh`-|;mg;MobS- zL#jzoOc4r-GF>Mcw-XjWQPBWO7@_ArGu}@$eCE!?E}T>sr?IA@yeNZLUJtGPiSA4N z1oo{`Rf%oAuxLjr=R4_(aw_yDAZ*7K{*^Q&u-oV68`lYAGx48Ak7(Y)uq5dpUM_^Sr|Y0mf3AKi$E!x$Vg@M_UY<(6?(3w z55vnvmmPwIta_>35^|^{)Zth5LQaWmTw67d@X?Q`%t_0Ft}xS>;O|P@bm$(+#Nh#o zKpQ8CW8ITzcO|br@D~)9mRdoMjYIy3P;b0?4U}cjM$K+Su{_AH2`;m(6=`zEW5V^B zA$aKXM)$B&6$~{(3v(T-$K97wQmit}s@NE|m{azGw#RwoocP8>_1m^k3#(1cSjY15 zsmnMR)gI!=ZoX9o8Ty1n8}j~WLjO{GBN$-q{e-m39X~nsA1Yi7sogjm(9)gy)2czw z2H)QjJIj&bN#|59V1e1GIu1Vp760(A2sN*k_2+w2tr~Y^P!{EEtjy=TuXaM;ZQp&3 z+(1dIw?MyV zXE0?4^xLZ93>>Nyt|MZKCpRH{q%bowYScvGR;^kmsDPg7LF|lOqw8)&bF8JGzvt#E zC!9CP5Z@KjRu<1xx+O!;(P;legg$-Coeohyu+D4-HXqLk+?jItDFD3>j=oCYn}|TJ{9D3eLi}Br zf7#Nd{4c{?#lJKa>i(U9#nCxl}js$xsdex+zNL0_Yv!L&wJhCc6f`B(y zS`Z&pRa|w!-90e#QpK6$E5|+_y&1oY=f~sL^>NW$gsvStOLKMverX3Mg-N)h8Cvh2 zQw|X7Q8`XpKW{R3MqHxv)Lr#b=XFJ;9iv0#_uQ}Gn_Fq^T=F^#u5`Wjt@-8;lBIG= zt+=qkNN4!ns{5u(okF=C*mwQDIkYE&!<=tt|EO(J=xiXk-m(j-+$M_|-K;*r)828ujg1rY*j3V0`?dwyKc>*JO<#TkOJe6!CBt}pSVU% zA>!etqc7JDfG9C9tI-(|*^p3T3oKe@Nu;1|ocd1Mjr4g)NPYgOGMcFAw&`1@DP$%y zgY1seC;v1{Hzn=edg`nC;1i|yf*BfH)Xsm7hT<1mS`x!>lVH6vV7;-4bO|kJOO(3)l!@7idaq-;-P5dqw2@xpUwE{eAj!81#D+ z!=T^>#wXpBvC)h~bP;VW&+}4E*crIR^mbj$$Y76f`c|U?PqcP8q2rUt(ILJhB*i2w zEw4DpA6WGA08c)mNE7{@=n$cgYgvnZFLlgujJeKe9r*AKI8bBVuk^(m1{I6uR%Pep z+!5CO<)>=^qMiCk`KvpITJJ#)5hLKok(I7~uj+TB4aAN&{EVgQR)Nkl)4EwM|#M%(vqK76WR$?aCK@S}-qYsHO~F0wrR0LLoSmjBu&_v3@r zvA~tGV|NAp>!s`4%PO~7tXjT&`O>8^ZGwwD&#`HKTh1>ZTKOA#?VM|d4`f+3VCvY{ zY6cZ3B1ft99S0``L?ZC0`#S3SUjQizw+pS0`>r+r972DezZsu#;rfzYZac?E{C0O_ zs8(mCf)cK#`qk4Un^<@)Ae+Tah=4=?Z6kbBh?JuXpR4NF=dG=9+YFoskIS!ca&%<# zumH<2%q9C==g(IJKG%p{o~{SR8@STXN{&v1>|NoE2s67z2GgF7{JvKPJ&e)k^EV_I zmfsNxQh6rRbD}6SyF<%WTK%$lN{;4bq1|CVPF67DM3AWXsr$<-KZun^LU(vmnBRFC zQP4pmq;cNxaCH^f4W(LEzI+VEv#oXMruViI=^%utAk}sJaX;%5EaBNkC*4h{{{5_3 zcaY!R@MO93t#uz`%HrD!961xd>~c0viSO^}(fU|i$wx7OKZ^AgX4z(#?~3&${7yW% z6Z+EY+^XX@we=MLEA z>O=7Tfk~JhGdlP)O;uZ6+IhsE@i#VFxjjoN@-h6paWoMeaRENe@7;vrkQs6Jjm68W z21{wYPl6&OSbdJdvLl#fVz@KW_ zApL`Oo0!QncY0Nx?o;vr>%C!JzWkBlGm<$Q>(BL&I3?qDR)2U*$qzkp`-G3hmCz~i zx5xNCf=^KQ4z%^nE4x21u^zh}Cyjfn9P)VvXotehcJM@$&i#WL8Bt@hq?7+^w@+*V z7E$jJ+?%$D3@W;^A3dtPU)SW}5=u`AE~Nyfh6&bvGfUc~0v|stL0AhaQ^<#dMCGmj zrY~-D=EbOmB$T~J5LA+;Ut4KV7XZdajUWVfs&_?0m~?*k_G*7r3PMM_&M$wY?SGD$ zg&YlfR0RPEgL%d%8(_l-?QeU(XLU)K?9+^ZCVG#4?B#EW_~X7Wk{u~~9hYSLxqvaG zALa-re}7Qsn}&uwkk=$^IwHruWaA2^p4$-?S*k%}D_>^bzvgn_gR7j=jZyz-jQ6e0 zGVuuqcATMWe8!!f{ECVnWBAC(=K@FxvTGmrtj|PTHRL`tLUzs8|!S` zLxT`6ZmwC(OX=N6TI4ak3uY?>M}$|TIE3(=zFe0q*0L>vd(-jd%RT!JZBBDW@F(Or z4QG;MQ=SYZklb|gm~d#}9mNyPYtEx5!?wPK3yQc3?tHv{$r71SsL{4-@f71=3+2Z= z)I@d(VWCmzfl6b!wIcfbOrkjNj&L}k6a2vw;>m{~YTdzdO8pPzStaTq+lPNXbvY(;z>p$vT;ueYz zzl|O-XpM?j#Nd!f>wh!R_2#K&~*}a@(2qDD_}cKo86;u3PloLH09Z=+W92aYwY-A7tyBgAAa1G5&gEFlell@KbY$#+thMM)6-;guBzyE<;^DcQkh$Zl^ ztLtObMD8hN(JGlZspxlak<(f4wP?oxnH79hAW}QKE^&FV;jl!+oEnDctR`M{Y9WxG=@$;x* z6+So#xYOWwGZ)0Ig?XzSem+<#a&Z5`Cxb0Pm_4E{!6fAe@_$S=NFg=2G@9e?8rYk{ zi0jD0_+u-B+U=tDQ9Tf(D^eV8u9i(cgcFcqk>VN_sgD@17%IeI&mhvo;kTa$bI9M4 zse1`00FAwYr%H-4=e_!k6TQyxc>@73-OtWG&BYx8;c z^-yRr5uW45rCeN(kGz;$SbWUV!L#MwfkWA+3TM8VCjwds^REfQejstS8<@h%HrI^&T~)wO2q2RS?RB@bSo3mtFkLQ_Msw+z+lS@C0~9Jb?_&2o}@U&;o2Z_LVN4}Ky|pk zv+*gS{MupM!XJ}$ZPxe3+joF0WG_l6(6KJWRK$JCyi9ndc5wrDETXk<7^lP}K$Xeg zP+Y5lNbLTn z&Q52NU!xOzK2CAcr^wEoyZYO!_A*446&(9rt!GuE5jU^*-hnf%WzTGM4yL?o0sDDn zv8Z5bLY3f8Grg2`UqJhF45c{pl>_aGDMCx)=OR|K$L3H9iaLx@Dg*S z3Ri4I-rNcq>v&aP-2ajxNjSHyQR{~4k`9Pne|WCjh@PZ6hr0JUX=lSisK;Z{X766p zq|ZYZdykVXlqqTdr)jX~g!9yF4a|Gr31;wws+&wVuCw!9HG5|kwp@vrg7oCL zp_|4!H}a-Kuc(V>Tex^WBG8-EgAuJjH{dN&L)OT2YDqgf>f0X*>I6sZ zOh)j#ao>Ob0fG0NJZwfnC@;rorcYr8|3Sn3wKWQM;?20q{>Ehfcif)6TO~k~&K^If zaX?d}cB|yh=a~A(#N53ETIQ&V{4P2cYYEyJ!j|bZorUiq>k|M@`Rhj~AHY3GxBTyR z7h>6{2vN6lzzkgz;GXf(F_+~3R7u1gT%gY#eTnAi6S;%`y6~R#Uw2PFz+W`s)|)*%&#N@Arr&GoFUceH!Q1JUkF%E(%4bvW z-@RB7js^0FNLfiD0E4>#-s_{d#^KEU!eLL=&*tqo-wuuuo%@jc@$C3yR+SKcxo~_G zVtFw`rbv>bUQ`~VYF@WP+z*N7;cs^j>m*R##4q(LDLn3db#I?b3*}W|^cUIVV|ed> zLEqdv3AOKa0wJiV$uX8KqK@qyk9y?pE68%@^;YW#3=?hj8JqLsa$94szNJ}qSAe(Z*wp^GNLny8_gB{9U_Ln3Hl=H8v>|^ngVbFz zt~tWl%RG5N^#?)``|;6!+)o>k3y0U1q?h#z^NCBYqb!0*FjVC$Bl?R0OQtdCeJEYs z-2tPe;Mh8C`xDtAkkw?P2&A8Vss!JH|BDILd|P?DzM&z53nbFt-uaH5vKg!@jLx~b zS8&-~2~p9&&r7}saQ%fM)SjOzg~LFi&Ei{rfoEoCu_)S*#LX&~Z!z(X)M4ty#HAr$ z;Z+Ym5A4cM)ETS?x|&O6dajwJ_v7Z=bT}l^iD_-%AGm_5p)%QUc-oaC3J}u9g<<6d zx0o0i*E*6lPl=p%yL#27pvPsQzP>Ytw>G=w*`t|_Mg58|-Bs)o4Xge2KNG2U>aHXI zq+@f_^4|A~5cGyiiv;SuBD*)GrTcC@Mv4epIxj7Cxg`2K>OLUdjZM(@5yeiswsLuT zOe2AnTUb&9E|^uW6@?>xDaxJSIn#+M^qJL_2hi_XZe*? z*j||^W84b@QJSCiAi5_&~%f>q7>V)jL2Ny4^*tk5*6*x*Eg`WY5Vc{R7K&pOIP>$UwwO1RCMKrR}4J z#zw7XAGq{LqzEpFUmj$ybuI;F24ybD*7>|7N|bC?!Um5AmK}DCILhvdBxls;^GrwZ z`@Nl_Tw^JCT@(*MJ#ij|$crOL%_Qp4cd`9WH&5h{mqZERJ|5=>8F@;)CgHp4MA1aK z-^jd96suO4wL}h#4R^-w3mYpIE4{r=gBLMOW(lvz)6buMY6`!pF1s71ug})&w60uX zhQWBsT#*Q36@OCOtQd>j&Zgd~_)O$}N2%;MCA6Ro-Z_!53*W>@YcDS&8=KmQgf{H` z){`GM%6~x)(RQHgNN?8&1VP3^wj@mK& zr!5UJLB@F`OOfz19zGWal!LpAS<1R3FrL-#Z{D5(L)yr0j>CAv;;7iZkPIXfOs}J& zq@J(26Yax`5_(L0{3?VylhAF|wLY0!u$`Vvs-=kB5-z@g|y zPJ;%F5&b8X1KDyHm_Ed-qX_)XPH@YXKL8UgVrI^~ue%6utE-A);ca1*uOHwh5<=SR zJ6sOr$Y63H*n3L?#ovT_Q3AGuI)t6y`;(zIHs>1(<)6t`w4`}j(TPu>cc)!Gbks-# z41J7no30HknCbCox<&$ELA>JOaM0%{lU&6na) zLpF=I4*cyOg*q!e9o@%_$Ec8*foAR4a7aMHm0i--O|A_ZOW&iewWdM)k@b;WdkgI9 zYH9T#enH*=?Ab(p_yXUQs}gl3n@by=hgE~@-pB;DeRKg=Aor1?@6mvq(2CIRcw`%jT*G-;bi`OXZ?`72P`u%%zDcj2tH;Ep$ zkOn%ogId==yFkZ6s6YV9$~C{N3+q&_Gj2g~q2v+&r4wdJ^}lZINF2j`AoLwcCe#sx zaPIeHm!o#^^wdDWc85&6E9)jQluEwo#>XcTWG5CIu9|(3qhJmoeQ+@CO#BqAM?NCb zlX~#VlC^Addq2N9xadWp44wZ+u>}hkUN`fEHxp;KwYtvvcwC2pu=UAPz}#5^D!0GG{ppqXTBG|>=9y+5^FpGOGp6d$XX zHGpcNUkql)pl44TMJ@1ad%HATzU$Gh)ku(QD?U6nN?%aGsS$e14tN#QDiEi>m0>p7 z?y>b;SJOL<5tUGx1DB_o&ZzUwNbe8)9)P<%WPz=VRpv~4=#Z92sdYYm^OgwUgbpr> zHWhnNak_N7F3Pgke*@6aS(mlXgHDCX!oX`_jvMS%#*gZXiZt~IIXgeb4Ab6w>S1#q zO5B%=*Nmp@ux<4|6%A)Gm2LXw z%V&+G67(ED79NIOhR)pw!dXXAC1&kJ=MO?jsQ?P~gr=t1uKC0_XDx#2gYJTovmNFN$bX`jy9UDw4FrI>zFm5AQk}?vm#_z9xLsSEW z9ser_Ix^zl`~(FH0vF;kxgi2i?egWz9C1#la3hz!!Ox-s3xFhIBr>bS4V3yFa+sGI4FQI|)oJfCdy?ZKw{H zKXG#ShPwa-(F7x1?D+VOHLeH4jSivA3DjFAuR(2kIg;r~yNbs9V^PHdmZlu+afl8h zfpU<#M6XI_9Y(!mWgV&rP(79=(w+^;cp~9X;JqL5$G5hy?vsgVy=Qa^?Jo(49pplF z9180=0Kf6@pEvKmJv*B%CC|RhD8n=ea=YQqY;V)=Z|Q*^Pnvr;;seQhSr&DDU?WFl zag_2t7b#lg`5!4<=Ll3lAkVqQqzk}~-nNQYGN~rLQ2sKyF6B62op_0aHjW^gInz~F z>><|*^wQV{YCYRc;7F4uJN7EqHg$Hov{wqsKfT?EZj!)4Ksw=6Z4aEwn6Wn^SD5*x zz`>Bixpt~YWXvlv!^;`~%Vcj@uwX%YJJRSU{Zw~4Sk*r|Qe>CCbUA3fbWJ5z4!@US zx8LI6_k(iY<+w1e|XRfrVn2p+3Yki7n-EwyKUp4v1#1jN}xgMDGq^3$nc=H|xJ{^X#^E$6p zCLb|rOmN+EKcH)!${bAmFj$l5UJG5y??EnNH*A@G%TC0`lUW_=aC3>_%I+#&G+~_F zZgb~z{jcFF?!*%S;9{zdmorN51PoIdL2*P4 z(+S&{hatqu%B!%@eqUyIY1U7uzcYKdQG9N<*+_udL}`|WjM=oNZ6{?JVefZ|8(c3^E9RO#!Z#p6g8IL(yaXTtzHb9wN-o%*o^Cf&x7XZ%-l@P*-RHWx3?yJur!?s=eSnBRrB*~ zV3D;pen+*iYVbdolb)ouigeP%6Gho5*D%3tEldjo*BL(O zl4X4@=`PKrF$)q+eoQB|eD-xcvC%Z|koDzUmC~LM^^P;Q2##ePfBUj^Uc>KFNq4A8 z^A6qlFDc>WjAhhwJNY!tBv-M^H23$=bbN6^lng=JJ3dSbV*}kst1q|~UcI};4;57J ze3I>~0DbaY%uW`Yu~>g%34fols#7e8-|Btjb%%nROfd@CRS)++fd8A5oqc`zCL_e~ zL>@6IQVE%|IrEr<+q%Tym(tFe3sJ1Th|8*HKQmK8E_>r z@yFf`d9b;B$d%R2nQRU>TtlAd0G~OG_-ZmGP2q2f;>nkid2<#5Y$v27&$L&bNB#m* z*0>mMwtm+_`U6%_`_#aBsFD+rDKaxiI^5)x6h+LOIZ9CW_Sc`r^(u1U_t-79jP>X& zK+e>{A+?BU7y^ztfHiY$(uz^h*$!m=w1cOwuTP%-$JDpoZVR(iz$)DD>M7N|xO*h7 zN$SDpH$&fPfcQNg19^)KlT}Z(IOUsegVob+uyY?09qa*m*{6;h8aCku*aZKtZr&IY zvG?xXPI52OSX=S(tDOZBJyO6)Lp{LsHb5*evL(s1lXQ^NyLeF~jrpvzg+3CVrk_0d zieJt{mRQ=T;D@r1R46Dz$4@1x1O}q@BMRvYT+c*38d~6Rf;zWZ?%(fU>f(YxxDSXj zD78ZI`xmlWsKj<6{pmD(Mf(Dg+l0^_io}@AZcIR=t zYQEAgSDOKyQWW0T4!_xSlZFV&fgJGas6y=&m~5NSDu5ITjKK>eV3K4aG_iUurC~(7 zB$6@!Ms=e6&R8WjN_{#n?VVZeV1Mmbvd~iNa+YxR(yK4lIAKLq?y%~l8a_k5=<>yj zNr!TE-ewk@-@ig10O_OE$M8xg$KD0f08OA zwy^bFw^np%i-QXcrqx72)`nMRu5r=(uhl#ls8`H^$X{kP52JAwknXb@Oa6pGi|Zpp zj{haf)%%dVq&Jq!w!{v7jDZ7gj~oZQ_TjeJFe3E>P-vXJV%UznI{-e)+&99HkOZuh zOP5Z}lW2sOhQg_yxE=kdLgjuG`BU53Su84IO5np*HY0H;0k{mDxMRis@6E{5e4y6G zv^b9qT|eH*A|4o;=O>ZfbGD_YupZZ<9VvZs*SVR{|GDYXKx$PWXU)kGEW7U}ac{eO z2yPnk@A*=P7|NJh-Wb1+%-%GpP=+q5gFG)p-uz(*|Mcg_hm@49aIciJ;cE?J^BGqq z@-xSyl|~vbr-h6QVq(D&38MlAbn?RSzX4iobh9RWV!H!qwi=v4qz5_K*rHw^MrV-$ zdO_e00?AdwrHvd<$$jh{4vzBtx}8WD0HfDf!c z{6xP*h4Yla5<{WM8?x)Ln@YguZz!`%jwC$6aR34VEZ3>9S-XO_+9&z>+Li}pM zab2H~<%enMH7dCI{Bo{cM*0@aiSh;X^p6aWs19!OThKBLNsTl%X?I5HG)7+h= zP^2BY!zuKP=Vuo^pSHzh$m4iq*cK+;82G)9U8NSDJQlsUkztECl*6{&Lo@+MJ>^gGD1F8Z z(Kr%A79G6Pj+ffHaF-T9Uk{DT%x7RIdus4!Q5ult0>LL_VhfrlH`9bS#&VjrfvvTg zEXk0gT2|t2xx)4fOV0~aG(R!$1-LpfYBh zp-GCIAHeCl&y>;MjU;Gw3-CvN!bBI%Gr2S8nxE5K)W)EwB6Qni7E|I4lSYQAT!8fw z+J@p2km(t}mW@gY;sWT#ha5AS$jm4%pYCIaTxsjvomq@I2g*b)SaR78v)p_qMWinj z*=S>-ZooQTkqie;aBNvy`@UeQm~+kEJWjrw#$x04bmU|3naq~w6RtWk{Ui1lErU~j1?y<44 zkxt0l+nfwPY?`nhZ7!#x0elf`kB>V7v`D(?T6%i1W3R2TkMbP)1B>7yfZBJLIsv0R z{NyoHOlbWqnz|8PZrl<%0Utz0vrm5?%e^czEc@25yRaZn_gbDR`kJGadz z-kE{ucNKcA3->u~X<-kz$c>ANEY6yq#zcDc3C0KbFhNZn2qp=oFr=t;kgSoFeTtxT z>ka4OJ`4ID%Ic~MuE=6*(Y=?*!027Z$Gpbd^3mA2lCv~Q4e{w_NF`kFMVW#~qS@ps z4nsrma{G1}868}~`4NXWb~rxjBN9Q`KA%I(Vx9x>1BEsY_S=8zet9f2P=6+~CeB?` zQPI=g1z;RU;KPCDBa#HS*6b0|^cU3P?^ru=K==}AF063{HQ&%>uQ3fa7M5_mq1Srq ze>agodIVcROhK9IMKOnsprkoRL+`T{PWV?XN~vh*2~G}5zSUb>YBXvT4-JWa9Bh-&$i?UyS3t#2SF%S~M+pAe#Q;I~@9nnKW z5hbhnDPc@MO%LtcF&Nd>aYZ2XwXZ-aJi{!jMoTjl9QUU{9(4nVwB5RijEn#PGuuqc zxaC=pZa6tte40#_hV}$YS%U+t}Cx4)S$@f(!FF0z*_o@X;Ev-tHx*4QC4|-Bii1EUm#}q`ryZc_8p1Qg^vJj!j z1YLytI{Ncd-f+eTgIr+zsLoMJeKG+UjN|_s&Ai`2g=xV85NqEr;%7EvXRDikWV$Que)7BhOu)pjXN5Jk}#>T>Wg4vDXWHIuRr&zAM z8Av%cWgQo!C9;-K^PVSI6yLEJjt;quFU&^zrDpCRMHcbi+Bvo(1BYXpE)w`W@kEf{ zN1RWWMaGnphF+yTy$geDgSHUOGECVaGUqqryupzo-1L*L#CdQDlmajOLQrI8@OkTo+mHGsS0RS)Kcfs+-=y}pqQ(i?o&6G2bR#H;3$4cVx;lr0h zDAT^1AkaYAjFI#G>*h5mw_f6XKkw)S(UURJxreCtV!+osE>D`Zg1p8L+?;Es^~Nqm zB18Z#08vW{EQLGfx5pO(V2N=-&b0m;=dU-C2O)Hh{7Ww)#?If2|*>5kzf@i=E_ z?Pl$pg(r5tjB2kr@|bY&j_>VR--NbbcXxNn(Y5feJlOB6CR(?a@%W+M83_7<$#!w2 zwfR!0b%(eP-hBi7M8}vCYs01~FcjfuqVYzLoV6(Dinm*Kg!;1;OHLEt(kDm-2v_8z++2v`euY8o54lM?YSsh%9vZ9_Jie;pihyJnc=%Jq1D;)& zS2uSAnyo9BEYSo=+JiAKFYo?&W3+q15rT_OvD6NH4&#G7Xqe|fcYi<7VH;HQ{1yQ| ziVkz4s|+XSei0DYPtoI4OZ1|lC!1htpvF%5E6@%*jO=spkH{BZ$(20{V8 zqXN$hU>V{AEOPKIcpv(Q4mH%*OPu^@4Z~WOCxnW%*0ZO`bRe49!s;@{1eOB?+0u0T=_dmvzALWTS2jwrDQ)IPX_JQ3zZmDJ z3B3%(KV$Zce3SJJ3jCrr(m!6j09tftG08%b9;fJ89KhWUfhuJqD%e}3qGeKIC==}2u=*Y;E~Sp znkDq~^d24_HP!W5C;Rtwl1L<@x(k;W7B0;AvBytiE$;C+y92$?o;}m4Ne+L(o>w8$ z(QY9L>xfpJLn6m;v@&~d-GLWX6_JvXQoE zE;m_vK~8RjCWKGO0boW<@Yg7k3gNr_ofnyfheNj;kdy-{^wClbNW8=<`5MDP!HRmm zXy%#-<-Pq$e`cz_Q`yU@9MmlWfOy-Z$e&3c-C1x!mhW&pr?Mz~lID%LZF%RH&8Cs0 z4m`@)ZTP-7OVjAZk1n#bm|pOg4`&$3PVG+W=a7w<$|V2#7BWu^Wpgj^lH!$IGo~+) zsmYkd8i@}kDywVGMo`h$Znp*Vlm1sqklaL|7(4gwo-=Ezf)BY4&UPCnvKjaU&k`m^ z+LsDf2_A#V3S%_o-N|k;=6+_f++LnK5$tLL$r=HQjjGOM9%bJ7Khc?xd zikbiC!Wj|3IUW5iv$9M12k89g{WVp@gE7sIlD%0B^}k8|`!u(xWrn+Fy)L|8?nXP+y_nn?mZrLH$qwiB zbdU2~r`RcyPG;a?DevUWyzxb4Nx7KV;P(S(!{C}a_%(+1IQ{I|;ftAg$-n}Nyfyd! z-n;>8^5g#9&rt_m88%iOt8tpsGdra}@iwkK^HYIlo?edfHDo&fRoj0N1&N1ht^qSY znfxk$kp3eNb$_nl!|(3&UND_!KL%j(ti3@!5!6rEY=AL*QC;zOx>+%d=Ip;i@)o7# z&A077UR6>&{ov_$e77N=dDh{7y$-*7&F2^wv5mgaFaP`L#7hMqc3nvwk~br1w`+iF zk@wW}knQOn{Z)6Gc*BW5@XjWF_s84Tso$lpd=Xvi6y$a42jokPScc`t6&4kooBH0w zFBAW18X^6h0W5QjX(k5jZKGX&1RlF`PHmhKDh+%#eXh_ zT7RQ)3l_{z{$FF=fU%C2y0T9X6pwRFo1gk0CgULmdwp;Ac)+#sf0|A4?kEvno;9Ll zcWD+I+Px72t?}30w0-}1x6Hg;>Ks~L^0spf>!-5E!FsL@(~~^)OzLs_x!0AU>`Can z#J@_Lz$~qv`n{6Rm^v4Uc4OMU3AW6p z{UjS3n}eyKs=UEAYg1?nk)LvHFnqjw`3APFtL7jGOr4L>=bl@zp32&Tld0-DDwRY& zc`~W`S6f^B8v2QyHuWFH2-ofOm7k&UdEg;wG)vO@8jb=_VloT8m>Jm$lE&MZAunJIP5)_fsW5c@25Z2cgaCW`trZ#mi=>Woug&4l=NAGhI$ z#o<1(?85x?ZaN~tgt_VC&$pjAt6aMI=Z*q=k=1SP$aQv2(o|s+_Ns#3pT^CF?$1#8 zU51bq8`u)miBoxUS?UjlGR0wd)lz5L>HH947QK~Vqs+|DYbY`_u@Uo5fK_#Ve=kj$ z`QgOrdX(teJru6Xn5SN!A-0kWCXl;nIjN76ws!3KzT<9l6FS!33t2O3!P}NSI^PpB z7h$I4nE&N=HaZ{nCf@0vlW#07yL!Q3^=`w*%5ieUj{3PX-+-4KZEsO)OVKE_-sDne zRS~mL;p(7?<2Lo!_N2&Mimlmd5AB&Xkjf<6+S;Bw_oz=jN^$W@LFyLc6CzIz~7>Qx4H+E1`&!}UHyDxS!EHK`EtL-r2+eSORe7TCr(J(!@4quR1ROxCibarQFd3kF=o z@AG!w@0wXxgJn>2pog86%I?{sT)r#~&Sp^b7~;i^0No)MS0=cUc<)6fF9^-Byn zzOy5)xPJ9&Pft&Ua9+9}Vddz2wnnT~5;{wm&l z=DBqaoh1$EhWC=&!5$*iB_?`cRaM*$q4s-`mO={iqFoi%UcHHe86p4z+lVM%cKBx+ z5H0_Yu_DE8xSZf^FiqiXM~B(+#dA=%_8+sUMp);Q`eEQe3z zz8Y~-@|q~AJ#y|XA2oOC&~`BIceTUo<)Nf6(JYU%iIci~1zk~X)qGP`R=QzTRS`N{ z3R9M{_Fyu(>D=Goj!oc90Wzx$m%a)xll!81P4aQja|zudP{=?uvM2+N?~?&C3!Y_` zeb=6g{l>)(oy(q``lhERba-ZVcBvT|7XY;pg>*+pa6DD}T!{Q`LTa&q5oOc1S4jzw zJ1UhGUom7H`niGm`I0T9i*cVLI2BG7%M?&MXuc z1^5X%+*pIG5j+;e4g2ic%Ls(Fv#RTPA&V1W5SSV!z_JT65A=EWoTIb`#}wJia=fIB^2KD|7o>>ex&=3*PqDjwIP%x>)Uh{PH4swvjT35!Z!Y@G_-DwjO6HwN`B?GT2>X0=GUm#GRKPuKMU@UmVwa;VcANP<0>3}Pa z9olAz{2jB^<(25N)h8dnTE(usa80uPwUS|L8~n|$cQniZ2ds01P-j>`YI;{9x~-%A zf8=ocqzK%0hr8j*A<^(a(rl)=%@?m8qUZi;%l8`Molzi?c#Fn!6{rWrc&cK6b!%Io z_Ao}1x((zD(P`S@+o|Pqy^;}OvL8HJaP~ASXkB_w^>%;y-J8|NF=5bm^X5&UN`Eu# zG=?=$XTXi0ofD&f32F1L7G&~{JeAe?k$HG_ecW6an_u~&4VT;7+flco$7~3e)oj_9 zvp8Efyh_;aY|B$vN(auzy3Y(GX|Kp8Z?~5cAamSZc6MEwaXs?9y`*lOinW>F(GJs@ zV$c0upg^&GYC88?-z@jYvCRG+gDh87RhgL1qiLPlo#CFI`(`WHm=ySQH5K7SRG<(1 zPlV?d%hkTA?T)u(m#)!5>avG!hYnpc92-^<&mIqJo7?hitI3mP=6?vNa5ibXj8xKJ zj@;IX84Oy8e*3i8CbvnI1DAs3*0m;bJ&bUMV{$A?j|kCtG`Ww+*$Rd|5Mv0$6%#A% z)r846J_$A)SqjGuWWIF%qJs=`4Wiokh$hxc#Je3)h}Vbyr8 z$tC{r1W-`$O40mEcm{3>O=$cO*r@1m5l$BdagX=`^C{Z1clYS;^Lq{s5bGfr>%Rcb{GhT0anemBJ(q z3Sq|OhbFPhhju4GNLl3b)p0Q0u3TXD{4Sy|nhTt;1h z`BJob(oato-~N#JrntBZw-Ud4bO|n~8n7>2FSvqrJqWhx*$^ny8Af>`HsKb7+sWAw zWpI;XcqAN$&@2?N^X-J)%h>~2S^bUpYNwg$=rXHuMg=T1TxY3J9&A#0k)UFrkD~&9NS@s5TaC7!JtkTY04Gl z1>c4c{FcAgi-!-uu^Oe6$!4c;u%H!nkQCmVSl z>ZIw((Rjlg=P1?4g6g;mDH*!Zc-6p4X*#;W>eX~x&A8G`PCsXXJ@_z18*UHWFMENB z;>>^^zz*^#n3~V;-o0zzC)Nh~NTSx$I?%T1lCNL8_U-I07#Xy@Y}H$!_O`kiksSQ{E2(+N~i!$7V|6Bvh|Ts(X5eIPjaeOw6`wrGt^OHr`| z8<9ZL8wh-%=Kx(KS2J>qd;MHj zdz~74P1?!fUWtXp(h&5ttQ}IZj-@R#E&14tMmOOP1que{8)`h(#yTMimQnDV-D&@ z6}GeHb6y;w=jRYQk`odVWOJ;4^jTRyu-b=1hne295!$xf=vHn%;$HQ?v@PZ~LdapZ zhuiWHLm7fR2lnXxax;Su$VXTujW&(*hbwzWsPFE)Pu#rC(Qax|gYE@UiTiGNc(_4B z0B#w~q}zz@KcXd$?8S>0h0oP#s2V{OK-bNiiJni0I=+?EuJ{4Y>5>>(8CBS<@Hz?ll!wzpsprUL9P5+}GH-#AC%wr*5{@8R`olL}B zDHO{(%N69M+kHM!g>!w%eW%VHt^o5=4cd-4E=rBn2@$dhmZG~Sf;@zjUrfi+f-)7a-Ul6c3^(2z(={A%vjc~>57NO{inMEpW;2>k{ai$|W5kDms*f&(-T4H&_> zLQHjq)O^Fd=g92ol^s#xL)eK|I zQY{3lcNay^&g_;Rj4Da^He7rT5ZKplNMNc}^KTN`M-&y26yJz!A;tL3w>Wi92I+9pTq3Wzt7;eT=@@_2Q#9e(`Ir>xz?2ll zki9mj89)kE;2|eoBld9G6Ck`@vDDPmDaaT!K`MxdJU1{h;$S~L`Z(4Ng~luPiKki6 zp>*6I-*F@Uj4=TuV;4tA4q6HBUR50pZydo37cMkcIuG^t9~Sl!hum#qY;w}+(xv6; zq*NY1nGWs0L$0;8Mcnr@>LOBefnuyT@2`2|dMMb0t*w|FXxv_!! z-DZ$|5cWiJwr8$egOn4f3>zzx$^19q({zG+=}+)9evsgDo`#v6zIT#+&Pbg(Kp(U6 zi0k)@34dXAy%>Jo0C2PjRB*k-&{1SpB|Uikz12x!%CPuhVPuS>__JYyXfsJ=%)ab; z*qh*J{4XMn7n@OCtQYThzmf{SxY4LUL%r7*2f)*;i%}s8sZ&-& zE|iK?9`{Rx<|6GIX%=3D7qRTB+8tpT(zls-By1ql^G^d&@U z|3f_Hz_mG%m+3n6ev9k3>i_)cQbee z=~B0@h=gdkP&AFD8{xQP;&IFglgke+a+qTu)vEn$ZqA(^srO)UG=*;GV~^k9_{0Ry z$PVD58XA6E1=GObZUGxYs~Bi&AI#j+0iXBx_Y?Ud6l5TlZP~H~TJ`6$lUOpoNqFq( zZ25`O6t`Rj~Tz)c;*t=7O1svc0%NcaWa&gU<98 z+&r%wfB%wCAPlzps*{=sl>M3-$rG(FNaeu4;wINnd1@pV3E8&pLp_v&C<@sLb^bYp zA5nY6EWc$wY2Dr1vM?lc59QAPqJ(n^INO;uHTEk_AC7R-YCKPnqh(|qM_h$1sPA8^ z{%)^~tGG$E4X?Z$ag4Z2dY(Uje(Twuy?ZYrmqexu^P^%st9|7CbZnsbhV)r*=VnK9 zXMt5Ky+ej=KRYwsDv7q(gHykX?s={vxHqR50>SUOG_2iyCZsdgM}^PxZ_Nz8f-%I# zjO-^C=aHsi3iS+#7Q*3IRE7m-R{X#=*ANNBm4^+fQeS-{mHYa2v{(qA#xz%3)dSsfv`$X8ihKXfplS0RG`)id52{OI zKWF%bz}8fZJp-t#<}^)DTwD@_t-ow#SJyvE=aW@owvX)5Y))`}Wu^%vy%Tg^*{Aw) z_0&{UkB@v@w>z;%Q*S<3olEuS{-a2iBzhU1$i_)dth%^6YSY-7OJiqvtAg)f$qdju z1nbNQ$zibztdJxI-qS#jc=TXF-5M^YTW&ihW}8IZOAkxkA;kGYR1 zA`qB;qqUs5gWtb@heI8h@olI&XJ%$b8=b7cN`#HU4~(rx=I}SRKTh}ujQgF>dghj- z8>!FoejL%6^VzoY{Kb_)+aB3G5Fw~jSO?gmM`L$kBjAG4M^*;AxFHbtLc}ice|mR* zd5tjlbh^BGcFTg?FJXWUAtUcAQksH-f^dTHEMqviXr`QHyn-&~E|P%x7Kqeu((%EK z-Q|%@?j-LL75W;l)}{AoWLUm~K!bh%m9Q|Q!Pcj%{$@a>6Y0yjU+i?rd)H|evzlaG zP;Pv4Rf!n^waLdTEE^o}GVVJ2^F62V;rjGY*oZg(_yOaYD3?hTgzFsa?RV9oSjQ=< zPCZN6EF63i_REt)jLx4Ws_u9A{v}YAs4G`DGc#`cy>XXWF$yMDArsg-YVxhNyKtee zvO_vy_P}t*u2`jqckoVkXvSOM0eC0X=aDG3lR(1fQi#i&d4Y&rWtBX(@Ha7Ql8siX z@g9BA>O!$oA&}o(lSd==$&?EB_nPOvkXZEf_8u2|^6;Tk;hptftox(ned%$Mi^Fo= z(c`;Jdi`U-)3TIP^$<|Jmbnf87XEaRco`DIuCG0;j;0#L0X^{=FEh}5%*nYV3^&!a zq7Kg1Nc(s+<0C9v(H*?h+=kSApkN zZkBgQtSv;=ZI;wC?hux!IOIjw-%V%zd8$bNx?P>9TX^2&y0aO#!NmUAP#&WkwQNUk zQM-2Vx|7$?M0+-T`qc27e_5-N{E58#Lv-_{f%!;Zo?Vt`<*DTrZMkx#^z~@t=He8) z4yOcdH=F+Nk|H7*tJXfi#rNhne^qw23+?I3cuno&7!$A>5#govkJs3ee1=n{6T=DM zFUH^7wA-vPp<3z!(wBF|#o)AWfeq2Eo0(`y2B1gc^fj?#*O{DDlMh#rT&dk27%Z-S zamMYG#)BfRXV2g|(wN`UXGMbK%%XA1`&qhW!{bA@)Z(}62_OFPmV}BbBWKoCJ%TwT zK`<(74P}-qYTV4s8nFMPhFB(Q$r-|A2R$k>dwU=CQ+eOLTj6%Au*=tf(l7&iG8~4R) zhCS7Np?0wHnL(0WM@oFqk;#!z?0H5Snit34)d}S4&!F+LYPFH)ojYKUy@iV`5~OV{ zwutzc1z`3y(SMvj@=Nk@8gF`p$CAE6)(rhub^{|lH&H`FVcpX(S%^mR9t)jF^mM;k z+sOBZ4Nz!|^ffhic7Dxv%0-ia;p#D#t}63bY1)EB!|8FSH{JT)-6L(7?(p8mmpx66&$tH;M%K0P7ZX`xJG zoVz??m|XrflW{eq`)wnYRNnZ?ng!&Ikt^J*r@Qu=fG61TrQ#MZ&(qO8?aBa=-#K@K9)z#YE;VDwZ=Ijc7mQ_4Iif@B5rMBYarCSrsm&ambi^S;G>GD|rDtb&t z*@_Nm6|_4BS^T=9BEEA06yoptg=_Az7{c-ehP?|-0QVOZ$pR~dl)k^cKFE}H^zl|v zrYPmm35X@AUt2Y?Xx`_+D*?g&efY;xU2YRs^t5dZ5TDcyuqiRxh!7O+*A-Ela$Q-s0)444X=i^ZcA#^IYF12 zidy7BTs{{XSzqEpIF%d~PPLak2aNUEf5-7@+11TlqFcoKr39KQJ8LkTtiiOqF4IpW zDR<=AW4i|eMy{F@qpTA(CqH|!Odp;cF-Rp#JL9m=aj2}Lf>ZbiEP3q~kl26Hh9|n; z`)1#4{WL08$ZSo#$Yk28l39r|!T>U!Ko6{n2$(`ud3r?}iS4P))pT}Vkif0c$7e)?q4kZLq6 zp!>TiM9rIrSya62#hKaPEjwC@+hB7=wc)4+?K_YCsGdc9Kjiy$t2{nHq9B@<1&ogH z9qG3C@@vL}g^7_tC&H}CDJ|%u|7@w)Zm)Es#|NT#Gv7?ip88~r?4i6T0!Qwc9 z=cVoBI!2*aGPl#uj|`MwY|Qs0<0;y_#{#kk1+%J_zKDA$sE<9>TPq-tl>3l(rn%Jj zi|@3E3$J=~$cSNNfy<@A3G+)#>KS+dI$oWzoXJk}7kat*D}ps8oJLY_@E;BL0kPl< zTOQxC5Xxo@?i15Gps{)T$ZF{3Ywh6eu*VKQ-$I=;cwwZ}Xk{fYg@v8h-Mi~4DK**0 zshOC}?2=0?(k;gK2nnej|J+_qLsw15$7K5ISh~rp`p{nm1t|e9AO>Ydm zHiI9Pi_CYnvynH&|LtY+#{Xbnj_#**Q_lXTOJ}~CCrRwT(Ax!5lA|7CKa56^vN#WZ z(zePT{s@=6WRnk5K0XNwW8ecvt_z65Otz}{DvT@m`7^%{uDm-?O1L(A2g2w*i+Xp< z6QM^M3frf_Wsej4C z(Hrz}%_yUvil?3aG)YUr)IcpUG2Q|h=oWT+y?V|t-mmeeOD?M3hRe5NnB>*8*J2l@ zCkrN7Nx_ zeZn@P+@vUe?b>!{N!;LlKYt4x;V3OEo7)SeTk`l9il01L_X|9ms29CD=TV?wj&Ry1 z)<3o)2UMd!A@q{VK+M-fwG+@?N3Lv9Ne6K%QL@@aso`cXDRtUV}ZoYBDWv zuZPRrZj{9K^YD;E_J{IY%;qP}dhs&#+GCGqU9*masvDwzhLN z+6cFdK5(Mv*B=7_qtU&Q#r2@Kh}Df9>t0_J{PCF{ZE1DB4sN~rd)rpe^vyy~WmvHn zLJ^(Znc~>k!vX?*xIE%qs-GDVCfHMIFRP=KHdV%G#HGAEKRuZxi|YqACEmbc(5F0+ zsNmvPbFOh}_|4VH-)#cuvHg{%6Ofnp(`jVtIi@56gI9kZF|e0cET~H}p|L*G#@3e` ziePrVTlZ)?$GVd0+djLQe`1n}UsJqiG$Y=wa|jTct7nmb{ z)s|E#t#LI9wh*wB&Wq|VrPW0rNO6HXwFU?Bq5n9$Y2SHg3`4>UpY^Pgu*6|{ad$xC zMNQ4M+_p28it*H}Nk!bmCrp0cUj(<8k%7Y7bamXZx4pfgwtT9^_v3S)#P6cYEeB-* zaqfvD@ja{Ub9&~qPGjnfXE}VX9%T=9G4gQ|1asxVmg1z7qQ&Xwj`7KX8Km+33H|je zXiRA4>z5QKe>;K}MAh-O(lASm!SOsbK3vT#iYZMIcLG`nF=@IpGb^iAZIJ0Sf?VA6 z_czmRrR-DDT2-G~koHfGnqQJ%_w(mZG_~Z1n^IaH>U4dUooc-%Fv5PRPaxMq+abY; z??}g&7Q1-;og=yOFO=a+l6WMFMsoWaqgHi^Pp2kF`sl*-8lR~v#Rni4H&u@}F+@7E zXVrxZDq~fNvVSmsaDt@F$Yus}_dXdpJ=y!Yb6FeVaG?7M|A!*)_-<3jA-_dUJQ#Vm z(dgy*OTeNpU3n-%lf%;17FeC%(o3S)`z=TIH5ChsCCi)G_l1Q-l8v6+Uh}WTVXm(< z_8qnEXZQWUzBBnfs$QV^(T6|#{Y1NNltp5~{8d?*&)C=)=3xNmeDKOufrpB-Yd&nxrp`>({DoHa&Sm3%BL zk1k(cBUlebnw^>EGyQvyKvE&q>H!=7wwG6`S*pZve?JTZe|R&~lm9KDzOW z+O$6F4vR#@nJqn6$STBgdY~&oK_t%mm@${C@;9~p%KOye&@3;ls0G>073Y*pkwX{K z8xPN4WF~&@TcdD=yx{G7y~`Z`ev`;EvB}S2D}+DOxpJ1++S(TTUNNc*76{9=iKN=h zWS(j-@b(OG^G2jpOK~VGY}vG@fO8x*s*N_()BK zRtvI0^Iu5g@9)wqX({kNG;@>ytqXfAdeCbM#x z!N`L3?&f^}ry5_Jc{@tl=a?m7clb?Thw#hH?T762dSFd7Gi4ii4lPR7+zB&(QJnxk zuf&-z6{_qU914Y%0mm|^XKsYW#AvIDaM*ruxX!||rg=M!7h@naNxl#G*?S7uZjE@| zQNGENovl7JaK!GKpYW=cD`V@C{Qr@}LJ0w+=n26YjZzz03UpFzjF@`ZB`w`n`Pg-u zLzLr~K1>astb>~5mV9@Y{wqJz+N{y_kqu)H{8Uvs85nd>HW}ta zIe)KtiDP6m+Urs=+mkJJlEZb3(|_`_rj=FLY{;3CTOB|WY;4vvQGUyH>kHs(;?RRr z*u(T%+ECd;+uvVo6|jsp3&G7BpS!2G*|)vXKmX5>! zYMvf`;qEKre%9gpG7ikjFFTSk9Br*?y2YjDq{6sIrU1y}VL4jP9+}HCOZzaqHH@;r z&2e_H2doF`8RbXiLG9($A}|1%rXAcY;b=NJ^0Za*-n|S+mROjXHB(GFpf?Sw-<5oR z@bufh2n{cjUC~Dra#+vWRyS<=`SV1Q0i|m0iF|5Tm1rKuV_qiJWB!lgnD>c`PfZLT zZ2+w0jyUR6s&Jsi#12 z@7R}#VRH?5Ngp5X+^zYchT5^ss+ErWL})=(yuA@xfkF=agQkq zI>n6}0}s`I%HT8-d~`NcbS^~W1V6)nXKA}v2Ii; zJdB0}jQsgr$Cp!SpVn^r%FxtZ(yz-f2N37|Agh^M9Q?-<9UP~84ZFo|loO4y1bI!c zd&F(~E-hRxm?Mg=r(Gy2nP2i_`CbJ=h^W<>HiMV{@|c{MKoey6q19K$o`M0~&61KY z(c;=|`R(LrgZ6$SNjEa>0(NG)jTkwvek=4#=%T?>lEx$J))82BboMH7f45c|7H?DXHLzr*bBIL2#J?j^|Kw`0OZ!PO7d_2^>HBA zO)jCwxpxWy^AEYiHo3aLE9mIPvaAnr!5q38_bW@AY>HKiq8| zjG|pnGN=orgQBt%|O#xr@zO@a^ytsL1BACfNY;y4%PA^(r&}aRfnW ze~65>6B_&3pZSy&VdyLUF50FJ8@sqI2MdT+#n^y)Z*HzPI!C~XA;t&a#mzOiEl#}< z+y>R7Wx8b7F#H1kx*ybIBt=fVfA{W&cj(@M4}vE-cxEXnvu4WW`XJ)vDv$Fnz#}sn z(SyaJghU4G8td26U8}BVf{4oZLbm=<+_~|d4D^W(f{X;U9V~O-OJ9{e9FD>7V9@~! zkTR1y5|L~F;(TOP!v6JZUdH>GcGujBl$H3BP@VVqV4+#3LyzbV6^ZlPd(?L*PEQT4 z2Lhy@X`KgRgV(0fSZZ!&XzpQa`x4-SVXo^ZN9oypca1W7kMuO;=cmbfPm0=Ilw!hJ zMU8l0sIEq=>C2?bsENvf1J9gCYUL=@yS|&af6=M&m{hq@((^k{B0)*nY;?(_Hl<~H>bJt@ zlu*$>adBWG$_Q?`($ALhZ+y%yBd!5Jffb1Ia43iG6;XEQ1T7cW){;0hrr|e%GzZLX z}o8?Ec)pvWKK5L1?cgM; zeTw(uZ^sAM+z2S%X{09ew$880b@(-^zZ9oYD^=C>cST zsHxG3a)Fr*Cat|A5}>VPBlRza673^BJUlqOy{1M7H_+2FvGBjxB-(lZ9Mu@++vAQr zywBX&Sf1U4g{2qig<-md{L!Ot`yQvgmSMfSUh<+gE8i(AbPy@YxgzT60$IezszI)L zA8Vj9BGRc4r=6(dFfnusT!kjaCiObN9FGgl><-Vwo@%#^lIP2cQ>{(b6X$i^zOD1> z>d|veGt=+~PvpCHO<$F@_&hw|fHS{;e~49H?vD9BHhT|xS{)=n$XkCQQ`HS+6AhaOL0H`&u8 zCGdY}0W`0b7um1E9sM=^%x6p6&y*XlP%}$26RjK3+3_}h+}KwlG8!FQ>e4nOI7A(S zixf(V7iZ9TkyDaD2?hVAoQtaE&ABcYdZ#eZE6|~5Vz`Op_Jb5rz7Gz$6fJu!^%FMX zoCvzjSV%C-IKqwRp_=gREZ+oV6la0YpzIvPF$~Jx+uj$}Z+_(9foBi;D&4JeCAvZ{ zobFIJu}qEUVx0bYTP5`dh6Jh0(8&}_OMe{?vk7dS?tdrK8F|-N@(8Qj>oF9puby!3 zC`i!03J6_RAJ1oZ4&zq?QDj|Ut36tK{@P!+rmNdfndo&6b$r&Nf2zQ0996!5DtR1U zv;kLxx=_kpc+{HUS@6kQW3D(VcPMnC~ODfYLK@+v84t2~2(RYbMdy!lE0tqg$zuDDF;FFflp5UZmra5(18h<+bbA z^vh|PI8Wv0fBGa*{s{d{CaUlLJ7z!pzy+s4ot>W|xM$BEVc|nEbj2fwx+rL|panB+ zFKjl-deg8bt^eU)pFuNz`~Is3it+_JgR}0xq-75MMx)Xy%fAFh?@e4mDYxSC5QK8k z+CG!Z_>)p>gJhfSZ_xAADLxo70lZ5Toi9BOTqi~Cd|c=yJXGJ09P!|AT`Df;|8zLa z{i>{cN4@aB^pv_=ty@dvVea+HeiO{SY`$g6O%8AR^7L3A?w&ggg3r*Y0he^D_2IB? z$_5VUJXJECKa|=}l#ofCv-}3GOYOG2#QEYC3Fyf+5S`e)xl&u7;l@hu(9o1Cg)pB0 zgjVNy9soB4!c%XqWE?p7bT;<5!?~S%$JCsiL+;e3t!SNp`9xx;Hc?byD4zr`aFt5z>Yfzq(quZWAK4|Y#~5-}PdsUOZzpFJ}oqyo#aUFe_I zp-nGjMIpPK{-8_3ztvqzme3?V;W)YU4hRM9n!WXhu_uxL3K)vJO*&k2>E+w{QwyXo zmuKFZ%YT+gDHZZAZ_U%_xm~)r28)l&_T7h+muY#y|Ao*Bx*n88u2Mp0Y`3fxF#opZ zq?!x=oRJ4V`#9w9IXxY|G~=8p$v^nUe7IYXEDAx^xATPw;~%nBHGw?l|2&cp38@1M z$?3dxjEj1^r5)3)@7}Wf%SjawtCPqXiFpzQV+cf|9lfjd3fi&Xctyk zNCk^9Xu*hKN@|=^R{!VX>Tbp2vX>|>7F68JLdKUC3Tz&ew{y|x zkN*%-%~Rf*r}%Bl!DT4p{I_!tB~Si)`4bf}VQVMaoTf)6tM9J0{IBNX-142E%a4a` z`K36|q*`43<%_qV)-F=UFYT^8W4Y|toxi`rX(5y@?L^4bitt8nI}{23XI*`~NfrO# z_vjLvcDS|Vrj+tV$hR?UL)WsVQ6AMwouw}N;u6-}lj4wZcSt?>Ke&1ZZ_i6!{t3omSlW?seH}W| zJ3M)>mS0YDFL#*%0C`O|UT(wwt@QI-)_s~X`M`hP>%tbl1Gj+o!z#DamZkc}Dx`@^ z72o3C>DFNTYnzl^y4X-ydWO{-4xd@szCz68*78-H3kN>R zB$of^_lX@*2)VgkVq3l$Rq{rXAkRg{sD4F6jDp2>O0#3saw& zXBAwwpf_Ih$+i|H$@UO!cvQ1tVI`iZyVp}meP<=47%t;2fl1jTbs($4K1&Uv0Z=@? z#;QgF3WuKMEyQB*`0Dxj^S8={8Jf%6$%eSko)S{p9W1WIQN=0}Y^Si&^ygB$gH4Pq z2hxsaCyhw(ct9d>Z5;7JcTtro_2tR)5qfKCTsG69Oc-*RY(fZthISh$f}?H78c zaH{ta-79mPCYWo3xe(v3;3=hOehzhY-#EVysih_qAKtHF{3D40sZEY@I}J&z>Bc=v zgEoBLk%0#?)BkRL*2|Z0TJmPypMx(3zO2U9F<66sfsTeHiniUYe0og#aX6tzO{OK^8Em*GxN&E2bg<$jrD$;u z^~YlVF-p;l92E&1>X(We7kns+gh)6RLrV;tE$liAi(BV6(BYRWBtR=TAobh`2Bww$ zBlT9W^j7nKf;z!OEzndhZ$tl{U{a-MY7SpXHHuj;+0SAz>`Ku;In?QKJ6dtXC<`O7@IhY30_BAm=iE!+T)Fk}6 zQ5-%LyI=|aBQYN2+U#WR>?C9u=s|{Ys<1@bLWHfZA`vB>8sGNFsr0q9?}yBpzUQEX zz;QE3#Oe_}F(m~|>*@sU&-wW+O)MA8(0qp;Z3e15yVkBQ!n*NO!m-M|4&6FyBS!rE zVENgBX;+mW4GaumFB@|S&E-i<_9iA_K#6?o4%-h&mx=agguWpmDMyW9Yu_pjdM+g7 z3=t_{*!Xk&kb2&YbRMU5aqCO{AY5tEX)d%@D0n1DQI(@Y$+yGrgK&3PLH2*kpdw?E z1mux&XE5dOpMAAiH5`n2JqK&+eh_pt$0>Z*#~Dbp{6bm+Ja`Xr#nYJlzpaCP30$gvque|O8fFi3Qq*xt<#1kNY0xn%7uHq)6ZL+!bz5@&F|Ll zPh>v3)wb;kk7pSa9bbV;_qY&KxlVuj@>=id6_pQ&a%e;HYmH){`wMhwr<-PHVzS~f zge6*$QPXRM=2absQYpr@#LQi?1d0nS0*%n7cGwW2kB^e}yRS#3eb9JkqGvATVk+dn zC-~LFG3kY1w6wPHAR#Y%877c*kS&VzAN1;l6xda?4?Bj6?rjr|evR&;HDvvL5Va!m8^G0#P(Ny;jhD?}(q+uI?pW`53aypL#FPN70AND7+&#(u{-urznHW zmM9auDG133n?%UP9NNo{V|ECd!#*D%1&9%^LMLL?M6kz@?><1R>6p7(NZJZM{D zSj)9=6X~q6hMUfHO125Peeopmpzo+X2D?p>7sDaR|Bs zXxkoLYGt%f=D|2I)fFT7+B+^zVq}ir&iwrRxX<7!+hyHQBabKLMuQ)zcy~;ioL{Cy2_YgE|0d(ykLu_VpQku)l^PKwX@XnfWPgS?6tc zCTaF*AxtvY!o)hQ$K8aG2aXg_o1l@Ii|Ig2MsJ>`NNL^|=;f%8_w6w7Req`j1U2_1Gnj?0mgqW@zsO_-?DQoG@>Y+m(fU(#vr4*BSZRZ$2ljJK+x zc!vSZnM=bNO|I(cG4wK~o^cw_c|VpMIB=kZ4`YUQ%H4&;SF8P0&Jd0@!|?f*kBoH1 zkrCY2-CF6dJ5!K)t6$fI?oOmr^pU9j$FZ&9Eqk$YBSS-#2Jiv#^z>8{vDHmAy{8pb zfgs;ys)-)lnY1RnlDc+ZQ;oCwDWYa^5EQ3aOra+Ud?A89L0UL)fK1vzC=elno3gS) zV)R>Nq^5j~;$0RAJU~?bYY4Qpw!!^DBN@D5q%SwB*z47Uc}SGUEL%g(vy`Gad*5Ha zP`bzkUaU3PeBO{g<4|99_m5hBZ>sxSmXp%~xmUiJ%D0*-$2;_CWu9y(FUolR`nAQ2 z2=vl*hxgELU}U`1T_ae01W`)!Pz?RxD6OVQ;FAJX55h)VuK0waD#EF4_NpfYm2a{{&L41)2eV$b&aDiiYo z&&EQ=eT+TQOatW7*gKe}UU~1zf6AAleWcEmJKMy~9X#FBRVp|`(@4-G^}ya^<@VgE9`?qV7C6 z-)zZKCexu`2ob*yqkXV>yr!10(sY+nrZ?^*XR{U|Av-N=ssT+M5SE6glTFprQ&Sq0 zYu8qlr!$-ls53BlI!k8hKNxpc^}durg__!H)u6JFqwcS-9j6p7$FKyNQ zY1c(4+TZ|VXE6(z9L88xf15|hTnPIb3{$wzq}$Ws2kGTkHlB^tleM^$p5P_Ot|p&2AMMMHuF1CR1)++aOnXSa>b|aFZWZ02VYs|BD42-X1 zoi#{UU7Vd~ER{y~dl^0MfO$=O()!(7h1XjDe7}adCz+wES~QKUzJ-yh6&KT5`ucUc z6<=U8uVja%15?=uRwVY3(2H;XXsNH%Hh^-1i7=12btCK5A?dtQGM(j9m&ku9;-!X+ zf2*}Gr8E9#t5TD+9LH%eq6rm$5~_zX9aO{w^zj6XM{xr|tv!RVr3V2M{6D^7#s((39c>+jJgX8zHj$8Zf^HwB zZFot>A2z8uLtl>I6>BwARRI@e!ZCm|6PSLXk}j{9dR{oYw8@z}!2}y3E90q8``OQ- zkocN|5(TFZ^L`T7)#^YT6JJY5LflbQiJd&uu2B7L+qO;fvsVfz;y|shdOOyQ{2QmIc z3^Uare0i$i5eT!^V7mFg-z zFj+oUZ`o5OzSs-Ea6{afbSnWG>1wA@LI4=q!X}&bGGAY?&UYV2T}Ao;EFowT3m73r z4v0_dusIAo=&D|P9pCMqp4Nm9`a%;r4o9bv!NLvTdQ40k^yqGrJt9%L7~mJB1@)5} zdk)2gg9i?HZQ8f20W{wx8793|dU$0J7p3=7kwhK@!oAVOAJ?&0A>{5vv^!08>VW6_ z90+G&Z83{^%9NCpyNn1`J`xn<9W9-m@xMM}LbeFc4oL8-fifQNjsA!$k&c;g)`0Q> zdpxVpd9okFMpG?>J6}Dig~yizpX@}MKlr*CtbHXs!`K#qOvd!3tay#L`pdbdB zhn{JG`7!7jqPFm|E_}d8QdZ}F>1KM{HdMQ&{kX&HX3W(2WYx_>MITIZ>H(e391`xk z{MTv=F^1w7AtGD1p1`OGV&GnScUM) zDEK>mP-Wm|*>Qug*KSQQ*w06961hVC&(8B9cjr~hEErdic2E%+*(0akml!W10?!)c zs_+F$Fb%+B-D&g+skhW zDIn}Dp4QnDvoPTV1ID^Y{3e+U7}iK1gj}M?RFKvGOI$XBjY4vXw=8ietU;6X7ONni)N2*5BT4I%p6;_jC|@CV)@A%Ab^j<}v$=BI168YAqF3DFpI?BQ7c0z{nrDkowcvvx)rmfhdYjC0IH~)f z`!WZ^g+;{5KDmUIJsaJ;oTWJT)l>A`|3HscT=pZL;Lt5e@|vU-JO0-XpIZni9yEN_ z*T^ooy?0By@BZ_>&A-+;w_dg$yoR$G&C1IbKSj&E_RrUfzeKZM$OKQz__m1cU)zA( zBw$7JpP@JoO>f1g4AWGE#pL`8UwlWzqb%!AuefYSe6zpq9-B{!iWEp%7dvW8&$PI< z|ASSr&puMGKU`nuo~~5D=ZDyK(h_U6RsU|Zvt^ThTk1j*5BEe4J3+$n+iiH>?xgwm zE?;cq&p!=HHnfG<7+1}qDlp=%T17;g-P1a|?^-u8J+`VYoC!cB~`qLUIJb!09( za#=n5S282|)`mpi+9kcaWPq%OWZ^f~+(q}(0}gPleSLjlm^U8!yVYvHiiiLF=74El zA`qx82p1{kbwB)bH1Sep_9&9du3 zqBPrx#-Et$)EIo;4jeeJ6}R8XlT|@B*D*eMA+%uSQJ5@uu`5fsOdnspGMl&EU)r9? zt9{O2=IxSS8FZ>r5f8qwhrb(>LhFdEv~0&TF$e^i7?cBuz^m*3BG8PijmDQ3e^iIm zksH6&aKq4p@IbO`q4xYY&(rhFurwan!mU25KkZ?bb-%uF5&+<~pOQpM2J=l=h~o0~ zx_L9%_xkfiRM&bH$p|*YI{xsDD;JCz9c{|Gu3oje*|N>uod!_4=#PV^j?8`h9nL&Y zbdzUR!rCn+@KW{{440^F>eTteY}X{$-H?9hWBA6^*^*Bzb`KHf2-e%=JG&BtmvG2D z)s^U}fLu;okJ=L@`nygp50>sHDY0S#>l|Imj$Aw0cdZI@SWZ)z6xRSIeOvluVKMOF zBRj$~E?{oU)4reiw_%k_fbScPr54Ws2T60jv;dW(zxBLnDh$20O3ArhNo-IsB@nkt!?Nwcs6#i(~S z*1UW7uE$h~7u9FT0e7W^sOxS^$sj<5e{0+42b*R0NbDBTkG>~DOF8lu={I?jWOyT~ z$>hWc60U_s_HiYV#j0-JwqSpJy21jZwz^@$UiOT2{q4D1ztjFcA+FA8h^q zSW;sZ#fUuWsz)C6+>-tumej=UPvGEPmy(&5*8hL9q$YgR9kLDr2X_(X{g-Wd-oQ#7 z!GD&32nfdOu`x#aX?3pdqD=k+2b_Dyi&1^Mc03@QaE~B-(;pH5q*F1#JxY3k@y{XAOG2Eb6?sC_*QBL=Uc}R zl)MW&?Vqr=;LNu0FY@kSyY`IzNEV*^mxa{EY5)55OAhl;25gloyr`T={m*;NGbGdb zV@~nhr|r#kf7jzfUiO29k#ebTiS4+4{pZyz?U-{X=24?_b(J@|A`itfk`5*Y$ZQ4K z((BJu3%L|kxWs&S&-C}J*_u-Zjzz-epl)nrwyH{8HtY9tH?8siba#_@G;W~^Ke%6#{?D|6 z&Bva!Wa+9w^djHuiEkSz&i(c5UD7|OA@loqamUQPopoHGIk>O)W}l1BCP^z^EDzIj zuX=&SBbE1@fcPiJpD)77h#HC1hx^1~eKPt@{alfcDhTSMKX31fR=gA~cQ=PnTXw;T zqZc{^tM@l;RKy3S20!PD!YXDpYqutt&Mn@!x8MfAEHOc4WyMs$Xcllqb<|xGu<_jQJXYh>_w-bnvi%EQXu#7=%N@Wh47=3I?tlDUL8^JRu6KZo zm!q-qEyB&^(x8@T)mCWcqaM+4N)EI~s_03QV#5`9KOBYg!1KaK^uyQIsB56v9MT){ zuT$}B14&FR2UU25xWY1BjBze$V`JmTez+hbbZ`M!^3|+O*vI8Vpdr~p6Yj+ag?5a^U+Az9(c%MG|@Lx`5~Pcz#Zk z;s!PNE{hrlvWu@wj?vOUHue(sIvG}SXhy~FvpxNK=*uqfo(Y8mgdU>yj>+=Alipe2 z;ac~8)2;Gf_bMwZ;lwe&jj-fU>w%P#Ama1zaEBcT_2q7Cl>GSFuORO8%(N5g=$TS6 zD;U1n72aWO{`u4IGq;R-T}(pj&)(kCl^C~#5p``XEz=OM*%e2~^i4I*J{3%lJZ2Y} zmE}=h;f$W@BhmvHT=)HZ@v9bZ5ktoo|29ZF!k@N$Xcob4gv2OFEa9hX;rmaYvf&?M z-zl7Fv#tB?eg_zZ@*IZ=oE)!h4M^~j?4qTOuqpsat#x5`T7wTXi!dRb1H8>_WVTcD|@4Ox2~_u zB~8kcKRo&>e|9~Lfi*;YO#rs-8Zhoho*tudjK5#UZFfYCaD;$$lfC_?2Xw5inWj)1 z`xgMF>=QLeZ9gXyMDb%{i1C*tO>0L0Jcdw+fQz0u+@;+W5Gl7FeC}2-38JL!G|d6K zW%IP&7YLiZx_(400-(zeh*2x}C`b<;Okx(#LdnMlIgD|sdcy;v#ma>hI zgrBR=*?1Z(S=nc5Q4iEgpgHhPb{yUaLcaY-cL!MPnoi|!*Rg!N@Z*5bE7he75R;gu ziQBXkV?Wx(Ki>~sI_f5=m;`yoS-Y?GQ$Hb}l9AC}=h&lH1XrLPDJf{xq4&yd`1(mF za!!q$D9=;1&zlR;K;f!@A6{_zv-Ta^e|dHmBxPyj$20T*cu~tarLWLKTVRkbhpAMP zy$MMp2R%m2kH_l-z=fCRi#lNdlAxKTO_vbSV^x#ctRso}Mn)B`E~&L=$v+T%4$lFvq{8W;iuI5ZrWcF4 zOn|9Wc&^@CV6cP!lvBF4zdhP)QiPF9g{ll6SoJdgTV*_%a_uziZMpS?lMh^15S_*b z^6JZudqixOh$zQlJ%wt!j>$fe4%1`lR!vOIp*}8PBGr$?pD%-TWPSP$GT|#C@aBN> zWD~Qn>Zb>zaVXgC!GI!OhtB)-lUp*LQ+y|Dj~546@v3|pE-I>z-|=K<_*3M6-5=%9 znz9?>ESh+6zp=FcP)j&*fAH`hE4`ryV7`>7;EI{tK+Izzpn-Hiit=mDxfGSAf<$aB8~I`>5%S58f-~LNhPEk zq#F!MK%~2*k?!W*L+icY-}{fBBMfI|p4iW>wN|xBBf!f(hHKKxfBBL$*|q*YhvSr# z|71o2F11Vt%Y6&dT%ZoL84yaL8VrtmUkRm>s@RB}+L#X{I-n&rDC()>1$_ zIqZ-pqj;OkX-;wROUujWRhHT_$5&yKWSlFhp;I%A4g8W_J32fx6b&F^BR<}1GtB%- zl7Jlv^t73|IwxI{dd}~yWxv$k1D+Vil@VQFj_b_O_Y(4n%g&vt(;zY3%cFb%S)HxTM_JsSV9qIP zwZHma%eb5@tZ8g40qU8F?y+jf1yu$STV?~91TeV*k<&)*!|^tsQ7u|_tF?1@ zc#)vvyuRq>44Q>d{mXM;{{=7;hDB*&yr3wnxFq&bu0c0c?w? zSpWLM%V9K@0O2;m|1(f@hTJX7x-!h#26oz=-FZ9&ULW))J*O|2CP4GZZLD1Y^nty} zl&0m0Qw5o31JQy$&?IMp093VT0rc3GGpilcl%&+Wlx78F0zf%0Q3>!V0Mqx ze~5Lym(Hq+)UZ`wbwImo9B`xOL-80+Xv^LTz z8Wmlk%pmFfY4=wyC$Y25u3fIE7k_r*=&-*I<%5*hhIwx+@o40$79?kApv3da&CT3; zQK#j`<>h#(Xh*>7f-D8>eLc&Efzc_1Wi8=uZo(UDM-phyN9Rh1a^Gq6`81+~I@?)T zTejwR+JkE{y7c8sg;Kg%ktgm(Vrp-ypic`(Y3$OpiyX)0U&hDlhx1iSKBDW~dZ>#c z&I5Z%L{We;3`b!38g7M5fE>w#Yrr`NA)BYza?A!TB7)xIQ_C~U-R5GGEi zR0NAP#(O5L9NI8DN4rzhq`)=Qsy^#TIt&?n+rA&x^9@F{y1`#=KbBbjoy@_<%Seb9 z4=rxYW zkd8?i=pK@CbG`c+dQn7}6x!e*0(&`HpP`RsY;255a0QL)v#IwEe@Rp*lG47WvA)U$ z<9wM{himP1IdQ3Q4K87~A}s(;R^|kQnv%P-1+af7)2uA@>$PWwCv_EGnjQ)%0=Ek2 z4Y_qAQFb7NAd~Pw&I76vCr{)hxk>=&iy9RGDZ5S}_T$RXQDpFh)dWP4LB$^Rc|_M@ z@j&aCsy3ML{`hy(hY#Pb>3jZBoJ3L|DNZgIGwwngz6MbO;@C%HmgZ5$9aSoD|QVW#4(H-+scn}iXsj;dpP)+i2gFoIwjpttksvA1|4 zghkj))<)e>@)8T2yT{EfF%KZu%BiWMO@=VhYM`lIhc=Wt51{%3OADNTl#1B`c~(JC zF8Rm_+z&EoW9(3r0(Y}_zB_%SByQvgvb56=+ zYM{hzNIw=#6hgX&x%i#hm~(S2p69vy%wi2O0l{LUY`nWM&xTzMR|YURLXrQd>pGw3 z#m!%CrT>KJ!V*a>hB_YvTEnA_)SGj#HleX59%|=}DNy`B&u-;?1B$Mem+<-oA5e}! z7pm37+VW%sF2`(8C|FxDvTgp9|hpu>L_=i-X@^>b699CD_Tok zpBs5A0op=FPk~QJS(e5vEEd>ZKp&Wglwx{jVZqeW5~%u66!RqnmC6qDHl6yd-HKdR zV=u4V(sbq8%)gKAm(!W)9Y;BFok8FPG^fTiibK<@!|%jq$2C*XbW$?2Ui?Wp#XY_tsT3Gw4B))Nv#}Pn5g-fx#*)D{T8Hr9X#Y4_{b*JzJINN7;dw!^F#s}W zs($_eHNk@gzKe3s8AzUh6#;l@a~N9zli|+7oATn8*^Ew&1h#{MsV_Ldd29@XpK;eA zep#Fdk}m`DiTWsx`R~*kkn^FZF@f=Kt7$m{Q4mQ zB>x{0b6nW>qs#w6C*j-QhsHQ8>sQ{^Cq{p=qm&lhva+0dzyhQXc;KCee`W9ZVC>GjR5aEB_CRh6)?o6|n5M@PJHjZ-HXCjz7U_xHlU$wg< zu~#!Z3yw7o?!g-j;i+o>HQ(C_&7TNP=lIhd35#9d2Tl8oD%Ky@L+2WlpDM(-w8w)w z_neU3OLntt+ll@^h)Y@G53T;sYUO`Wh_VzOV&4kh{`d!}ZdSl5AbWC9G;nKd`Om5* z?moxPl6Vf&(_37-&sPW*P#%a$Svr3~31Cma?;_Nkkyo)I*{&W~rCjty#?t@gq2)8% zODpKm1?1uDE}yw@AwCbXqPyyZ+hgv0=|oF&ox2}q-?J};GNpPDzC=S&YOsO4rf{!= zO&h1&e;pA_Bmot&T^WX*Ma6ym$ktz9d6xC_6x@fbScwuB3d5Iiw_U!i2{C)ht)UUI zFaqM{$g^L(>&&}110aaFCH%OMLezvj(;2jx^E(BP?Lmp)-xo!!W5iwD`AcrH<2R6& zL_mdYm9bC%@tmR-O2gV@sxd`dPfX* zDDK@GMtyOVDG@@(?+Q2P;U6T$L|bDC|G2p00c%x#dmb=htE#G&dRFP|JmYICvoWyazkLd!?-Vku za!mB~2ThN2=U?)Hs*A!}E|@IAhp0uP;o;cOa`xd`z>E)_Z=m=U=VxPVJXr_rfs~Tp z>g(%utt(5{``t>E-Hk!^Lnb^As^;nK{9nK9&ORDO6?&`6E0AkEbqboAenCg<+?V8f=mH#5!KX%+ z`tFr3RRn)MI9v6aZUCCg2a4igMsZk}hw0DnKu3|msyH$?NDOS)1RFg)syhmD406uB zdxpU7kAWzzP_b))wo(=Zw3~Lj;=?B_t3PV0Tf?zu)3=_gjs}h_qr3lXJ!ZK9TIt@= zwfoTfnz!dE;o3KW?gvs2rs(c}7F&?@h1`Kr5Z>mk>iKQi_q`z?p+WEXLsJm;wIeLw zxeE+;2an(Qi(m%U(jQ0hnR0Qs_T+%Z4P;1n>eUAa7}-gPjxziq$!j@q`2((1X78>u zPR?fK!cmLY0PZh^Zz=rOP1biZS2pMI@rnj{We9i}QqdS)z zMAt%Zb`(;GC8RmLRYQu9ao+gr>-Q^w+$7Xs-O}y`?B(9jFud`5{z{hYj7OC^jKcMQ zwAuc6GEhuM+t>w`>^u(%Vlx?QeBVg@|Dg6TUe}N@_)>Ls>mmOSs68xbXqCde5nmFp zf4l#L+9Rbkt#Gg;H-9lQ|KVl5PQz%S`Qs=*Y9hb{~K%15`M_;zdao*s8i~^z)v3(KyUtI5HvBhEgx35`C}NJw-j~%9E??D3PQAd4XD+) z{uh~x* zB7EWfR)KO!sIPlL9Tez+vmZR)(BVE6|2fTqQRF%IAm$Au8ir}#{m0n>Iedo9O&d_3 z_6papxi=O=b`+aMB9c~~+nzq_jU2A7Dw}DNtJ1>Im{rM&sUw`iv#srIXn|}=r4ULZn zF}K&>3#8Q_R7Xl-2M#Cj6@jPwG?D}i#q!kL+c20Ku1F7Ca>?Q*G=cp9&Dk3~PE4WC znUj_4btPvh@i4|zobmqJQY}TWl-9XO?Nl!R-fNuvAV>l!lEHC`7*J`EhV*OBH6AKL z@vnL&vpT+S124pGkK-S$1>D6S!Gv8JHU0DendcqQe@_I`S}+&{ec>gFHB+twJ-%KN}?M7$2NOAwe)p_LXsE)oJhz>eIyzGfAbti`NKI zMb!ib*hSH)Gp*=*vxCg`>l}1bORgVMI z!-$+3Ktcf##$_@5bMB;fLjedpy@!Ghpi#X?O_~=#&j_HUvmlo_WF6Dh=z1D%!kvqe z4U{Q6Ggf?%UV!BH5&x}PNb$sxT<~ZB7XgX0{F(2WMC#SMxd3V+A|hR7W5B&a*{u`0 zqM5v2H+UdQT^KFU$qs5^R~v_t#<7574+3gL-<9lsAfi9MzeXe}xwpGpwGoPd=h5Z+ z{%;kossaDma|{|UKXrV0b^_YUKgUG(>a5v(nS1(D@V+f*Kmq^V?@`DICp~QY4&r6_ z1Ox;`M2<^Sy-e<*?<@fA5rAzXPZI^;HAo3({CW^?^%p4#JFcprC>g9y4Xk(L`=j2k z=mJghKw<{WP75!H9bbO|XoF>XmKl5{1Q%H#_6K*@Rc;6HB7jzF0eJ#V^O0(=!2>*s zfdS)n?bKq_s4sBK6@l{>SboPzO*ihQu=eg)?`)Ag$mplXm#H?5BFjBw+2(J?)8E*+;yhED5ly|>;kPR$2S2U6xm1zTG3^YzXRSzA%Mq2L!f0} z7_S>hj%)(R3$l{41K)jw<7NSQ$^klaoxgf}5os^Tm2PeV0XiqZMFDm@1Vzql>Kiwv zVxxoW#()UUaUy4^4YbPA6^QaIMrHtnN|nZR3+(12fJMPFmvT!^o&=ZzQ?_qJDWDi+ zV#M!^`<4Jo9su;jY!)$^;S#c^0VyZ_ke`inwu%wddl_hTKLBi}w^3Gw22eBdt^{;R zxe`PUa}tI^9=_D3*49t}?%mv2ECL|4prot^qNWNMb)6GuS3ww3`aG*y#6P!^Mq_An zP7&q+{)r70aMLhQ6cy~SAcq}Wq8ND7Fz!cN@(lp5uK)_xk5a1CzELq&Ho5C4vAyca zm}$VW0ws0=;CwJJF#K1qqH;hrW+3!M*oN(6!h6JcP=W=d!*5WrmFTp8D5M2J%WnW; zUB$a8tOW{jwr6aw4Bn~2`!pOk@Z_cnDD{=VVobkb?nXrixn{ubK&3k4DvcTJdig2X z{Z|vp;`rtd&Mr_p9lsHX1BU|J^jA&&{RPO%TU=8!`S~bugOSho;g}gfh^l0pd~R$+ z9Sae~p%L=NJ;4F!q2syl>Y+2H01zau6Sz|?3v~NP_jdvk9={4emxe+GBxaj{0RmX{ zNIjyKOaK=F-AD*{^a_2+Qy_`BNU#31QRjPm8_4M;D%>*9n<2WU24)L@@qTy1$%7x= zeWEX@XAe}0J`8$H=Gv%+A`mqIl&hpu5Fu|s6X-rKfh68(qT4MkZ#+S@YZdhQd@kEO zdZbeaTZqaT@E}D-65i3d5`eK%PSp@(*3-Bj=R-jvO%{DdLO0|JkNNfJlg5y${(gix z@LNODcYrD(B6NVn*293s5{Jw&;Bwed66(=XK7ioXv(Jbbj-@P@u8Kt_`FBI?>`xx7 ziWKM>8C8|D2=B-9)RO|j?XSSW&=3u|czoWEB;sxjY2x80ufb%SG;{>~j4M23=O(%Eng7Oe)}yK%tPOMVOU1z8#jOzD~Ev2nm2BFrTS zM%bz{Y}!MrTJs)*Yc)UzvotI5GpNI$q}~E{@$TKb7I|SokWLQfbddMM^`az`E>U!! zKgS;~F%m=toofKR%J-c^5cN;%orQX!Hi=71`yho|wy}&tPl8urYI<6tghJ>_#t}nB z(>K_0l#+Esuu7#00snjKxOP%%=!g1 zKjiYh8Aart!|$@ZqV)a!^W&4pNr#V~rZ9jJUOY}fn_vX$i|@T}d#nLZ#jrGQ3LC0c z-1TZ>SeS+g5j z7dh=8e6A~oA`1|MK>p>oWLTtJn$~wm+_ZI276o{2IQo0oZn4pK*+Gc6I?@J@g|f*O zHX~>y;T>s=mEyl7DfzC^anA2J`)N<8ID@mk4;V28t$;&ZPus8NcBWQ6dF4l)Kikh%99^6`>GEV;wvU z@R`S}|1JXBg@GUd>Nx2~{5bGh_11DFbEgYB$+UX{}cL)3g6Um>AyS_yjN+{=anViU& z{fwMOW1}rlNNRBFB-MC*=Ww9QvnwMUv<261q-Sx z%K3ZD?--q*;2-2fY!-&*%K1Y57i2zvqz(Y;@1`htO2*P>Ij9&J84^kPJs((#_ru@B zO(#VI!^!f->ZDn`<|obz6#%eVR=MGXYvYVz3DH0%K`)f0xrB)Rd)vU|ICXYm7xW{zPNQ|uK6KN#3xv5}HVf$aTp|pi zo{eOZ#_fPo=6l~;qsK<2;yfr+L~K47u%cV;+af4KXmDX_4* z#zKQ|poP0?(r+-SLS_{X4gJ=nTx=P@2+LNuiqg25QAS$(()i9J^Z~(fjsxHEE};yh zuJw73XSFu4)&f(m5fH2D^SQm5I&azj5C}Z5H%?t-Nr5gZL|@QBvQhm9v(zJ50fCpc zQ8&hCgbA)3zA-ay|EAmZMTZ&lV8&;QG zyVJOEeq;flXSaz8~pc6bJ>6 z3fzju$IXcfL;ADk3uCRYZAM2CN0iAAfEpK^Tqmb`zYg+M3W}q^pJ5hIq6HXYUiMWC zS;EVx602*a<1i9HUK0Q=KY)Cp53&uEc&P0=bIhX&8$W;j%2Sr-3*H7{ccn;I_fq9! z-|%DI35Kn~I-QvJv50Q_52GAzo;8$x7?yS}d_*ryUk2TFb-_^S6ei|RdXAEpW`_JX zf?rPSW)%nlrC0>7v)z6`@Bslu#1hlJMa?__XYi@ zwMX5RR-l*42k@EU>XuALjb+B!GS-_y2|dn&s)}@-y+{ovc$X8k;bkx~KB~|r5CfA8 zt$v$x^yfosv9j|c65$Y141EWOs4OPEy9uCPb z!p0%w@OT(!545xHd1pBY&zpq#oee6<*Q)HE*R#oEvH<8xmvvP{=`GH;71tHgcEOq? zN6;;{+~#sh^brS+N=niFq=|_rn$5|H@m4KEy!E8X^Gb2?S;*2RObAiX{A=j|1&N3~(vsP9ree>p~ZyeU?@2 z%TplzvH-ajJu$Uor%&IsC7W~tVc&3tHqTOT_ih$F_Pcsc;aW5Lw2~1olarIRSS?52 zd0Nv9o@vX}mLvn;M{+2lupvem0n6;g^#Xj(!mLPU`6#DF<~|Cb=PEiJsTNR$cXQK+ zXtXcFiF3Ih$M8X{{}mpT^t(;UBht`-ILapP1I>~FcJfMQ@9LpD1BxxnPMfRH|8O{6 zD^d!UnrsOLkY|fWLhMv0b!{LLHo~a=HV1;jh6@M4$w))%P(b|=fh@yO9a{O|X}p5| zbr>48!fU7SNYNAIGvW-}hCp7gjjK(2V?E?mQtOBF@0%06BB$Ndpt6&QKRGg@dqlnU zBk+XyM)N^H(ow4IjrsFX?}30Hj@1qzwwy}3#~OC{{a)kT850(?5_w=HXif#LH7jt( ze+zB`-Go3u$giZ0_0{+y3DA&C7IMR;vBbS|02B7h$2INKFs|2_t$oI>qcM zaOz!AgEb-0)5Q}G6xD0s=%DtRyJxEb6stxDAps)c92OfNv zs_E^a*UZ>VCRYx-`PeaKIj%(Rgv;nkl$gQj!oOm4V1T{ zk97%RxQheVvmwxd<^1-A$%*1DEGz^}_%H5>q333%@f#-&AyF>ixZu&qw6F4D-or2n3H&~yOgIG>=q0L zJPFs}P=at)JP&mY3k%%0=Ej%|%BNBh(XY~PXMjVamjMPg3w{iw2;Z)Nfh?hwe8ZSqjKu#iPL3z!nv1pbLIgIT`n{Ur_Tb@U$n;I8{o@#@6{z1cwI!wTuDiJHxJHDAd%+5$G$09=LwUf^fk0$n1fQ+uN%q5)n_R z(UPKu5Ou|9EyYXx_&x-oyvDO-h_`;n^~MT)cgu`@IpJ}u3mBp3aCs1?4&r({U~BN8 zBh|Z@_HooYh}U!kwyejYWS!cuwnCyvGtQC!8j$;!$s_;Ph3=wqDqu zy}siV`Vzk^;Yq0f5$>fW0#!oZJ8(z_Yi=SrKww9L6+H=&2Eu{}k|&Ud8el?rKf$5% zc^kk^{k+zFG&aAiLq{6-{Wf_;jywMB)~U*poIm55!wX`(QE=MoKrjM)7ZtvI!w6#q z<`!ec_#6xo1Tk$HzIygpbvm^Y=liPy81XikfpG&8C9ne;uD5}@bNsi+)4sf)8zJzA z11NG}ezIF79N2dRF1h$q3`Vf~l+;i=IM-a$PF@=*#s~glN3Yie->r1x(VP)l!EEr-tN;z9VnM|P%SqZF+ zZitOHo?vm_sH+CMriWq3Zxna#+O@P{Mn;YDGl@AsA#*kcTEJq!`02T;TIQP#xT4{$7Ni&8*DxsN~aRVq7)B=BCI8FUR5AO!eI1 z^#QE*U}*%Ed>632Fz4kw85JBa>EOpZq6_badK#50kx-bW%@pVbO&Wm$y}n=;u@k0% zi^RB6eIne|H?YCilDYQRXyDR?kTyKFQT%HCoFf?#CceNVh(yik$E7S~XLu_)ZDu0a z^rHuZ1Irb~(y!2SltFwoHBfZVL1)`v=rKZ9+&1+i=&y>-3|ipr7x-A-AA*#s%SG~@ zk&ztJzEa2rp1jEN6Bujqof@O>zn6iCA>-;w2JgYnKqy(LJ?O?0+j}1So^|u$6|xy`YgD=z^Lc_b9&F0aYg$;l#fI8=yQ5J@b0*nYRx7M7D~pD(z*nW2QAJ3M_psiBQ$|GJ%p}c8@h}*~Nyx~^C@6*@d)=L7tOBfD z|9F0I;pqg70Yp>hSx9*H$B^VT4@L_a`YWj7k$t}lX`HQu0Bn)dcrQIYGfxWOHuz9T z{0cs~Uh7QQG!{R?6ubQHIQj^ID#k&-8}YzlhBg_CPw$6{lGi}K#R{^9(w*LeKLQ2n zBbTj=c2=GW+CiGB2jITRtBNB9ybn;Y-g<@z9+In9uil<@k_(Iad2i$aS=N4bY3Ct< zIucUEWD|Gy3g|ul=m~6JF(43n3?v6#`Jl)CR_#gGc>!dEk|-ybO!i}i?N20@=yQBM zN#N@IK|izo{)OOvz=>I-J?w=PFmJy|O&zboR+awy)vtpEp<(_I{X=wbu68fktIdPM zjy$#?yeb(Q4jfxy|6gwx z3t(}PAcBIu`CmPwinsriJD;|y+5ofyK9&H_3wzfhqveu-ue!4b_U|K!Xd;}wwmSax zFKjye#ot34i!}aSJJ*SPtKAvZQ`@(U;4QvJbITM9eG|J!YL3|Rn<$Bp$g`VUcVs$l zvU5P|B3wmzm#p{Jm>hMn0p^1fb7QSVDn*x}oY+KQ@WXDNki~Y6l$KVGP;_n*DQJGW zssFK8#Rc%QCkdJIH7l()Z?>9(;@+rK)NO^Zdm|_19zHwn^Wx-rsf^JAkfmBVstdx^ zpP%smgMlm9C@y1dW4DT^r?}x8N-<*w9T z+k*oGsB9DN=LykzjiUgXfI^nfGU!bMPefr=sFB3tU|Sya zGV&CobultNY(LFG=60Zis#r2q?Q4{>a)qWH_@J3%Vx5i{Kwb(|xSLt(CFIcRypR@` zAXfo~2~zR5+8moitefS43A!Fcjh4!Y*(clTQ+e7ZQZKr&q5s*8bdbWwB>`8Jc6)z$ zpP5=%(ES-Ic$VP44^^jBx{j)(tZt36%kXdvM4`(Ku{f=H?gjziap*%bpXO@6P-c}8 zyRP`p0Pwj+;Ndd;ymbZJl`2k_U)%ml5werp%dh+#Y!r7>jXl?##2n)so=YC)(-+;@ zw#?be1$=Ozh$h4fr)edUUNH)Siq5C_^aHkaYYRR*ujMR-ErJ|3Pd4=2tIX_@TqXW5iO&CdZelN!e zknwVKbTn+YB4joD@)Zk&qI2v3!p`2QIFXmEoUD8pB9B;X(oztyyL-4xH;WTUc9YD9 zj37v8DSy+akfEmn>b<{-$lgxecccf91_;YRN^OPgtuW}b0i(h#wJ!~Tn>Wmz4Znt{ z2gW;$ebR171bX-!bMnAfT_c7J*SOl02p+osnpA-zXVm%%M}#1=Wwa=QkkkO6kM$+~ z%5$9dit8)KRDOYQGMs^rDt*V@?Gil$^{0BU0mMO{ML;1gjs-gAYF?Zxb39lcdQAzD zd^c3?$02y%^~EfS%~v=f{!AmWuxYyNjA`0`09ZS0qEWv%LQrgBk}Dk)7l9AoAaCUB z&qM{{jNh;3COE`{fWo*5grp4>J)-k@@^^Cq&yM36)koC>JcvoQyYLBs?qg~d=hQB6 z*oF;wLN|e$yG*9R5svEVK5{YtsYl&~Sh3N|N%yFI-NFBRFhBByOf3@!^?zIio17vJ((G!TJGREkX6H}z>HmjN0z{!T%02(ly z*G2%H_6qq-NMJ%7-Z(Xt0^ua& zB=f{V?jVT5;|Tf(q7#6-P3b7CHfHpR;BBE-?<-JQ2WD@x_G$83g4jj9Wp_X0)=KqV)rH-me4_~?eKhlgQXdZauq;rdYKW^hAt zx4@l*D{Ly=w?XU_^Hf2Uz3k`rR%)#hcO+n`v}H|(MdM$zulV}4z_d>bX4TR%!_g^! zC|Gah5j-|jevQB!G?9MT%>1)_I0FjS8nUF>@#LNY&4eN9p(+Fxf4J0R+4mDLgbg zMAwk2nFqs)B;!)GB}2ky2B<0NnjfNufPx(V`h#qV@TfQiyzzJ-vRIzo3_2~}rP)|- zA15u@Ym}jvk)(Wz({@fg__pK9A?xwBuN`;rAZht52=u^d1E#v)RnpQ%fviO3oe=ZH zdvT+)VmYfrLLBUtwo@)gZGBgr7A6k=h#T!7<~&&-fPDs=_l(DJ*Md+W*!Xl+;-R{` z?ydQ*)vh&jV>4q{%D{8X3;1>(rJhIZc(1s+#yH0~lN4kHES_I5xfoS^QiN4Ih%RJY zE6AZq*^Z+-gS=wF&s?j=w;sqD*euPwhzdM!&tx;!+`hH(@N!oMHVL=$AvAOh%rhk7 zC(*B!h({0}J3~P*g|+>&*Ga`MvA>dLp1(Qf{QVjfO+CS40zG^*;Ok|UA2oTB(6HsN zjJnAxSyu31by{z(TPxWNlt@L{pN&<%hbtFi0Kj`Y58};UO~3V#%yuK-lXy71{Yj}N`-AZYlM;3zK+Z#(##E*K2omSgJCbRpZ zvhR&S@eSXmj_-nDhk9d3u2_^x5!#cZljj(7roMD{J?cYYFY!iT3yMK57Q*-BTOB4P zptNeWJm6HgI-#KTMvRTQx zBtj79mZFbYqvnTfMI+#8ST27<$6p2Sz+-+ijqmB>cB4Fpd+OLEC2uA=Z@Fhvtf-h* zM_PAU&OY?wFqy4xE)|ja=hIQX7j2nD95)FWwJff;YWgWXD!b%_l~hTar&Hm5?bA1< zT>EL=9D!({fn8i^58qsA=ISu3oColhWkU;e9)R#e_)0m@pSI~IuO}64xLz>Io@vvE z2Am8(8s&VQ8jcsdZF5Zu;j{&FUmhf68+JW{es&}D%ljyC}L{2mqY_so_DsIk+GnC9p zb_1uO4%6m+&8_d~Rfh8KW#I)dp;9%eAP(x;qGq z%U-2}ij==Fb$x-|uwXv4;~cbg*mf7bFCA4ht00CDHTS(y?n0gLCNxup!Srx7lj}DO z6uJ^8U9#?AcEq5~pSlJ7!VeSXBmFj}Uy&1?uAstg^dow;+IF;f&j zb0KS&n?woC2b)A37afP(kwxvx3-+-T;l&BOkW?y4-HaN}qgBBizg0E~<$_2U@;MgH zWkQbz2%<>H$oN{m?AKbFdO>=*#j?54I~bVpxu(C7Mn*=~g2r$pG@Dp3LBj1~PT$-} z2rqkP#apgRwgSUs;9cr&Lu%ds1gjLVdG)|K+NtT+nf7EpPz0i6b%wsO zA{7}K&X98-ttYzx7YO{)#~8Ily!=`hAOp*H1usWU;a|IpKfK5EHe$$9x&`L_k;RFX1!?MbFqQGyzxwJK|w*qB!dC<@yvGR zUM}0Y-`K|ob4x@x*GxKS^TukU?zeY*rt&BcnJC(v5aSAZ$IfZDP?c>XBqTpSQcuo) zZba*w2eh)i>bXhw_R|QlV7uU0b9M#*|Drk9rq&b0J}ix;iIp%x<6L_fZ{vxq2kT;x z;ReBlUO0}D*%a}I_yjtlYseIg0vvv%{t6oXtvp>CR|Lm7gIu2jHgl?hgjBsdQafM`lx?ZF0lT&PEmCOcjK6zYP74vu z+oDVcRA)-EExz@dj-blq?_D{U(*p&~Gg*ogJ%ogxtO{%maV=0UuQa&FFf2Z4vL2}E z^QRB*bM@hOb1n5yNDNCDlAo-B&Xf|+IWX=H=daOeLALqMStTGBy3lsdIivm2i=D#@ z%t0?ENIydk+89dGkbCXImOptRb8{#}%;|SxEwjwetlK<#wa}ZG?6ki2y}ec2DGzd~ z&@&Nh-;{4-PF-YB8H!Ne6E%K(vv z?CDYmUs@$Lrwu)HpJbGuDb-?42C!p4OX)PsDv-}ic=W)H<;LC5KCh&0$OwFlp#F`0 z=aSP}tWPs-8~b>=zI)o8NEHVHx_AR{xu8>ai03rx=sT&x8K2}2<_oReZ4QeZ;bORW zt1T<DeJ__(epi@}R9rS{g*7O1lgdiYli7H_os7N3c3=pTdZlqqR!$=q)g_mi zkU!ONGCu#k9jmtJ)1P?6y49Be?9G>+78dr#u(6qgUD+l?GYQhPT}FAATc`cGuRb;{ zDwr<)-TEeD;#O;f@ftVpnR8rnpSXRpI2~@j(yZo8&8Z+8FkJ$&=Xk6K1FAt2ke9o{ zcZqS(W6A5WvD4~{q2Sem^+m3$T6JM1@|=9FUx;^1J2I(tXJa14iNsSho*_vQD(>F0 z^3L4!$8N9B;k>DVrn=90W2x=`T(GcNXeGZQ5(i9fFFZ}x-indI38jF_1nl)1oE)Ck z<_fLf=|ZXsstB1cRd&VZ)})3|@sob0qI_i%v1Stidk!c5Bpw}vu8q`5OTSOh-7e6n zeGCULO=>am`f!m|i`vj5{b~iU(G1Y^*>>++w!MuFEvkLV@_clu`OrJ3ihh4!Qv&(X z%-WBdmEMRQ*Qodfo7BVa6K}AzWSt(Q@KN}*%7_#?L}U1zcNrJhQnoH(| zlimkqeBw-?Lz+3nEF+u4)8v@5MzGa{aMZV z5=4E*O9(;=CJWi&C?Wi+L0nq>tN4+eO}yES3n{0$ax@{2uz3HL5>D2ANHRj1utoz#ssixO=QonP z4Pe&iyBdo4gen_mpTV_+ChXL3FHZWi$>)lc6_m(uT<$+*g98=n;Z5DSvVNv|?^`PA z{5IR*cyD26gW(K?960~$G=Kph^$HE$g}>MM^|xW-N1 z?mJ3#rOxB#D2HmXuc=XjK!+^&JQv5=j9Lv-3|i|(sqy$h&7j)g1MsJ?5Q4Ior-M)8 z_s<5!Y#aT2-&V^>H}m_wxs*&xN}wll4p#DVTk{XY<|GFn;YWhomJT=FxdKin)l=wK zikW*Jx$RrK%g4Sck~X|jq}ia^go?0opLSZ9m_dlcP38p!qvgn!TMG-3bUsdji8H9o zR;|280m7PuRt#X;K-KyUl)iNo!<;s0)K!ubJ>Lf0&bgh|YWw?6#wq18Q@z`uB1MTq zW}h+w*@uNrCiQ4MYTu%jQDGmu(jIU{C%T0M@>nzYNhJKvd!A<=O$&I10i|Ur)9dug4YD);1!Zx?i3)- zYSa7uonL*VPt(E4jI`2zFn}EVv2ZATfP*RLu1dOlVvumd>KwgkhppQAVeZP_PsK6O z!TrgBZ8?$MH%aWcv_Uushvn1hKCa>RES0MwK6y}FFaj@oJmo8`4_1;kJ8f;=VrkBu zn;j`-g{wpl*Tf6G1i>Bv) z@^~e9pW{7b^$oiW^|9Q8Sgrv|jHDQG9~6x;8gG6YSh@EQgq(<-ZhwMP@9Xn%0=l%s zh=|6^FD^D_E{Jhxbp~1!Q?|i>0~}CVH{YTi&UA-%O4YR`=5-d0ApH)fnTiM~3+wb0 z(?Iv-%Lmio85NcqWn6MEo(CZ)C@Xb>m=z*cpqS-LBmB>)wvEsyrz<_714ait=eBfR z^Cr0=zI+0%=i}|xEmrNHwLeVHQ}t>lE~4@p9nECBUa*(NY4(+Iw#hxdKsa5;w;OW8 zsZEdh@{pUFPvZj(^zYZU=fzb)wJp7db4fMB%cgM{$RamCuv=SUTzJEF(WLkz&x;AW zDtdCqNjom~;n1%T;C&?&lZ$?$37e<^3N;(Fn*!kFslzd0CSae|2%OeLd$@vr^kqI&;8^Idu{$> z`G;v)&~2Dj2y!~Ci&p#8zMg*hq!l&FAlj6+;s{P1C{kTyRs1GTS0K%7@#49K>f@LUIa?klzHPf=UN_$aZY4Q z@p%#X6y)itST!qUfi@VUbj|JAjw$!|{TI4fio`++Awtxz)#h-lQ2EgLQsJr}((1C< zF?q@K+o#nr^WqBf5Yv?r?IS%()}3c>ePvU5q_DCcoNWqRVEms>b|i zxY(S#@a_4AsbG#3eY&YJVHK~QY(P>f7tdHracL8kWD}C6bi4mGk7tX?*WgbbZw!2Ve0~JZmwbVQ$P}FG7nw&J zf%@d>&_eqpllh=KBqA<1(c1E{-!cF_$mkh(fN=vC{4^v4+wC977_ zD+{m)9hPSXAUA7KNpq_|m$xrKJ9w#5g*F@7On>nYf+l44Xq+&g`?;<-)Ux(b>P^S8 z+zwgjIZ}uib3p>#(sAr&P^+nQqvOQsHIkFlRjk~x9y>>4s3#naYr%QSdYcPQ0Ms&F^A_G%D&yKv$+!D_q{vlTcdR8#A)TVK6cHbR z-)uSz1>H*X%UQQ(k;giS*2KSje1$T$ zE^(%k*&V;Li7j9lryYqNM;=yD)#vp-r~V~yvBpsUlJ=?kb6guwxYl}|pzIKmQya&0 z#m|+RFGQGjxjtlL?l(YTdSauAK;2W+#|WiddJ@Ta!FDxPr};H^42Ki`dL90H^9oIT zhj~-Nd9T|uHNtcSQ@`AtHix)2X}!OcOLg0uOV7DWnQ=UPMQVXK$)7Hok|*T9Nj;3W zI&Aaxl&nm?PVE&VpJ9A8!wG?WyZJ`He&s^04D8%1xXVE{^1IJ3sGlC7vVKZdy~qH?C#~qOiAyKze15!aT*SfVNCn=x&qzI&8+NWW-KK!ib^dhocKOM8 zs3U`|nnB4bo#nzcEpek=xo<$qcbkZO@SobfXc5l-VvJEPC2ZC8Y9QU-^`S_(Fs{_O zC3>36724Yva*&?$XVkXxAjXoYV`k<${``cNy`Jj&=KES3Apgl>`{q30-pAKM7| zGkwQ2-|$)|$ol2nYQFKpr|5agm9w53DBIGLU?#B3;tfy}Psch*TsP#{+hs@aC!1L0 zpPHbO-Uk&vUte3Et<+o2ZYO*m#}M+pudIWrD4`?CtckGHYgDTy7}Y2COY8kSaATPd zsD*ce+Un;gFYaBIif)=452BGz=q+@ue|Tax`xn6T!VC{OMJ=wHoeLZD|_;YDLR%FUNgZMn{H5bMLjwl4Y!Xf$D zgZ|HM#-stojqyO1a(ydc=iWF5$kMF<;ir0q_sk?_;@XMXG=+0;WGcIg`qmh5s^ z?U%SpgJb)dg$Hyl^XzgM*^Z7s$o9NM_~0^#{|M~6c6DHFN7{q#-=ODc-I4tXT5ka! zShxg;is~Tt=+T$TJGrE-u?+oz*e0nF0g`Tvs{KsG_5u`8;eNI=;x3lh@KSDp=G!AS zdn}t_d&Ksh;B}$%ad^sIq#4qlw_))~_zsO7`G&>Rd!zuk?ovqKI(;5;5YU~=f})<|*poybPtdxGr`I}iBN z86NPTZ%nv*zjk?!djo&(f4diR_eoQr8(fqPKYL)8BD0HQ^2fzZ3fvgkT~GfZ`PqVA zO1?K7+aKx(#4qoBbLizG_nc3~>R#VoXeQ`?zd6Rv#e{;~7%c)R&v#r+7_Jmr^dFc1 z_02kK$9bJSjK_{2*c33ds@3RS`+o}V>yzUz4_$ayuZlq^RLt_{wa(lt=v2FVg2@+> z)t@vxgtrEFl&7SA@4hno!$SgrmH<1|GL;EDiLNly(?+|+}rwQ z1~j{a*D?jdZEJN-iG&uSuN+*O$i*WkVqWje`A=M!w%?4O%R9Y>t*;93a02eWatiuP{kO|-HD zyAW9vJ9pf=Tt^QLTUE*}k7)aUwU5ybehWP6?(%bo*UW!&#tHjZ@>1jA4_{W0^2WIegQ_Y>-w`-5#*s)4!*+vqThP{ z83|}g@`_g%MZQS99DPvqzhA2#%04%02+HSnZ;o-Cr}+B67titNyV$C|2|fXTm>BND zWBxm}&F$qnBMG$M=6~Y9Ct5rdCK_FdrGD?WIz(u)2TdCCeRTvBi2Zw3^qlIC z2X`sOzrlI%i;oMzQ7>MpnYv7>O7?$@9&Q{gS8>@N`N%7TvSKK{{qNBe;D*^KzCz`) z`|D8CBkuoR2(M*e@Y0*4nE$xtPjU3p%X>x$hT$_9=Mz>VBKadn6#vI`A+P6x3pSTB zA&hWqYUz)--~aEGca;p<*$nRZ@a&HH4VU{f^IkRwH!%sC3%cDsCMERybgw7yc%4U* z62I>))UEIT%MgsUMsmYOU-TWHVQiwSY=`eVGdY*UHEN6ZwKCOMx!ekK0i zx$oc)1Vmw8I=rcL){nR!7~!p1I%sab!|Hk`D{F}StvE57!+)<>mn-1!;8)zG-dhEs zT4?eZv7wE4Hn=78Tp{CCwuGs z@Zj4aTV~s$b7RAjaWkp)5!sz%E42B4pPRVWu>O82J2f8sRp?))*d+{k%W6sL^jv4s z|GwS!qlLqxos(6nhHEH{(Ej*Np)>T=v+@ zL$ZQPTgTDf%B#Z>-*LjXXU`R^-h+3Q%2mAaL3`u1{o$b7r%nKGuzD%f5!M>+3umgo z=3(!MW`#BEd3XP0V@8|*x5bM=@;|EgsXPzv7bx2u_h&u$8m`LlXmeTI^oL^pk3EZq zt^y}pQOZx`z;a2)klx*tyY^~#RmQ-v@_^Ls$=(D!qf-5kZ9t{?9$aC=yIp^rc`j=N zH+T-3<1;Z=NzfCXehlz-mfsuP?GMOcJZM3WOl+qaPOEnReDr?nY`PZ~%Vao@lPc-! z5OhKIDQn+yZVRSIYN?R@g(?7l)ZhAO^C76 z@LJ&$KdaW5^!s_}{#lfR9^5!$S0`$+h5M8}*l$j>XWR7=8U13-z6gUk3N|7k*?(j2 zrESPn@9zEQ$kiLag}+9f~iQwKXVBV#Td{ zFMxb_J&9h)w&LDhwRP&e$U_>)&IJ_>-vt)d*0`H?)BWyJ$_G{ey)g`UWJGp zADe?+V}teGo}RIC&wsEN!8Wjt_AQCHN=)ocpVvItLoKY|*S*`02rDx#KSpG|PT#EDx~pxcG6vDscZ*&1CKs+F zoFu#OKD!Ofse}hPnxR-&6Y{^Uzy1ZEXNzJp=%eY|#W?BIh_~yHW`j2e{RbW~b7>qQ zOtD_bK$_|W{QqK|=I!~t?+0Dhqi3#u)rh~Hs%LbP#|ac`&>XBti}fWk*bqD&x8A3oXp0Dyy)E*1hZLKlPdy zh4umaWq$1<8=|1t)f+|srTQUrAVL(DT5lx@1{(V7^n=qk^kg6L`3Hg+CDN*IAU(`O zX<3-#@<%r4u5%bMI&Hv718ef=W=6B17p|B9IgWU-`%zi8YW;jVg5cc3mIsZtWH@VnhX{z7G2U`6oe3x>!6QnjX*iij!>C=f%^_y~1zScFS7rh!F_qPwUF zw{-R)8g_QDp0&Rs6p(W#c6<|n?*7gmgw8ksE21OacHQP;*h-B3Z;K= z?TaGP>iZ_drWAtjl9x*Sp_HabXZ`iJ*;~!OD@n+Vl<-1+AuBsrEBSP!`th=l>G;Vq zUWcr1?|JU|FJC7^ou`I;n?_?~(wdXqha97|-gLJUxQ6=)i&rwnpGc*5T-pOk>pG^7 zYZKhFdv~2}g-it$;%!ovw)TSzwS9vH@5cU?yhSL}vZ=jr_qlb^7Gt>R9nh|tf3bZD zX7ZD4zX!l1kdkbJqKnet;|F?b&6RHrdWeS)oT_6!c?jOnIazAb0U953bY;|lNC{sYF!-M1+Kp^ z@^ljNXY0jlc_dDln*Ib71zQp4iGeMyzJLp}V^b3JCENE`3win|z_qU7XF7G_zi zJj=$PH?mG=Eey9A%6(~;xsvsHu|GjK$vzivwK%iwX-HCqs+?rpk>KiSafwkd1ekf6 zF-uDjMca6fekx~gGffo|4_VD3*^9er6XxvP6`*MeC3%q4b%uD z-?j3@ATaa&hcYb8qlTB0e)<}+IX_$b&NMk(XDEA)*`WX7Lrw|&mhTNX*Wklj9sKV25DZ;RG7`-D z8S>5zO&z-g7Jp+ic}&0N-_4?qzgo?Ep~PVjI4vY3^jS}%PB~DtEweu^3;qKzy*2?W zr)LlI-|JXiuNT%aVwvwwtZ|?IWmaaEv6G8?_EY7$A(^&5#frR+x|lo}f*3*EgeI`B zGzzIsnBHxmK{jP0xVR>AT>v6?=k&1vxpC>p9smY_O5ck5RD4-!>8{fu?*RCr{*Yv- zN4}P``E&KXt_v^oWF&T()uid4(H6hh_$HZE_-*XXj`iCO7?Bs5*aJtBMX*cufWk!M zUeG{6M^+)L&ZY1iz&?RK-?IAgCP*S)?yY>m_x#$ogFPXmAGsHPNwrLWF@5vgJ|DFP zdX8SqsJPCIK?O#xefxdBq8}YidO6n%GZ1sZ;P&e#sISCoD3S1_6KIaj1+bzkmAJ!v zVJ;)-x#O`=C8U_>XA8BzHRdUO^N5V{`busOu662p&Q{}7mTLMv;<~cpe>l}F@w?_a z!#dj*_qBh%ee!AX`|B1mQmJ{9jUU5i-X}JU4YKmtg9MjJG0e?ATjMid_|qZ8*5r8E z6Kl~1?SK?%5Lhd@9)F32(K1zc=ZP{onVfTEgJ*aiFE$7m)Qj<+A zeKU})6`%J!)9RGLh}9SN(NR^y6JOn%++16q3lgfHNGgh^PTKemuBd6+wh`oXWXjm*FGImt8;hH!|TsZK61C7ic|MB7LgeI zHXLfjHPOl7%lR!`5o#=la;Ft|7x3>G@7?y|O}r;3d-ZPb^zh>IE&Ci?I}$46Qto^$ zRPewU(r=auqbXesmk#ST6ei0h!6N#5dK^Oba5^;7#soF5;=cejqmPN zLtg}|=|w*-neDoO_x9Z=*n%~&Q6hKPNAld|e6ZlW7c;P{))eV{#S7|Bp9pElbBChr zdZ;`R+5lxli3ku2B)Oh$wMaR0%Sn;bt_tu@GVSr~kNjLiwpw>UVMShtuar?!lIyM8 z2$n;E57Z&(iYEr*0iLVF#`w9NM)|gWBYiD{JRu3wmP~c~&O`^fFYM#>VcDbktMaNG zdOlx&o>MITV1Le3$ENs0u8W50L)`d}X-($eEuSZSowaV5 z^e=l~hjFKs*~%S~jk<^G&y{a`aKg=O=r9O4*M+{P;(hiLp$+F z@EzOht9H66t>Mx~+dGzjQ;zU#|D-!Retl%Ncwv$$OQGW23;%N+~Hq|RK8W!H~0VF928* zzpWC&vq_bJ>~NG$vuo7JTC_j!hlUQ=qT@_kpwM>|u;Uh?Ya5C~gK|>wrE*$Q=|j*A zv&A^%D-@qep4HG0H!Si7tV_6{(+yxitarrOlx(}J7S{_U8?|n;_>Nd8a|ZN)ZUE7| z4)qZLrN*e3=`iG0Z~Dd};XNC7qUX&t#(R;wClUh!Ql}iN=S->`8WIe%1|BPA+GUQ0 z9GSHjteAVUM}PSJ*g^L#!`-g}3?~Mv7TykVvrVgxg@{{y4G_s5{qCOa*!C#A<=H+# z-=klyO-e8bzGc3$ers*dWHAH&M}{=pc|Gl!cxBnV9-m=%_pY28*&x<=Ln)hI-91Ce zx?&f%?!R2rVJJ6!X=1Ln{c@VCxYe0#Zy{mpo=AqIo7T5=Dr6;X-aBSolmBVK+X675 z(<6&Aheke4%HHSIndqCToL02_mg=%FY!j=~d0QgP?rLxV_mtPyiw_;VXIcw~ReOZ= zSPynxelz$zTy@smpW{q+V}fDf`#8?}g`c-xFr^i~|CW^W$(FV8VBg0!LDkw4sj!*! z4*SLwrd9Nc!wXiwzS>eid-&10cfF=-gx25|0q?w|)KHOQUZ1E~#Un}SfXB4hXj^d^&n0s`MQ?oP zYwJs}_22rr%X3e!Y?uQJ?4Z80}=b0?_Y?CUKZDs*@W6tu4 z2b`y$spj@RZ%Exzn|#tfL3mbUr;xi#>jtfgu%^kg=eL^~&z59{&R#u{qTFnXxG4&T zpBp45*&n;cSq`?p<~u)LvgUhF=S+oON~f8-ds69ChJ&q7t7q~=-qgaH=Ag}QI(>)U znO3aIt&(td8jOWv-G$B=&e_5;&AEoA3dIt3e68EHeE(XB)`_9XJeQW>uCSobMd#CA ziaq?grdVz6mAm`mh+nOd6-4wZ{n16Gn&6%0#(7ia1#pbC<Kr< z)t^Yxn|&Rfo0D9swcn_`#P^bZ)3a2;A>O;QQyo!x7M4(d+_hz>}18a&ypMGJB|fd4AdspJpi}$^7`3R1`7{8bzYj?J>Yst zs6V@Ve&pLXed(zyrdnD7mio<32KJq8>2fod?0*cgKTkF>ePU|S_}Z{bq}Dr1*MWeyXrJEj70F@-}`4n0BCxrkT9TxUNo{V&8 zM|zaX7d!Wles!Eh*lgp4Ter_>j|o+m=KcWYkn zT^P-H)Km5A%!lXVvG;W40%iFwzUpf*j+X67zvI`ED!lt=W1LdOY5c5xe~OE|sd(OX zS_3JI`u$R7<&U5J=wVbeil0+*{tD$>_Z$q(YBJUH2A_vB>Co6E#^>fNSQkn(YPbtJ zbWRWJceZ5QY_K|O?Uj3IIJxnCsm}v$CBJ)neG9Hz)Vz6Vn|;0lKYsi>Z$YeViOwVA zPMgwguFh&ht#ohCIy>jjd(Alq#5u7 z;8foMO_(8Y7P>1p^VKhR5vtRAq*@LF7ILyR^rIX+QA+EtmEM?}lCu4)X6eRl|+tN{a3C@SM6r-THgW@`w97z?>ee1 ze>S98QE8_ACdPA%!eMTPB^vC z7Q0#H;U{}rvNU@(JV?peH=HavIh+`#HjVeF+ilj=6T#GW-dnX{qBYRZa?A8WkfKh? zn3uE7&4~l<*el?s*T;D#nsLO6$2Dr5d=eWuTp$@ZtL~NY#rmpDdSRJaPUDT&EG?{C z4-Q|~EBr}Ar$#pw%?Lr7+X*MSmm--+%vXV9ND+Q88Z)^#2Q3~KP>lI%Grp}V>l+Q_P%;8KH4e5cFj z+4Cf8bDSq@B}&qu^$y^TjgI{2`tFJB7Kf}-zA9*a%pF8eX782l&u-BO9>^&y(#k?j zKG@Cg52G*>`b1{W`4cD|H?x3cY*z54?(p| z>D1;$9*RYsrA6XKVs|~vz#U!+0ONMkDEP1koUGY!u+#rQr#%Q0G`)R?!r5_X#yS|R z1r4=HMM;;-Y~JeF@0idrPm~|ndSxrL0Et7(_>tS#U9Zzb z8|S!ySJs%X&+s~3?`$B%ZRUkS;t~$=hmlZ*6<&s4uiDz#;#*2gPutCz@u9@S)q10{ zlMhnA{*e8ZemOpF)fK;gY;O;I&2tDinSIcy=wU#MT^*b`Rqff&bv4c8X4_V+soTBA zBIoC)9|~Km-po#a6mWZgp*nWN?K6H>LAm4*$KFVNp8z-GFwY0YZsKMGQRi))On<(y zWxd2_nR@2Oz}4X9CWj%w5Vf?JJ*F_ZrS{ECR!g13)#GI&BK5l2=7ryf+_iIVA~|Hr zP#Ypql}FLS`$j!PgX=i^>d|S>Eop$l ztpHdP@5@B;IDBfMdBUSFjCPeNAgJ+do=VxQHI?zdA56uk+Mpc*sC1#ng59(?{)q$9!kC?h4EfhiSSvj{Q0u zto~EnqWPtq+<`;=i~WWZ_X-5fdL|yE&riR~(Vwn6esk75Q0kmc$CH}oyrIvYF@r(- zClK>o|sFYzbidH-|-}tUpHmIOMlC`2FV|OevXUZ88qCCP0JUPM20qD zFL%kJY^u3*j1}YDUbJt@sVf_ZF>!YY!$a(Y)5pBOA@)1V8@$f5y;6Fvi*Ghf(%&jA zW@s`>-Yg_#Gt@i0PH=u>!2k|Ir{j#3sg*RjQ}JQ{1!qiCsse1`#2U~bb%SrOjswJ~ zh>(zv*3o=>$YLPaxzLxP${_tKe;!%@0QqfUB0bOb!&<&e+>G9q;PHEhCd%)ED*XbJ z0#;-Bed!PlJ<7WI_=T}BxFUwbD$=4lMhiItiq}I>>I)!|m;ADa29cbJdO@SjpDv$2 zw7tAGNfxTfPfTn@!9_b`sn91=0yXVWI1Alm!O(;g>ezn2Wv*Mo0g@NJjc=6KhuM}Y z@ORHAbDvrm&Jj6!g@q1$_KX?tdA_;6Xa<+rl68hDufd*V+ZE%matHd1W6iiP$+|e9qy!?*@TmlX%zFVGtdYlX5I>$a5>{U$h>nnQYwk zU#0F$tPG#9kl<`JoDN3a=cKvz&V@HGhV2hsNbhj7tFpAtP>tu7HU9byA}cy) zv?pR4LbY;7iub<@T^wGUC+B8zb-vew^WxeCx9-Dujn%J}^W>&8pK5yjW9zjqQhAci z&E>-)M@B!OKrDO%F?(-8^JtTi+O6=^&&Hjve(H?(v5)$3jD@^tm@`772$7(+@z1X#vs!vj7`utI93wGW90xiTf4<0MCT}Hi%g9$xlI#xs?3H$^2Ce zmzti4=;J>sGjSLd+xiS#whWUBHM?X5f@FrY<4p9gmU0;6*wTD>4QC4Uk{17)ya9SC zzI1+K*>Wnx89~{&)e-yf;X}xDDZX?n-&+ju#pZCyjF8R~cT4jRx7KD1vH4u+c-+gO z6R@`U!S=>Dcc+29n$psjzkG<9802ig?@v(PafykRGwE|g@HPBnPWOQ(zq@*lL*YZG zO7)7|evXXn$*|$AS5IS*3v+H%_q#N#D-R1NZ^=m`laoTD-iQC8r<(< zrTlCf-w<{`W^c16M)1E>#XQ?F{nPbQ`%2vNhF-H3Df@IJ3h7Q4hJxMPo7kWqKjr!5 zbEdXUy72GGk^MV&A6bcmfIug z>1Mvg&AP*7Ov%Yt8j4NyGluWawuB^H{2{_qe*JXNfyHtVkM^kJMehT+6TI`v(i=l; zExQyo7mcfmzbj`A4r{G@TF~Z`bxqo8=^&yts1B-(SRKvwPg#8c zG%{V`4#oD08KQfPRJFAaLF5vkE}J0#H3?A%xFe+9z!H#rzQU$H4d~1j;PgpJ0yOl! zb|j1iSlMnPpZKgr^@?QEeYWi1RRnj@w5{P*yR<(eJ*fz~dRTlA(SoCKyqCAVr878~ zmO10qCHRZC8sEbPSo8qFz=S=?bBBn73jlcSgzxw4h}B!-ETsj+-)XaP`1#aHfXsjP-;@*L>L~*@m8qNnKHwDhL{N=5E{nU(E63ZvCmwUQTz19uaQ0SewOT zBd4=xzA|;lK2LYl;_y3IFy=Pn#}@DUv*}(htBB5C+qQ>o(Xx9sbK2-!y4#XH{?k}4 zP}=?Y^nL5OrnmZ#87dE5>@vHnc=ze9Q$9@XP3B$`vAivEQxDxkuYESGV@d8XYcAgT zCG+RoZFyN8jXGfqKR)tSWZ|E^&&zNvu^PJfc<+Z~2wb{3ba&RRv+2kdH`Nvo(^2p( z@y$^3+?Yr^fh%K9@i`!oQILAT!yhyId*-yPl^DX8KoTj|4 zWUvtH-a%?Mqr%BY_>rSLHmP70M7HO631{{u0VvMTh~T)a!AuyYA`wEKvwWu_r__zr zjj{la0V3OdfP$0L`S4_1Mt2anA<{43LHFKKBx^6U0op~j*JlWAgm$kSvCJP6N-y?m zhu8r+wc<>mlweID069VAlQB87-BS>OR@mGJSjN7*s!?6H>vlumh~+8XOnLdQ2t0&Y z3b#||gT8qHD;ahaoVmnTe;0F0VR5re+q-D4x~V+N+CI&6uB*fINN`Wd&8>qAAge%O zw`8b|y-o577I!YwZ`$|ksYHv^bs_FhuXDpUiWgH`^as6AynJ2{X6NSan9Q|=^YYTg z>@&}H8T`1L@BDmk_oC}}OZRtKi;ImeQMI1XXg1EcTg48?NDdFWyE+a8 z_>IjLDm5%l_%1BEjyeoFif_Eyvf>9~WOWavTB$BXuYdaV$?1@TB2t@YSzcd;>wP4IC&MN+=O#)e&vl49 zeYvsd)SD_YKNS1*;I54Cv<`(YIh>}CSjU#2&7^nwt~pcS@4DF1GMU-i?3Op>jME)% zZ20Q*!8=2L=11%*cc-%O0NM9jpsnf5e@9aSA`yB{LInrT3?6rp(9KU(;XaRCV5^kp0vft=&+}zGfcFzU?G~76LVat0- z!3{;ZrD=fIdVoQvLn~m-Zb-KSJU7J0ScPUFNEsldW~C@kXt?^(Qp)*u)+9sK^A*#D zZ)4M53*(=9;FO(XE?=nWTCL%HEiio~`Fg5dq>c3IFflVX?`+39PXQ}+cP(v=iG{I) z>oic;evICvEpy=|TD!zPtW8?!Pjt4-vYr%R zadYay%1Ij>v;Q>`!2JP=B9L(O74G`u=WM!FlYoHB<#ywmobwA^ufL`tVG_&* z(DHR=Ipt4CA-YO)v@k`E1G5jE%qDaYKU2(C9vG~y7{@3_js>KQD>QB3t{Q60S^YfW zqlby{n#H}>KWItFsB1jQDg8e0P0OPH_>jZ!H|gguO_}&5QJYT7L$yKai72W{8=q|k zr-%~7a|X-TGJC0~rNs_H_f5Y>YGKo!huqA%Cwpcg?*+z{L$Q6T#>(wVU*l~jy?K2F zZ@2Mq(~re&bADetMqQ#JpbU!C=i=C=f0`Dd-|{JyR4EMQnx&=M*^+L33`0(Bkoc(Y znsgYScXw}DNO$0H?&7uH;mbzz9-^KMIb&E6eJPjj51xLjSPN2=kdv?y4-~_pFFi$w zhen^Ed_O`$ay`QiUZ^-X+RgGG>5y#Vh~lL>C&0r{9xN&k5ogcns9lsw6yxV%OO9I+ zmVnELXasGphthsmO(<<4v5NSCJRq`Cs2<#&f35oLPGpleL3NM=VPrCp+lC%8L*;cw zj3>EeBqqyaKh=OhMySvV%!CN;IEw~1rO5Sd$5C}*(CvV_+EXTbx27?N|7|>EeCU~B zG6?FA5`dM)M8oCn)U3I=_VFH_Zw7{2A`jF4TYU^YIIVEwz#bI3AIN=vjo2IV(`PrS zEDw(@Vl>GyIc@l_*(tP>U~6ba!|T0?^eA^0XpTz=)X<@7=!x8eBX$R{YvJ(OKYd(i zKW-`!K~1{2#onz$%D0&etmEAS`f|vPU*sVX3S%QSnaGWPkH`uXWGYz&KjPYRV>c-0 zHmKJ1_=S{zdY;2T*p2)f(Dn-id7& z)SFp{6*o1jY1$$sXED0a#%Iqf0}Jwh=@CS=Ko88Owb!^OT^cgJ2lZYQPR{huRN`fH zaBS>RvK2xE6~hT~Yj^7#vC0YFaKnSpsX>%rF)vKTUcaZn_B~JS&!ByT7$@x;+X$;B zG(2dhFrqToQI6=J=`ln28)LgJ<(ZsexCGa|SFbYo+Gl9k&+!cQA$7o3t=8)iGcXPn z#BmZAwrw!@Ld_PU8SvLfr&6;TW)v#tv{{q2M6mC|I^uktopThd5xdQ<2nCOl zaY&&kMwUKsMgNt_X{`9D@tD)}gzzTC13f!DTkB7lvt!>q>TvVT$}|=35rAPM<3SK} zFD~`((Vxn{w#Mvor&oz_xZ9&_y-OG{~YYLokXA$Wp?M4$hGW$Y;L7Lj=MDd@U&`?LeRIKt_F#%)?M8n z7q6_Wh5zRF47!$73P8TL>$$v!vUp~+=bzMvU_c_bgV>|ncap3L4=w5drMe(Offy(Z z9$r^(k-el6m4WEXmoNJq%uZa({MzH?GX2yx{raWuQ-DItgBqT?{)~lO1zVztb)?aS z8=1=EUKK2EU%z@QVa#*fs2+k1#s<+Qt%-A-@>EO+a+W*+*XyDeZlBCFi{1x%tcmFY z&OyC4bf^~}1$>Du&cvS+P5n4aWmvv8t#;ZT_tsLb-0FI*HE5cRo4}x^;YvZ~5##Ks ziSrPsSXg`mx9$2IOoz0~gg7Pp7)`EX2e5+9Q>9!5>~kAeo3BPa-Jv1@7Kjq!#)J;r zxZhYT=}jeMC&UW!ix5hmSPl)Scwr{;Ss3((>7LkqI*{}f!B_;J?3AT7%TdxEQDTK{ zT-5`00v96y-_R$Aq==DkJO&9$VSq>1t|$|f;10!3P~7r*$?6rLg@opp@JOjUszc3lDhPXFjjmhV3;}P}`&&g-95wpq265&Q>de#H%o19bb8_iI&`kye~Q>WezK-R}cF2 zAMlk)IP)O@$(W!XfmCDOuPabBsbZWhMJ{V@M;-8m`*{v>X@ygePo*4vg)dZbaY;!CTmKM_mJ&77$5L`GvmE;_p1*tza1}=k7Lf;J|PR zJ44+4fEW%+brg%jNGlY6R6D<&u<*%979U7B#xYswEYJsT!QKH%iif;@utfPjUVj<) zqTFjg!XT>ySE5T5-CI*LkYxt=3@TM7>ggu{pok)R*scj3+xmj))z z#8dGNynj>s1p%(NNygEQ;TmG?u_Q`>|G6hLRbw{_O=^!lrp3c`)X>Yg%ADH=fFPBi zHXTq>A(QYEQh3PC;3wwds~-=cQkT5AOXvxjzMYH=Cx+iKGbj!BGBc?+1pK_=sY7r*7RCgw{eVP2 zK(bZ=AQkHZrSZr0(zdtpb3JH3yqoJOnwRT&J4%2n92S<=*rA>sU}3$(thVQ^hbfZ@ z2gpswJN5vw6O7w{uDfYssXu+;Ks{thLg6MvfZQ;Ag;aFH`rsbZVH_ZKH2{25z~iZ~ zB$VOFGZ>|1JE6gLuQ&|8EDTqXW-K&3Bh69Bd-mG>;@4#Iu8A(4b(HY!ocNkR;MzjY z@#gqa4=^$3aS zF|F<7QVM@yFHfea_~gRtET}1ai^c{+sS1Ob&TA~8DQ1?9K7xwf4j8k-Sm<7 zVGnD64-i)E+xWR|w_{C4IzJ6xW%fHZjAQmYIczyG#yo4%leSsBJ~17ZodUx8Quq38 zQrK{sUnRx1R3=D)H_g%hbDASAw(%UG4+BzV`-{os3Ry47Uyl#~8A?=0x*bA{46?&h zn-~rc^D{FXeK1$3dN#oLwd6(}=~IPIWHh_k>rT#QZxY>x_5t_A+{I9H>2MW!6Elc9 zU{)ZT=6LV3g*umY$7S;jhXZuXX&z9zL*76l<$1VVE_z8MyLKt07G3%4^4yoK(B<0I6A@z(mWMV{z?p)tt<;advh<`KYl%#?k#VPj>G0Ss4w5wN?_CD7k{3XhvyL_J0JJ?wS5n6jHmM1T|m?V-VN zH=_v#dtLhIPw>5%puW<}aX5f9tXP{gGzc^}~tcOJF zjNJuE6!EpT7HrmJ>@u=+P;|!v>w>hwxvy%ELyiipLYpaV5!n$XOjZnO@!c}48$=Wv zeqq!P>Jdp!s~dEejT&;6-ylAR%p0plTeYG|D(F0n+Sw^gS&@NkRpgde;&MOl2Jt`b zTC)p-3#6%9<`~hbTYA=?50p|`i|#u~0lqP-un_k>@{)<*8l7Qqj9;uS@Lt-$fO6MR zqs#jg;rY+dHfg@mjg92PEcV1V)wpl^5CiNG}|G-5@TS4$XqiN-t(AdOD%_#;T3 zD7B(eaMcaCKkDpS?z{XZ?0-z6%9Q&v92_NJsR}yutNtVzOTUCu8obO}z5M<^*#B5X zsREP0V5EbS=*dY#b9DDK`WX?FghypF+K9S=m&B3V$R-++x=)p2HX};;E=#l_e8A`- zjeys*{$1Aj6`Uf_cN)%d<&!M?pl6Z(a6Six7!!nrh|&U7q(26Vi1E4{MO0*r+uFeO ze_|_h1W2Z6&F2`3H<<{dYRLWsNu-gZ-ua=O`HzY1)>Sm6*;5+gqIwvy(#^nOA=zRDCZ?1TOh77E4s0}v4HPMF-AE}M;iTHw-8 zDIh3JHlS+$dca%(L1A(p75*oA!Dz$WRzOgQJ@&9(!Bqr;qCflEp+Bkn@?z=XUCbJ1 z)e;(*AmkWc7CB~c$)DS>SMaiFSAsp?xO=z$zKm&PM}*a64g^Bw!H zFJUl{PJ2t*NBH|JY$_3HFZ-bBcN>lO@68V+q#6GN7_zNUot-$OiX1BEPpiO5A_tws zFHscK{~h`VV*}*4aMiUm>SdC%Yf8E4$K+t&rEH2r480Mv%1!ds6lh z=L3g!jz2w%ZHI-jwgg27t{iolx;&9fN^uY)(1_Z$T|^S_WhbH^{eumn;rQ#b z6h{|?Z;9(7SMUewTLB=<69BNzHTK6}Up5|vytuQ=eXLRIk_JSgyZ;O$ zQw=CG=Y#zxm-)h2;*Q!+p%H}F39Q}0j?#!y`At2kf%>XI3u5XD&w#~O&8IZt)Ru!T z1|q)iG`4dR%@fvzO%!$zDqBU|*Z{PJ$-p&*QlEI-Y08npSjxx2Ne0-MC-j`VmeqqW z?Ts5%=qVmGgFP~?=@` z-T|uYfsUYfI6o6So0_IC(D@-2>k++ls3xt3oQeTQnGyJQQ z7Eg&BURosd<+`JISJJ6}n+gw8>vgqWwH188VQ5ZK$Rzd-;wpQGpMfcGpjN>75xrU> znM|%C6MV!@LYzCNkLsbe2&9LxMW9FG5pk5eVB{aLeO9gQw5F-Wu(02uW&>l1Axf3P z>f+kKhg@%UtgY>UF3^eQq z(d9Skm6mYSNXE+K66zy`+Clybzy`G$lDCoCp(%x!!aR4zf_rE6w5WhECZ=s|;NGPP z5I+!ggP#VLyDjG7!DZW1VvMq-P)EW%Mo!_F;LsUed5wx7!xoP=kYfR`J18)U?w}ZD z8Tah=E`!{^lad}R@Hxh^OAm>pbf+!%Uco)0hs%sWV^(UEyDSS$=S`1;D-EI3o=Oap z0hgx9a!7aR&o@RLbH)RecNc&b{*?a2QM&;E-t zTxL9?fUsJAl?u9qe+0!Y6PCNYR9q-JlR^@q00oYEdUDBpkUEZA%G!~hUy4>>c(#Cq z?K}CFfCIr(`3p@zx}jVRadx}m+@*S6{lM~$xs`Inv({NC@UCSjA{Q|Aja8VS|3aSod5S!F>y?;K;0fH&bxl_@x z8p{+3piNoU5yC97M`;D*J??8n?pc0{pcS}>9?SaQNmgI){jYrhVsg2QULnzSTK>&ph4}w@IOhBt>~ zCq|3TvZ_GdYUza(6H9>Vo2zyV@ykmp1|TaX!5eWGJM^-EMt(A^Z9?LSxq z5jW$8JKZZm9AvqdUkapJ3XH)-qF`>(9;8cb&N8a3`t#%fXP7R*e(MmKDG*WIgGyex zrG8_JRJ{#2+IxSlI9raW=Vwz+K?SL5Q7<1=M%3vTe4@&A$Rrc_3 z(?TSY`pu*eO=KRBZ^7uxvN%;{qvL*eyAt$RJpZZjs`y0BFx*?3^Hg;kb|84w2prlp zpMe7Pvy{FHUCd38DbC7}|A?ECYJTePI*0(%{+>Oi6Z7$iUgkD&;y>4kodf=^4i{Sj z`y#G)W$~GTE)cN)Wj6=+hC;~C5ZzW zXpiC$*1xOu_lL0x@qh;2TOhow(*)jB?`oym*~DIf#B4j@BadsN@m`@#6u!I%h8LWL zt!t|j2t)?Dhx}zn7R!Yf4*TdX`kF#A}S);n_qfpgPf~=Tr{PmYjxe`e^MvU7ZSZ2mE4`Oz1S!)2UJQ;y2 z7r)L%v9AK-+c&9D`ME)4a7LUxio$4-#dsM_9jRs7wc6nzVN@02R;moI+~g$Fu9n3oD#7=jY8R#pcYw2-#w^6H2pu^h_4 z;I_b?_W9n>H{|sy=h0EW8(ad0;m8puxSWC(5$`F`cca;PF;zpIf*jvI(ouJyM{LyVu!cKHd6{Eg1I`ISP&;-{?K$gZz zsho5i2HB~kE&n})$`cP8Tle@#AOefJJRfFXk7tywDvv&JVmfJK_k-Wc6Dh+~j3q~4 z1LP9++>Q8^m<#Iyf0re~MGgKfu-Z92?&PJT4?i<%bY+YWV73Ec>!3|m+eP8217TMh z;1Wd`RyNM_AWM=*2BpFvTD}|v;_>f+VWstXz9YlJiA3}y<)SGB?g|zMlfu3e07l+bBRVc1zr4*Ux8J`U5^d#(I1H zf7(K}pZ{M{3Z{nv#h}f+8bR;}|CW@3S+;>&yUu;R`jWN%LsAMd5XYim*En1Db~6lT`d00Cq;C zhDL28&(4G&)qxO~sIbHjKiBQhc|j*y_gZ~3#T>i|9`TaNZ$gu#r1pX&l z4=AU_v+{-te>lvGrn0CjAEDb!;Y$63ngZhq})HOH;vgj)8K>Csmdr z3iP)$6)eaT30b?C#a*P5DoXw$6x4r<~bu zRpBbgMIIN=kGIEZ{XJ0yvq7!^FR}Wk3mEYCL={B1kqq(9HY@TX|B$Fc+@BBZK#&7C zNovpN_8tR@7z~_I5cJ8!7F6MxyuSjXBa8EgjAA#x7$OQ(u*D==kj?fL(AVl$?Mr(2 z8z4IJD%ebGU}HSL&Oe=s`oCX9kqcocw+k-BLR5ZTJJMfVuf1Xx~Xj#aUD4zDdX+m5J)GQ5&3-S zztQ?P7;&*24?#*LL>2|2kbr_0uJ0#ZJ9^L9sMRHM?+a8L|7=9Wk4S2 zc!9j&dE-NT$WR#7b?^S-A0t_PYWQ#L@;wxU7ZD^G#Bb|4II`s9?kt&Zf<3*%C`#?7 zLlY3))h2vMT?%^T)kKsB3;@nW@C}gP@Jj=#)#79(NO_-_Y1bVP%&otzz!IrD*(Lh*ed@N_EUn) zg86onr&L?xZzTfQJCHFI&wk5P^M8|75NuTgx`dCg6AIM+nydoaz6Ay=?Zh8ivbTRo zRv~72KPYf8s%a-B{Zty!QIsK!f3QWcgNw(-f9>CeTh2ic^^Ed-MiX=j#N|?zFu|0n z(`Hj0tl)XFvI7VV+@4gWOQsm1HhW3%h#v^E1D7k<33Z;lxe&f7Tv;nXPF8*EL+erc zSR^GQ-*$zeOjhyhp#rFMjHr-=XICbxuooFsNLArWb3~Rs3e{GDie484mWT`=ek&KK zlm_G1Z7otA9rZVJOL*&N1Sx-Frzk>Jihv(tY7lK9iz(dl14kg!1LfrGl>eVEr3icj z!%FLJ{!SUk2Nye#3Iv=EE}EfBn6BX&-x5g;oWHyo5N78Ddue%em*^H`cD%Taf76Rx z(2G&K%jM(~qsl#W_aDNUAJ9nNB!BxeQfi$@*WaoIc=AAur&GD!mMd^l5*aaX%V{(^ z#u6P+i)LY;`@nwOFPid|crQ=FxvPZ@rL?$60#-&X&h@iAj*g3`( zSBI78!>|i+N-sWB=936pAN97&2*chux9br&c^ITGfW%Ys)<)JLxM2eh1!)(TqOT=u zP`0FfXlbiK^a6z(MI47uQQhLAB-l!wt%BM_$gZ4wk`TD%y0uw4U}ASX_EMTPkTHZSfjDr?-^;60 z)eZhuBtXhcPRxZS~nl6NvORtk_KU`!0UP& zpFnJng!S1mRZ>NW;dh7v$-`E*=VhUW0HyS5u>B%e#gpa+8*)TOT)%d}iZ)$pPn4or zjHX%`2PMH&vs==LKoF(El=>^=Xq6gkPD~pn_Ek9N19E{j>cHQ&FS?Xp@;FO!D&)7( zfYQh|jQv65LVxwZ1`$f3_^loQDmq}1$rFD%(vffB1hoQVzWN5zAh>&7B)MUiC=L|c zh=Cn=*?n9~Tl6jgrNI^xG~lbT+rI zEuQ(CWhL@N4fI9>HH@_4@1~1fj$XsxFaUXzz(Aar$pA0jyp6O3<|iI?RPH1^kDTK$ z&p|ltE`Fm*)JzK)=uyrVHYDU0yr^Ssc^ZGYzmhae3`AYtt>&;o_Aq~%@}({%k~}R> z&k|OfdGIIM@|Gy0k|{TNWHkg1Uk7vDuhdOLsaj&`%QspqxK9j9l}Gh8t`hxN`MNgz z3?u6O4Z8oT_~}>^CY=u}nIDgNaPFd5ASft> zzxKf{p3AH6%(k6AUG(fih)wrnnIY*i{2>5rsh(+`zgzK0{|o8LAGlnOEO%g;5vdZ!pd%p(M2NmM0DQ!!E} z0k^@~{>Df0ZL*Qb)tYfkA2@>EIF#Ev0MUjOh>f z^oNk|Uy-jf#wCJK7SUfPGox}0ZNFkjns3MrrCP8*7%_c6OG%ZI2#*yNE|`x9m2Jx^ zRP9%&I`J(_zxF{28-pqlvcYw+RF}0NH2z6uFAPB^8XQzjR+LN1`6rfMLi|9KzWXq+ z5{;P_B_qU+=9B48;yP{^S?*SGJiP|hNeR9tta9nqS}Z0RK7>RNKL*=`j z-x5CH?$K}%m0c{r!wRllim@Ym5#wyX$c@HiiSb9+Q-Z~mNz4-R2arBNgWqa}$YOy#4@XAZ7v|(r z-TZpm#%*L{PhvTN1_yyP-J2WjDd%&JcV$+r@)`^qksRAnYD6irRLn!(iVZ=A{a}uS zd193Ef4hKed3s>=Y=tbzh0ZJEvdF6lddvkon^QqL<#FiqNlYr`Y(f8!=AlN(nrttq zwWd%tO3g(6-QO0rj4z)o2)-WmgG8%FVBZKdXO+LnB{fN4yLaFy>TL+WBIvsc4-ATS zV)CV^)xjL1RU;dG5|M2XAcoWo;)Uyo(I?K2u)3u`F?z@{yVBvZ#m|bE*MFTFJ>ct{ z7kn|FnJ5HC2r6e%Q1!U%F*>FqoO`h!w5R?O`^uykFw zx+K`rpDV7aL}2(GfI;#CrjA}$NUMZ8%=$m^gy%A- z>*E}iE(%%q=R~cjcp(&cRgX74TQ2`2n)IbDla~Ik?V<94T%|O#2?tAt0m#*huvQkGYc3sQpduHc8Zj;hm zP`ue&XYaO%!cOMA{jbR$5WEk7hM!PKE5Rr;@{iFasK=x5406fel?VG=+Fbix`XAc! z`da0D9N#&zFAWHurZ^{qjPkEIQoK^escg@Gvely5 zdMp*c*fG14^U#6kHhZ{q3KSBX>g{)@3~#l{9tPCPdH_pLc1v)ZpV4*3rvi4SDRli; zv!(NptPN3A5V(uG%b|GxE7%+N{?W0>aP8SUnbIf2FHx0v#=NH6Y_`)Vt7zaGTq4x= z@vaqI--28%V_5xmxDNX3XK^<-92zdzba(w$jSiZdG(k^2uKbmXVrv>3AWVeoSQKxok&dIS&G8h5jSV*Hb0`!JpoY z+0R^JOH*q(WsRShx6f@f?BAd`FR7o^Yx+EsP=;{0aG;m(EY0g{JoVG}>8?Tf+ z+x4Jm62MWCTij-K?S7~`w75=wSC(__k4+DCXm&bscY|(2>8jQL-ur}H)}pE`V+S{| zaJr6tP5jU2kwc?(F04^I{zvN3JJ-KRUQGT z({8wiZeJSQ<#G*hF|=KVjc=|OpZXE+W>>oVjT)D1+pSHOzsR9X4aOD@ z{Zd?}b)BCcxz9GVEcCc9^n8&TYJ>}~E_}T68~`jH;N^=nTvK7Rk%ieIzfW-0T7O-# z@s8U}8BIWv{`fq%@lOvnC{#HBuJHKJ?|dT}XTlZ6@eK~q08|c7@UnZyc034MoaSO=q{{AZZd>h22$JZY%PjC)c`kWZr|l?VDsFr`qRImPTkLr-1+z5 zRO?N{ROkQob=~n)x9^`5rIgY@$%vw|5|zzUA(;(3lwCHJy&6xlOGYFflrnO# zAzSv!-s7C#^>M_ZzQ5PYA6}gE`P}2Wuj{(6@qUlxT<}f?xNI|6zQjR)WldWEj@8#6 z+ECTdl^?xD#Z^j%>v>C?WYqN0Rp%Zn4rxf&n8~Lo#ws6;U$ogKK>-NdRiT0y?BXyS zRQ@hEW~M~Jk%X^IwI1&P&@=+hoFr=KL?aeITh8J(1-zS{db zK=c@h2gK7xP5WPfr6U38%=15=g*jsvJg7L_C&;<7$7ws}B`#YdLNQUyz#y#wq)ce# z_;z3nh|66Z3i7=R7%c8i0PoTafJ32CCp;wI&wFSscLBqkXtyU%mk@9qHct=M>0G?* zUwqU`=05Lc2WGQh6t23fM*vPzj}Lhhzk0FYR0R)k&o}@{;xcv);|xLr@F>D}Yokhr z^eciWd-ujp4~q#t9N}|Xo-g@H#jdiCSUu`~eD2~sT{God7KJsE3~hj@5~QWm;xJX& z4}eD3I*|Q@4d?~3Q*5cc(|#VEe87Gkc`41w4-ml!>Z;1=i}}YLTY;VtXh`a4eS9Z< zoR_<=0l5UgmKWI4+jP4AIQg6=GVX^sGvHZy|55b1ejCjo|9)y;8P)Zc|0309=wHRw z2WyA&X&z0QU;}zK9AFTCOsX2HM{5CZ5=|=*cy_9 z8$WS``)8Ry8&4X-?ia;S(+L!oVbgv_mT2H9g&$dodl(Rgxmsy1uWbhJ0mh{V$k^O{ zfQ;)o8Xv-Nm(8>Nn;Il?^%NUr%6mBrc-GF>^}8SaDmd2tLH2&OTZ49Q*{NobS_bAC zSTO%|rMm}MWImjIxsZOZ{u9tj>WN6OUe5%OG0A?f3+*SlxP0W^I|6>P6b694rJ`qt zVdDdVdql4aI0k8$+p???Bzf6`Vu7~Z(>%Dc9TyO_+0q(*%W78MDU9LO}L zd*&J0pr`-K%5G-p9FSB1@v5Zf93bcdMocg8R|pNqeD3-P)D^|&?m8~bdQ|~ql%EU7 zqJIlm9Q6gAUM%40usM^S$?*4d0C4Ei;)-*AWT|)SSa?*V`+><7?UWW1!@ivv=Rrlc zC2)A~gNx3Fx;LCf@w*dAH(MKL6<;fipp*34cA+mB;m$?4b%FbC6o7;21n%1an;DDz z+vN(L>YG}h0b+?e{dWGNbS1Ob196M7?(XhGk4vxgYIVN${65z_e`qKxa)BgRDcnYB zO!Zf^P1Wd@kQ5^Z^>FQikLB=clOq_VolUPy`r;c7ouJvvj2>^`n zq~jsvN}A~WVkaPFefdC6bY+@TBlGymo(rwxU~Qb0;%>cmsJI1c#j5gLV?i+)QC#+9 z8L$*dV3L#X7t>h-&@=6dZ0@&jWV{ODHuU8VIIf7?p;((C^Xr?npszi#;M#@b9C;5D z$~dJm?wnbBdX22$E0I*3a+sjuRBWS3UAq50r%V_RZe`BzrWtVV1p`);Nm|=Opd*$G z<~?;i53E}5qCTSg5WMMbpksI=;Z{9rK?Z3nCMRWX2sMgZG(fy3Rly1vhb&UYP@E_{!hRcV)|KLMRxi`a0 znezgM&D5HCF{IPqO)`yB<EytO9&`uLKJ#S)WK(TYGCPl%4p0bzJY4+gCO$X3FP*;^97q~=N zI8?LCcKWHm&@Q^B<78`Vd$tP@eCSCfKqgIF)6@GVcBkU+o==%Ok8k$bT*UNTS$v43 z(|*KFIyDWMeT$p^HadJ9y{A1&BaAcF&&jf~A%*)H4hVh|z&V%&R|0mb1K3+7ypJvb z3Cd;n^X8p-kv<}sWjmE)R4npIsi%h<&3+`@*#TCCLU6>qfKu}EOX{B;07NcuJI^Jp z-HvUlgd?r>tl_utBRHAPovd&64R<_f)OcMg#X59}vW>#c z={+473GS)Utg$LjT>*qIPxm#T(3|UDen#XfwCgxVgqJDL&{pW**o{-yb0e>vV6rjd z;e*V1mG<#^!xDQK>d-er0#2AYs$sunN$dP)HZyp%F($26g2AaKLi|NChel?fZ|Ou| zRcIF2g($6FHJ}VNYKQ?4uc|OnQ7g9E{HNs9`XVf$KB8EgnW1_q%;dT4aBOba9a4J_fqR0~mBpk4Q(;BNQEyhEs zK-i&%yfNYYsz?c#rKjf5p4U{X?V68mh;p>9xOH7Au`lpW`}AxV6^EMVNKN96VfH}3 znXeaObdlTZZ4(qFv4r%f6FtiN%3|Ec=&!SdpCM2$LyW$?6)}t6Cw?_97gY^)@1bw9 zo}vhetvme%BdE_IMP<9Z@@~vA)iP9xLvp5MQd3pb!Sc2;iX`cj?@$U@-1@O{Cq&3# zJ6U$WK!H&?J(#sQJmPwA=gGA~kPl&Eb&@WzEe&IrwazcKI0_WM!c=Gqm=%5+xU8O% zS;!IQOga7|Rv^Vxbw6{{y%S9$4yhFSfYTfNSPXnSM0x8BkAMD3A*d^sJDU=}=kVF% zQ`eq}*^%Dd@Eh>10zPEHtAgmC?O`!RJFJ`1+fs*9o~vJ|R5{ovvsBk1s(wo|(PF7~ zTh@b;-H5v#{(}R;vJF{!3sgzvqUMVp zCH10}tcQPmQZH7LY6;gUj~2N_7Vz5f;I=btKI|E#*qmi?kjo+xQ>C5EZOex=;$JaEHRaG^r^EV{tI zY%;bvxL#(Dk8@Bd(BW_M-^^X#`w&rAn#>R8&iAB=;MiO|%GvZC)HHQM(i|$ab}T;( zlhbmD`x&NWFgCtN^a(2nal%7a$}rsVIdGK%K&qoQ1A-8o8zto2!~_L~ib|zOAsZ04 zAn2?tqhT{%u-C{qS`@=#+Ilzgp`$3y-qmYIS;J3S=lw_|Bs?>(f1<-pSLh4IP^tr; zkcg5ITP^O!o1N7)6LTbm=ii!{5S$Ud;Wg#c_UQ&X-~_qS%~sK+&qr7bI(ux{G{dy0 zLd~49j`;(;NqWQMUPAvC!xSK8hgEJXSkIh^eIhw{X)UsWoO@=HU=fsqnFO=k*w&lw8x!*t4b5Ph%e{I7}I$i!ZrFywG%VLCYIZ%!V2_D%}y7h2P zVoHdolCos+s_O>1%GL%KLr*b3Nf(QBe=`w=#-e1K^GP|)&ZLGFQfd|K)kVrgpDjmv zt`I1u)wReABo87?CavR#nhR`j@u(Y%AZlRp>y{(@Hw}bEWLGFoLib>Q0Z<{+kT^YaKg`-eA5`fVvtMG4GT+*B+~v z0K92%=SdlkyEHWOpa_7JO;Z%Z?1ZnEA3mFQ_3Bm7(7-(Utip`~fQ-^dGlyB^-l&Wy z@&QIRNS|Sp3nKFbCx^(@cxTUPl*4E{qT3z}2+4Uz1_)c5rBdum)ao&Sa1JX~tK*z- zcX076&@c&2e{%$a@aV<2cP4wJfIV_f_QFbPL`1}BHO1kxBLxTKJ4~~j9ymvF@}&KU zD3)UNYBvbmHgvY>D5{WYA7v-_-3L@}`%^tGwI=a+x37PrHPF3MN^s)G*P?Dxk zpufNH(nuYTLDh%G8>Q4UFO72OV;n7?QT)=m*bwM-F6Gfa!}e!x$?;c>+Ud8{lqRpY zl^Lob1GrY;UTmtGbKm0ruIxg%2sa7z03lTx*dwR;pG%+hb5<&Nz25io}wdI8HNFci|GIQuWTB_LCHcB~8Y=73o_|(*EO6PxTz=1LJ3Qz8*i!YuL}{V9A!T{mUE%=La0JlJ(bWPDQ_&h-A=6FJ;k^9nD-< z2tC*Hw$!{p^*LD&C(5%f-ez$xn}LjbRkK=qakq^{i7_2>L@pU{&Z`RPm7x>$#l;ID zD1E4=O-}IF5e9Uc+l05s;yiZ6`d0El-8*SfkCCAsKS|bn&EC=$<2vg)*$W<7tZ!`= z^+O(nUNAE)P^Q$Qv#LnbnJ{x4{+|DMP9?)q4=xwFPau?>wwgA% zH9oWc@y7e_=(c)Mu{rb$M*qs5G6eu$94G_3xpo1jdL18OZk}U(Bo~Eh(aK{GU*NAm z6ec+LhD}NDETlKx6CqFkgPbNp|i79un^YL7f*HY8D##^>4Gnm}@uBi^YVXsSjSE_X{`Wr=Q zLws`ysKU=WEpu_@Og{o@FMATF{jQ43gOw{ow_?wQThvSKmgO^)B%_JNZnNMh*XImP>F5^I?EtL!m6S%!+<^!Nc;}ul z2AvU*lN?a9g>50Gi@&O!v088i)t+W>n&+zVfs43xVR00Y9SbDo?4VCKs0gxhp71E| z%BS%WG6Mw)e4>hh8s}$=Xq_Qwpse|@=_zI3QQ_d|6wAB zXbzvbt#H{6!pDN!ou@+32>aq?aR|%Gkhn_H+ZszrNmZ04L%ci9SoKKrwK1;?nsbBE zN`x9(B(H5&^}q$TCW@y%7pXiuJ3=|+?Q(HWI5h>hZ37J z-}U*mEo{8UL35xugIogkb$%3&@sIWwnQgZH98>+((h`6=edu*lbePi-2I;H0`?Ly6 zUY&}U@Q_QE7uA?wp2E?FQqVOlbTi-#fGSDL5dGnU&EUKK?uPBO#y=qXoUSen1hj+l z!ZU_P8T$$z*G0>R@>@v&-5-;m$BQHC@tTg>%Z1co(r?|dFHd-kVQ`~v+ahuAI2_)~ zg)X;R$p;1o>aj@bA4X9c=t}^#yr5N|`nz}ULQERY@ua6#L0p?Q4G5;yBZmeqlYfQy z$C96X9|Xz0X-%g6X-bm;RlDd~f5pN{o`_W!ke@nXqn;4&CgW4ezSOR_BweoQHnI7z zJJ0_+aPX!Fv%m0MKRf-Yi@9!#5bHabuhOdr;fleMiiaJJ)n$)|W9qnBs=;jeh^S31 zo#9E-?nB4tzFJVp;L#}x@fYP(NnjMI3SBB)c0S=Y!QK4jsZmkZebuST?D_Q6*+z^U z+R~^!JA*q3yGUD*QWPNL7WCP1=^W2DjCgc_UqPGUujZ5v zPyt`ZyL~XeL*fw&%|=|sPfXiV-HhHBR>)irI{f|EZ%3fS)rEu5H-*2uzNad=L_z}u z_vQ{wQ@DzjE}eGurYOm-D^0=@`zM8`D*O}!LPecNwQ!4ejdAM68G6+fv6UCbs8$xV zWX80Qt9d=<&NZwFHrpY=im(QFRz-*_I*0U@`5P6fr`$VaZFHBMx+>n%y0>hS<`jp< z@MBOJzv@J} zfk$@Bt{9-BMGuB%r|}ilhJ0Sml45R`Mn&^f@j)aVmvyz}Vau<`q2tV+a>=!|=eQyw zv=yh($%^QEG@ksF{}rZORnN&R6VniFNLTx{mrEmadI%k#-t8U2T(dl+d_i;|{Hyc) z*F?(Z2O~c|&hFr`W;=cQ>`GK!)?5Qno{KX}I(@dEdmHAIIX>nbwopV{*Y1pK>oNV+ z2)wohPmW}~%s+3^Amhd_h0e76|%zWz~eDs3}OUJ-<5~r~WlwNOrqHwI-MfL&Q1qRVEJkyMt^?U1C5&Q^=Lm{jzsq zDa$4CK?4RyEpC!rQ$#sd`Fd(X_JS9L{X*^;A7E+Kx_&S-8dEcktdb)a z48K*;3B>q~%#^Wly$T4RfB54=549T0V@SRl&5a7bboxi5+gI|a#Z6Fxr^8N1bL5U^%&VJy3F{xZ*zQre}4jDDW>iI5@!;cr-3<>B@rGJ zTKqcKr>(weNB)Q#$q>Czs7X1v=SXsS?Ypp?#i8<)7!1V!s5Na^CO^eHJDQ(0=_=R} zEgyEoqEXdgW!(1?R;R#iTkcfo7z8_i!pj4L_B~g=MIJ34)d-%bpPayoeaBKh(6x$y zl>z%UQp)Na57Z&mLQ|IuG1!t0S~D4sgX3&kP1BO1ndV`R6eD#8EzQzy29dQdm@yXL zuNCj17n%mHVE)OHZWdX)#IhSP(!+cN935k#5|u*$zhs4XB{#F~_o_k03z z>PB1TBW}C{f50jXcfMwUvRiyP1J()ghd?<{4Ohh8@3R?#WcY4$3CbF-Cu9rfe{_a9 zD{>dH%A`A&zAOZb;roNz8}h62xKZoIf(o0{cdv;bdZOF98sG_*$@}ux#TNT#=fSHa zLE$mrzIznWCpmP&5bI87=&=kxCWcF$xe_)FWR}! zB^75Mzep6GEYX5YwYvxBq6Pb8ONS^0Fy<%MXtxA4PEgJ17MdmXkKeRPkq6ZUheE)3gg<{a|6 zqV}KsW4~hoo`QNSu`<27u1t0Mvub*i)ag4w|N!s%DoOht}3;U#}wp!!_>#SK`Vcez$HyXM?_oHmj z3qDo&WOJD6Z7)YV{+E!Q&2Dv}?o|AFjtoV;?Cj>x^^(|`PU?rJw8|HO^lGZF(#vS_ zdvn|iQhj&#aL&4T3+!ZXIWRZB%}7a&)%gMhBT6wJj-AoWND{i(5Tu~P7XPn8H}B(3 z0-<0=yq6$~p+JNpr6y)!c~Kv0Gty8fN}JI$2pYXfPc^5x? zJN$7QIVxy8Z+?@2yR*1`!ysjgXE>D&IoBF-jzsK1`oj}-WNeX@9?45z#h9vIN zd3U#lsgUw$B$y7M2MDUHMK)_RekGos?!5K)rLKXdRF6@A{?+c))t9?2lk;uucTp%o zH9aRH&$wu?>81*=N@BiESe@>E8P?@ER~h%tgYR8#ta|`!v)b^z6dYh(y6g?)@GYj3 z9JieboK0l%_=-tLJ^;$b%Hq-A?Fk?wqa-7n5g(8S5wWk#dDs7bWsb361IZu5|qLgBCU&~bN$n+Uu0B9%@Ro1szt+}hU*C9XC?aPdT5-; zb%b1HYh~(53D-q&mqL~Qc(!_TGY8HPw7Nk>7YuKK{JsM!gEHZ-)xK*^EYe5Vuki_A zpR22$XZQErJq@e((IM8Iplr^ZSbjbHv$idToa4O!k)b@WyyYodBhT(oS9{9t_0?m( z8pMBsTmSCZVDyy(;S=aUf5IP*OQb@7FP)9jgr3Po+Tn*j>oAhEaxGw1Ot zexUlji)-5%u1_3EFkncmkOb9IquEh=4vcSwAtu<`L8O@wM~B9r6o=O?fA$n2UW?dg z=I_!?}(R5(ETOGAg&OR2scCH?+K!?sqqI61&e z+mzqiq!L~GIKl}i3P^$xuQ!m z@EPE>w!)3%BV#4%-X9CcHC_l{T3XZNo4W9z2IZ32A%e&9>VPouI4xW?Ger$;srMsn z*~vQsO^QcIajS!Oo!<3ftBzf(T~|Q*B}fY-H+J>zUjQ$iTvL=!qHbk*2@(ti9vz-e z^5!>d@~XiIgQgWx1M_3PN~=VG8%Z%xIveV4t(DNNdi81H^Q1TNcImg4U+>5IfQ*hD z$)Eh*1b3-C)jxOSd+e7lUPSh@t@q}~SB90Is&0G%`DS>=mKNH2#9(`lkM;OrE;!6P zQIaSLeg(0bxr`q7}$PH-@NO?9N~O5ZSO_&Kjt z!+(er%NKX?pTbOZJF6C-mD$+#Z$hLu3A%h&m2#9;IQ9^g5{b$Q+|;4GMT~;{?`h*z z2hTnQLhA?PJsD1>fK?t2vGfy*MVW5ZWP~dohkCl7xC}S6SX6-@#NSRY(%1&MD(D$u z5?j#P*8T=z^TsblaI6dT>ux;?ym4Qku6hzvl=F}LCS@)*wNGIke+rr05W8bJ zdQ7hOP}|oZjQ?e$;2mn2_qjSqz(fDd=B`HGC}u?ssh$l9MK*U$M})sXEqD?^U^|W0 z){dZ85K7)xk*Em2Rmbw$7?iCyQTM#hRpLwk;FmjPXZqjO{=Sk3F)VZ5Nr_!F zWNQR8u;Q+#Q6hhCr(y6eawe2#ZIp{^_~zHUQjrm^=r=>xi_eK7_no@UXh?Ez-Eg)60 zCltadZ@;2F6~4@MbhR+pP!6@;WB+>+Yo2-!^zbm}9X)79z9C9a89R4x))k1-wnWh7 z(%K(x8S!(MP31#)keFmD76mQhsXvd#Z{L&AmmE|!=c`}%q;&&lCn0|Vfh;o?gE=|+ zxMhO|43s!tffI=FAa#k>Q$S~IYT4X28ywq)I1MpV4Ad`zw=2JxY4@9^&Y#5`j z5qvJL@E2Y<)sk=HkNbu&t%*trKv13WnO`eygJBzL{snuJOMwQ#T?;_tiJK>ZTGhIDbD1#!Zda|{X7OwO2$_11k;RAqxA73h2RPpe z-#UD!@ZgG`Pu1piA8>a z{7J&5c4c?q)lj(^Uw(&NGEZvbWxt=Yu3;2bq2(Q|s#YC}g?$N|gap30W-S>>LBkx( zzITQ${`93(4U#@(VH>&7}Ze?#|X5;DxIjuY(2ltDCx-7hG9Yj!wUzc zZk(!q^}DjCKp}0~m9@LAi&z;6mVt-}hg)$IWG5mb+K!xnZA1>IsoxQs>UzOnh@`~j LF2`NG{pkMy_Jo%J literal 0 HcmV?d00001 diff --git a/.attic/nodeimport.png b/.attic/nodeimport.png new file mode 100644 index 0000000000000000000000000000000000000000..db51bf58be733dd009e830bb5df99a9bdffb18c3 GIT binary patch literal 207996 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

); } diff --git a/create-a-container/client/src/app/Sidebar.tsx b/create-a-container/client/src/app/Sidebar.tsx index dacccf5c..e5ff88ca 100644 --- a/create-a-container/client/src/app/Sidebar.tsx +++ b/create-a-container/client/src/app/Sidebar.tsx @@ -1,5 +1,6 @@ import { useNavigate, useLocation } from 'react-router'; import { + Button, SidebarNav, SidebarNavItem, SidebarHeader, @@ -16,7 +17,6 @@ import { Settings, Users, UsersRound, - Container as ContainerIcon, } from 'lucide-react'; import { useSession, useLogoutMutation } from '@/lib/auth'; @@ -38,12 +38,6 @@ interface NavLink { const PRIMARY: NavLink[] = [ { to: '/sites', label: 'Sites', icon: , match: '/sites' }, - { - to: '/containers', - label: 'Containers', - icon: , - match: '/containers', - }, ]; const ADMIN: NavLink[] = [ @@ -111,16 +105,16 @@ export function AppSidebar() { {isAdmin ? 'Administrator' : 'User'}
- + diff --git a/create-a-container/client/src/components/FormPageHeader.tsx b/create-a-container/client/src/components/FormPageHeader.tsx new file mode 100644 index 00000000..9c7e2d2a --- /dev/null +++ b/create-a-container/client/src/components/FormPageHeader.tsx @@ -0,0 +1,48 @@ +import type { ReactNode } from 'react'; +import { Link } from 'react-router'; +import { ArrowLeft } from 'lucide-react'; + +export interface FormPageHeaderProps { + icon?: ReactNode; + title: string; + subtitle?: string; + description?: ReactNode; + backTo?: { label: string; to: string }; +} + +export function FormPageHeader({ icon, title, subtitle, description, backTo }: FormPageHeaderProps) { + return ( +
+ {backTo && ( + +
+ ); +} diff --git a/create-a-container/client/src/components/FormPageLayout.tsx b/create-a-container/client/src/components/FormPageLayout.tsx new file mode 100644 index 00000000..0e6065d4 --- /dev/null +++ b/create-a-container/client/src/components/FormPageLayout.tsx @@ -0,0 +1,66 @@ +import type { ReactNode } from 'react'; +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@mieweb/ui'; +import { FormPageHeader, type FormPageHeaderProps } from './FormPageHeader'; + +interface FormPageLayoutProps extends FormPageHeaderProps { + /** Optional supplementary content (tips, docs links) shown beneath the card. */ + aside?: ReactNode; + /** Card heading shown above the form fields. */ + cardTitle?: string; + /** Constrain the inner content width. Defaults to 2xl. */ + maxWidth?: 'xl' | '2xl' | '3xl' | '4xl'; + /** Form body (fields). */ + children: ReactNode; + /** Action row rendered in the card footer (typically Cancel + Submit buttons). */ + actions: ReactNode; +} + +const MAX_WIDTH_CLASS = { + xl: 'max-w-xl', + '2xl': 'max-w-2xl', + '3xl': 'max-w-3xl', + '4xl': 'max-w-4xl', +} as const; + +/** + * Centered single-column form scaffold for create/edit pages. + * Renders a polished hero, a flush-footer card, and optional supporting content beneath. + */ +export function FormPageLayout({ + icon, + title, + subtitle, + description, + aside, + backTo, + cardTitle, + maxWidth = '2xl', + children, + actions, +}: FormPageLayoutProps) { + return ( +
+ + + + {cardTitle && ( + + {cardTitle} + + )} + {children} + + {actions} + + + + {aside &&
{aside}
} +
+ ); +} diff --git a/create-a-container/client/src/lib/useDocumentTitle.ts b/create-a-container/client/src/lib/useDocumentTitle.ts new file mode 100644 index 00000000..12c0efd8 --- /dev/null +++ b/create-a-container/client/src/lib/useDocumentTitle.ts @@ -0,0 +1,13 @@ +import { useEffect } from 'react'; + +const SUFFIX = 'Container Manager'; + +export function useDocumentTitle(title: string) { + useEffect(() => { + const previous = document.title; + document.title = title ? `${title} · ${SUFFIX}` : SUFFIX; + return () => { + document.title = previous; + }; + }, [title]); +} diff --git a/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx b/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx index ef5422bc..a8f54f2d 100644 --- a/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx +++ b/create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx @@ -109,7 +109,7 @@ export function ApiKeysListPage() { onChange={(e) => setDescription(e.target.value)} required /> -
+
diff --git a/create-a-container/client/src/pages/auth/LoginPage.tsx b/create-a-container/client/src/pages/auth/LoginPage.tsx index 32fc9f47..e90825b3 100644 --- a/create-a-container/client/src/pages/auth/LoginPage.tsx +++ b/create-a-container/client/src/pages/auth/LoginPage.tsx @@ -3,9 +3,27 @@ import { Link, useNavigate, useSearchParams } from 'react-router'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; +import { + Alert, + AlertDescription, + AlertTitle, + Button, + Input, + Spinner, + usePrefersReducedMotion, +} from '@mieweb/ui'; +import { + ShieldCheck, + Smartphone, + AlertTriangle, + XCircle, + Eye, + EyeOff, + Lock, +} from 'lucide-react'; import { useLoginMutation, fetchChallenge, type ChallengeStatus } from '@/lib/auth'; import { ApiError } from '@/lib/api'; +import { useDocumentTitle } from '@/lib/useDocumentTitle'; const schema = z.object({ username: z.string().min(1, 'Username is required'), @@ -17,6 +35,7 @@ const POLL_INTERVAL_MS = 2000; const POLL_MAX_MS = 5 * 60 * 1000; export function LoginPage() { + useDocumentTitle('Sign in'); const navigate = useNavigate(); const [params] = useSearchParams(); const redirect = params.get('redirect') || '/'; @@ -24,6 +43,8 @@ export function LoginPage() { const [challengeId, setChallengeId] = useState(null); const [challenge, setChallenge] = useState(null); + const [showPassword, setShowPassword] = useState(false); + const [capsLock, setCapsLock] = useState(false); const pollTimer = useRef(null); const pollStart = useRef(0); @@ -104,53 +125,124 @@ export function LoginPage() { return setChallengeId(null)} />; } + const passwordField = register('password'); + return ( -
-
-

Sign in

-

- Use your account credentials. +

+
+

+ Welcome back +

+

+ Sign in to your Container Manager account to continue.

- {submissionError && ( - - Sign in failed - {submissionError} - - )} - - - - - + + {submissionError && ( + + Sign in failed + {submissionError} + + )} -
- - Forgot password? - - - Create account + + +
+ setCapsLock(e.getModifierState('CapsLock'))} + onKeyDown={(e) => setCapsLock(e.getModifierState('CapsLock'))} + onBlur={(e) => { + setCapsLock(false); + void passwordField.onBlur(e); + }} + /> + {capsLock && ( +

+

+ )} +
+ + + Forgot password? + +
+
+ + + +
+
+ + + Create an account -
- + + +

+ Protected by push-approved sign-in.{' '} + +

+
); } @@ -161,44 +253,90 @@ function ChallengeStatusView({ status: ChallengeStatus; onCancel: () => void; }) { + const reduceMotion = usePrefersReducedMotion(); if (status.status === 'pending') { return ( -
- -

Approve sign-in on your device

-

- We sent a push notification to confirm this sign-in. -

+
+
+ {!reduceMotion && ( +
+ +
+

+ Approve sign-in on your device +

+

+ We sent a push notification to your registered device. Tap{' '} + Approve to + finish signing in. +

+
+ +
+ + Waiting for approval… +
+ -
+ ); } + if (status.status === 'unregistered') { return ( -
- - Device not registered - - No device is enrolled for push 2FA. Contact an administrator to receive an invite. - - - -
+ ); } + return ( -
- - Sign-in not completed - {status.message || `Status: ${status.status}`} - - -
+ ); } diff --git a/create-a-container/client/src/pages/auth/RegisterPage.tsx b/create-a-container/client/src/pages/auth/RegisterPage.tsx index 9f043679..90c339e5 100644 --- a/create-a-container/client/src/pages/auth/RegisterPage.tsx +++ b/create-a-container/client/src/pages/auth/RegisterPage.tsx @@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; import { api, ApiError } from '@/lib/api'; +import { useDocumentTitle } from '@/lib/useDocumentTitle'; const schema = z .object({ @@ -33,6 +34,7 @@ interface RegisterResponse { } export function RegisterPage() { + useDocumentTitle('Create account'); const navigate = useNavigate(); const { token } = useParams<{ token?: string }>(); const [invite, setInvite] = useState(null); @@ -106,11 +108,13 @@ export function RegisterPage() { } return ( -
-
-

Create your account

+ +
+

+ Create your account +

{invite && ( -

+

Invitation for {invite.email}

)} @@ -131,7 +135,7 @@ export function RegisterPage() { hasError={!!formState.errors.uid} {...register('uid')} /> -
+
- -

- - Back to sign in +

+ Already have an account?{' '} + + Sign in

diff --git a/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx b/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx index 43412ea0..05ab2502 100644 --- a/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx +++ b/create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import { Link, useLocation } from 'react-router'; import { Alert, AlertDescription, AlertTitle, Spinner } from '@mieweb/ui'; import { api, ApiError } from '@/lib/api'; +import { useDocumentTitle } from '@/lib/useDocumentTitle'; interface RegisterState { uid?: string; @@ -12,6 +13,7 @@ interface RegisterState { } export function RegisterSuccessPage() { + useDocumentTitle('Account created'); const location = useLocation(); const state = (location.state as RegisterState | null) || {}; const [qr, setQr] = useState<{ qrCodeDataUri: string; inviteUrl: string } | null>(null); @@ -41,11 +43,11 @@ export function RegisterSuccessPage() { return (
-
-

+
+

{state.status === 'active' ? 'Welcome aboard' : 'Almost there'}

-

+

{state.message || (state.status === 'active' ? 'Your account is ready. You can sign in.' @@ -61,9 +63,11 @@ export function RegisterSuccessPage() { )} {state.enrollmentToken && ( -

-

Enroll your second factor

-

+

+

+ Enroll your second factor +

+

Scan this QR code with the push-notification app to register your device for 2FA.

{qrLoading && ( @@ -85,7 +89,7 @@ export function RegisterSuccessPage() { /> @@ -98,7 +102,7 @@ export function RegisterSuccessPage() { Continue to sign in diff --git a/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx b/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx index 9ae96a2d..1687b9c2 100644 --- a/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx +++ b/create-a-container/client/src/pages/auth/ResetPasswordPage.tsx @@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Alert, AlertDescription, AlertTitle, Button, Input, Spinner } from '@mieweb/ui'; import { api, ApiError } from '@/lib/api'; +import { useDocumentTitle } from '@/lib/useDocumentTitle'; const schema = z .object({ @@ -18,6 +19,7 @@ const schema = z type FormData = z.infer; export function ResetPasswordPage() { + useDocumentTitle('Set new password'); const { token } = useParams<{ token: string }>(); const navigate = useNavigate(); const [username, setUsername] = useState(null); @@ -75,14 +77,14 @@ export function ResetPasswordPage() { } if (tokenError) { return ( -
+
Reset link invalid {tokenError} Request a new link @@ -91,11 +93,13 @@ export function ResetPasswordPage() { } return ( -
-
-

Set a new password

+ +
+

+ Set a new password +

{username && ( -

+

For account {username}

)} @@ -123,7 +127,7 @@ export function ResetPasswordPage() { hasError={!!errors.confirmPassword} {...register('confirmPassword')} /> - diff --git a/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx b/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx index 379e9a4d..22f5f4fe 100644 --- a/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx +++ b/create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx @@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Alert, AlertDescription, AlertTitle, Button, Input } from '@mieweb/ui'; import { api, ApiError } from '@/lib/api'; +import { useDocumentTitle } from '@/lib/useDocumentTitle'; const schema = z.object({ usernameOrEmail: z.string().min(1, 'Required'), @@ -12,6 +13,7 @@ const schema = z.object({ type FormData = z.infer; export function ResetPasswordRequestPage() { + useDocumentTitle('Reset password'); const [submitted, setSubmitted] = useState(false); const [error, setError] = useState(null); @@ -33,7 +35,7 @@ export function ResetPasswordRequestPage() { if (submitted) { return ( -
+
Check your inbox @@ -42,7 +44,7 @@ export function ResetPasswordRequestPage() { Back to sign in @@ -51,11 +53,13 @@ export function ResetPasswordRequestPage() { } return ( -
-
-

Reset password

-

- Enter your username or email and we'll send a reset link. + +

+

+ Reset password +

+

+ Enter your username or email and we’ll send a reset link.

{error && ( @@ -71,12 +75,12 @@ export function ResetPasswordRequestPage() { hasError={!!errors.usernameOrEmail} {...register('usernameOrEmail')} /> - Back to sign in diff --git a/create-a-container/client/src/pages/containers/ContainerFormPage.tsx b/create-a-container/client/src/pages/containers/ContainerFormPage.tsx index 8dc5d019..9b01dafd 100644 --- a/create-a-container/client/src/pages/containers/ContainerFormPage.tsx +++ b/create-a-container/client/src/pages/containers/ContainerFormPage.tsx @@ -8,16 +8,21 @@ import { Alert, AlertDescription, Button, + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, Input, - PageHeader, Select, Spinner, Switch, useToast, } from '@mieweb/ui'; -import { Plus, Search, Trash2 } from 'lucide-react'; +import { Container, Plus, Search, Trash2 } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; import { keys, queries } from '@/lib/queries'; +import { FormPageHeader } from '@/components/FormPageHeader'; import type { ContainerCreateResult, ContainerMetadata } from '@/lib/types'; const SERVICE_TYPES = [ @@ -61,6 +66,10 @@ const COMMON_TEMPLATES = [ 'docker:postgres:16', ]; +const sectionCardClass = 'overflow-hidden shadow-sm'; +const sectionHeaderClass = 'flex flex-row items-center justify-between gap-3 border-b border-border bg-muted/30 px-6 py-4'; +const sectionContentClass = 'grid gap-4 px-6 py-6'; + export function ContainerFormPage() { const { siteId, id } = useParams<{ siteId: string; id?: string }>(); const isEdit = !!id; @@ -95,7 +104,7 @@ export function ContainerFormPage() { const restart = watch('restart'); useEffect(() => { - if (container) { + if (container && isEdit) { reset({ hostname: container.hostname, template: container.template || '', @@ -119,10 +128,13 @@ export function ContainerFormPage() { authRequired: !!s.httpService?.authRequired, deleted: false, })), - environmentVars: Object.entries(container.environmentVars || {}).map(([key, value]) => ({ key, value })), + environmentVars: Object.entries(container.environmentVars || {}).map(([key, value]) => ({ + key, + value, + })), }); } - }, [container, reset]); + }, [container, isEdit, reset]); const [metadataMsg, setMetadataMsg] = useState(null); const metadataMutation = useMutation({ @@ -189,11 +201,17 @@ export function ContainerFormPage() { environmentVars: values.environmentVars.filter((e) => e.key.trim()), restart: values.restart, }; - type UpdateResult = { containerId: number; jobId: number | null; message: string; dnsWarnings: string[] }; + type UpdateResult = { + containerId: number; + jobId: number | null; + message: string; + dnsWarnings: string[]; + }; type SaveResult = UpdateResult | ContainerCreateResult; - return (isEdit - ? api.put(`/api/v1/sites/${siteId}/containers/${id}`, payload) - : api.post(`/api/v1/sites/${siteId}/containers`, payload) + return ( + isEdit + ? api.put(`/api/v1/sites/${siteId}/containers/${id}`, payload) + : api.post(`/api/v1/sites/${siteId}/containers`, payload) ) as Promise; }, onSuccess: (result) => { @@ -215,7 +233,11 @@ export function ContainerFormPage() { }); if ((isEdit && containerLoading) || bootstrapLoading) { - return
; + return ( +
+ +
+ ); } const domainOptions = [ @@ -229,161 +251,269 @@ export function ContainerFormPage() { ]; return ( -
- - mutation.mutate(v))} noValidate className="grid max-w-4xl gap-6"> + mutation.mutate(v))} noValidate> +
+ } + title={isEdit ? `Edit container: ${container?.hostname ?? ''}` : 'New container'} + subtitle={ + isEdit + ? 'Update the container, its services, and environment variables.' + : 'Configure a new container, expose services, and set environment variables.' + } + backTo={{ label: 'Back to containers', to: `/sites/${siteId}/containers` }} + /> -
-

Basic

- - {!isEdit && ( - <> - - )} -
-
- -
- -
- {metadataMsg &&

{metadataMsg}

} - - )} - - {bootstrap?.nvidiaAvailable && ( - setValue('nvidiaRequested', c)} + + + Basics + + + - )} - {isEdit && ( - setValue('restart', c)} + {!isEdit && ( + <> + + )} +
+
+ +
+ +
+ {metadataMsg && ( +

{metadataMsg}

+ )} + + )} + - )} -
+ {bootstrap?.nvidiaAvailable && ( + setValue('nvidiaRequested', c)} + /> + )} + {isEdit && ( + setValue('restart', c)} + /> + )} + + -
-
-

Services

+ + + Services -
- {services.fields.length === 0 &&

No services defined.

} - {services.fields.map((f, idx) => { - const svc = watch(`services.${idx}`); - if (svc.deleted) return null; - return ( -
-
- - -
- {(svc.type === 'http' || svc.type === 'https') && ( -
- + + + {services.fields.length === 0 && ( +

No services defined.

+ )} + {services.fields.map((f, idx) => { + const svc = watch(`services.${idx}`); + if (svc.deleted) return null; + return ( +
+
+
- )} - {svc.type === 'srv' && ( - - )} -
- ); - })} -
+ {(svc.type === 'http' || svc.type === 'https') && ( +
+ + + )} +
+ ); + })} + + -
-
-

Environment variables

- -
- {envVars.fields.map((f, idx) => ( -
- - - -
- ))} -
- - {mutation.error && {(mutation.error as ApiError).message}} + + + {envVars.fields.length === 0 && ( +

No environment variables.

+ )} + {envVars.fields.map((f, idx) => ( +
+ + + +
+ ))} +
+ + + + + -
- - -
- -
+ {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} +
+ ); } diff --git a/create-a-container/client/src/pages/containers/ContainersListPage.tsx b/create-a-container/client/src/pages/containers/ContainersListPage.tsx index ec53a62c..ae6c8501 100644 --- a/create-a-container/client/src/pages/containers/ContainersListPage.tsx +++ b/create-a-container/client/src/pages/containers/ContainersListPage.tsx @@ -70,7 +70,7 @@ export function ContainersListPage() { subtitle={site ? `Site: ${site.name}` : undefined} icon={} actions={ -
+
@@ -153,7 +153,7 @@ export function ContainersListPage() { {c.sshHost && c.sshPort ? `${c.sshHost}:${c.sshPort}` : '—'} - + {c.creationJobId && ( + + + } + > ({ value: String(s.id), label: s.name })) || []), ]} /> - - - + + + - - + {mutation.error && ( {(mutation.error as ApiError).message} )} -
- - -
- -
+ + ); } diff --git a/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx b/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx index 6cc130d9..36b58321 100644 --- a/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx +++ b/create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx @@ -84,7 +84,7 @@ export function ExternalDomainsListPage() { )} {d.authServer || '—'} - + + + + } + > - - {mutation.error && {(mutation.error as ApiError).message}} -
- - -
- -
+ + {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} + + ); } diff --git a/create-a-container/client/src/pages/groups/GroupsListPage.tsx b/create-a-container/client/src/pages/groups/GroupsListPage.tsx index 449a7cb6..18e82a53 100644 --- a/create-a-container/client/src/pages/groups/GroupsListPage.tsx +++ b/create-a-container/client/src/pages/groups/GroupsListPage.tsx @@ -66,7 +66,7 @@ export function GroupsListPage() { {g.cn} {g.isAdmin ? Admin : No} {g.userCount ?? 0} - + diff --git a/create-a-container/client/src/pages/nodes/NodeFormPage.tsx b/create-a-container/client/src/pages/nodes/NodeFormPage.tsx index 55976af7..f3a36799 100644 --- a/create-a-container/client/src/pages/nodes/NodeFormPage.tsx +++ b/create-a-container/client/src/pages/nodes/NodeFormPage.tsx @@ -9,13 +9,14 @@ import { AlertDescription, Button, Input, - PageHeader, Spinner, Switch, useToast, } from '@mieweb/ui'; +import { Server } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; import { keys, queries } from '@/lib/queries'; +import { FormPageLayout } from '@/components/FormPageLayout'; import type { Node } from '@/lib/types'; const schema = z.object({ @@ -91,16 +92,72 @@ export function NodeFormPage() { onError: (err: ApiError) => toast.error(err.message), }); - if (isEdit && isLoading) return
; + if (isEdit && isLoading) { + return ( +
+ +
+ ); + } return ( -
- -
mutation.mutate(v))} noValidate className="grid max-w-2xl gap-4"> - - - - + mutation.mutate(v))} noValidate> + } + title={isEdit ? 'Edit node' : 'New node'} + subtitle={ + isEdit + ? 'Update Proxmox connection details and storage settings.' + : 'Register a Proxmox node with API credentials and default storage.' + } + backTo={{ label: 'Back to nodes', to: `/sites/${siteId}/nodes` }} + maxWidth="3xl" + actions={ + <> + + + + } + > + + + + - setValue('tlsVerify', c)} /> -
+ setValue('tlsVerify', c)} + /> +
- setValue('nvidiaAvailable', c)} /> - - {mutation.error && {(mutation.error as ApiError).message}} -
- - -
- -
+ setValue('nvidiaAvailable', c)} + /> + {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} +
+ ); } diff --git a/create-a-container/client/src/pages/nodes/NodeImportPage.tsx b/create-a-container/client/src/pages/nodes/NodeImportPage.tsx index bfd4ef03..a0a098c7 100644 --- a/create-a-container/client/src/pages/nodes/NodeImportPage.tsx +++ b/create-a-container/client/src/pages/nodes/NodeImportPage.tsx @@ -3,20 +3,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { - Alert, - AlertDescription, - AlertTitle, - Button, - Input, - PageHeader, - Switch, - useToast, -} from '@mieweb/ui'; +import { Alert, AlertDescription, Button, Input, Switch, useToast } from '@mieweb/ui'; import { Download } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; import { keys } from '@/lib/queries'; -import type { Node } from '@/lib/types'; +import { FormPageLayout } from '@/components/FormPageLayout'; const schema = z.object({ apiUrl: z.string().url('Must be a valid URL'), @@ -26,11 +17,6 @@ const schema = z.object({ }); type FormData = z.infer; -interface ImportResult { - nodes: Node[]; - importedContainerCount: number; -} - export function NodeImportPage() { const { siteId } = useParams<{ siteId: string }>(); const navigate = useNavigate(); @@ -44,37 +30,81 @@ export function NodeImportPage() { const tlsVerify = watch('tlsVerify'); const mutation = useMutation({ - mutationFn: (v: FormData) => api.post(`/api/v1/sites/${siteId}/nodes/import`, v), - onSuccess: (r) => { - toast.success(`Imported ${r.nodes.length} node(s) and ${r.importedContainerCount} container(s)`); + mutationFn: (values: FormData) => + api.post<{ imported: number }>(`/api/v1/sites/${siteId}/nodes/import-proxmox`, values), + onSuccess: (result) => { + toast.success(`Imported ${result.imported} node(s)`); qc.invalidateQueries({ queryKey: keys.nodes(siteId!) }); - qc.invalidateQueries({ queryKey: keys.containers(siteId!) }); navigate(`/sites/${siteId}/nodes`); }, onError: (err: ApiError) => toast.error(err.message), }); return ( -
- } bordered /> - - Bulk import - - Sign in to a Proxmox cluster and import every node along with its existing LXC containers into this site. - - -
mutation.mutate(v))} noValidate className="grid max-w-xl gap-4"> - - - - setValue('tlsVerify', c)} /> - - {mutation.error && {(mutation.error as ApiError).message}} -
- - -
- -
+
mutation.mutate(v))} noValidate> + } + title="Import nodes from Proxmox" + subtitle="Bulk import from a Proxmox cluster" + description="Sign in to a Proxmox cluster and import every node along with its existing LXC containers into this site." + backTo={{ label: 'Back to nodes', to: `/sites/${siteId}/nodes` }} + maxWidth="xl" + actions={ + <> + + + + } + > + + + + setValue('tlsVerify', c)} + /> + {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} + +
); } diff --git a/create-a-container/client/src/pages/nodes/NodesListPage.tsx b/create-a-container/client/src/pages/nodes/NodesListPage.tsx index ae55de90..d2628c06 100644 --- a/create-a-container/client/src/pages/nodes/NodesListPage.tsx +++ b/create-a-container/client/src/pages/nodes/NodesListPage.tsx @@ -47,7 +47,7 @@ export function NodesListPage() { subtitle={site ? `Site: ${site.name}` : undefined} icon={} actions={ -
+
@@ -79,7 +79,7 @@ export function NodesListPage() { {n.apiUrl || '—'} {n.nvidiaAvailable ? Available : No} {n.hasSecret ? Set : Missing} - + diff --git a/create-a-container/client/src/pages/settings/SettingsPage.tsx b/create-a-container/client/src/pages/settings/SettingsPage.tsx index fa2b44b9..a99b301b 100644 --- a/create-a-container/client/src/pages/settings/SettingsPage.tsx +++ b/create-a-container/client/src/pages/settings/SettingsPage.tsx @@ -118,12 +118,12 @@ export function SettingsPage() {
{fields.length === 0 &&

No defaults defined.

} {fields.map((f, idx) => ( -
- - - -
))} @@ -131,7 +131,7 @@ export function SettingsPage() { {mutation.error && {(mutation.error as ApiError).message}} -
+
diff --git a/create-a-container/client/src/pages/sites/SiteFormPage.tsx b/create-a-container/client/src/pages/sites/SiteFormPage.tsx index cbe0c177..e475cb49 100644 --- a/create-a-container/client/src/pages/sites/SiteFormPage.tsx +++ b/create-a-container/client/src/pages/sites/SiteFormPage.tsx @@ -4,17 +4,11 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { - Alert, - AlertDescription, - Button, - Input, - PageHeader, - Spinner, - useToast, -} from '@mieweb/ui'; +import { Alert, AlertDescription, Button, Input, Spinner, useToast } from '@mieweb/ui'; +import { Building2 } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; import { keys, queries } from '@/lib/queries'; +import { FormPageLayout } from '@/components/FormPageLayout'; import type { Site } from '@/lib/types'; const schema = z.object({ @@ -61,8 +55,6 @@ export function SiteFormPage() { onError: (err: ApiError) => toast.error(err.message), }); - const onSubmit = handleSubmit((values) => mutation.mutate(values)); - if (isEdit && isLoading) { return (
@@ -72,12 +64,31 @@ export function SiteFormPage() { } return ( -
- -
+ mutation.mutate(v))} noValidate> + } + title={isEdit ? 'Edit site' : 'New site'} + subtitle={ + isEdit + ? 'Update networking and DNS settings for this site.' + : 'Define a new site with its internal network, DHCP, and DNS.' + } + backTo={{ label: 'Back to sites', to: '/sites' }} + actions={ + <> + + + + } + > -
- - +
+ +
-
- - +
+ +
- - + {mutation.error && ( {(mutation.error as ApiError).message} )} - -
- - -
- -
+ + ); } diff --git a/create-a-container/client/src/pages/sites/SitesListPage.tsx b/create-a-container/client/src/pages/sites/SitesListPage.tsx index 878862d9..20c06e1d 100644 --- a/create-a-container/client/src/pages/sites/SitesListPage.tsx +++ b/create-a-container/client/src/pages/sites/SitesListPage.tsx @@ -102,7 +102,7 @@ export function SitesListPage() { {s.nodeCount ?? 0} - + {session?.isAdmin && ( <> diff --git a/create-a-container/client/src/pages/users/InviteUserPage.tsx b/create-a-container/client/src/pages/users/InviteUserPage.tsx index c65f361c..95c5e9c7 100644 --- a/create-a-container/client/src/pages/users/InviteUserPage.tsx +++ b/create-a-container/client/src/pages/users/InviteUserPage.tsx @@ -3,16 +3,10 @@ import { useMutation } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { - Alert, - AlertDescription, - Button, - Input, - PageHeader, - useToast, -} from '@mieweb/ui'; -import { Mail } from 'lucide-react'; +import { Alert, AlertDescription, Button, Input, useToast } from '@mieweb/ui'; +import { Mail, Info } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; +import { FormPageLayout } from '@/components/FormPageLayout'; const schema = z.object({ email: z.string().email('Must be a valid email') }); type FormData = z.infer; @@ -20,10 +14,13 @@ type FormData = z.infer; export function InviteUserPage() { const navigate = useNavigate(); const toast = useToast(); - const { register, handleSubmit, formState } = useForm({ resolver: zodResolver(schema) }); + const { register, handleSubmit, formState } = useForm({ + resolver: zodResolver(schema), + }); const mutation = useMutation({ - mutationFn: (v: FormData) => api.post<{ email: string; message: string }>('/api/v1/users/invite', v), + mutationFn: (v: FormData) => + api.post<{ email: string; message: string }>('/api/v1/users/invite', v), onSuccess: (r) => { toast.success(`Invitation sent to ${r.email}`); navigate('/users'); @@ -32,23 +29,54 @@ export function InviteUserPage() { }); return ( -
- } bordered /> -
mutation.mutate(v))} noValidate className="grid max-w-md gap-4"> + mutation.mutate(v))} noValidate> + } + title="Invite user" + subtitle="Send an email invitation to join your team." + backTo={{ label: 'Back to users', to: '/users' }} + aside={ +
+
+ } + actions={ + <> + + + + } + > - {mutation.error && {(mutation.error as ApiError).message}} -
- - -
- -
+ {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} + + ); } diff --git a/create-a-container/client/src/pages/users/UserFormPage.tsx b/create-a-container/client/src/pages/users/UserFormPage.tsx index bc8abf36..c7181013 100644 --- a/create-a-container/client/src/pages/users/UserFormPage.tsx +++ b/create-a-container/client/src/pages/users/UserFormPage.tsx @@ -10,13 +10,14 @@ import { Button, Checkbox, Input, - PageHeader, Select, Spinner, useToast, } from '@mieweb/ui'; +import { UserCog } from 'lucide-react'; import { api, ApiError } from '@/lib/api'; import { keys, queries } from '@/lib/queries'; +import { FormPageLayout } from '@/components/FormPageLayout'; import type { User } from '@/lib/types'; const schema = z.object({ @@ -85,7 +86,13 @@ export function UserFormPage() { onError: (err: ApiError) => toast.error(err.message), }); - if (isEdit && isLoading) return
; + if (isEdit && isLoading) { + return ( +
+ +
+ ); + } function toggleGroup(gid: number) { const next = groupIds.includes(gid) ? groupIds.filter((g) => g !== gid) : [...groupIds, gid]; @@ -93,15 +100,67 @@ export function UserFormPage() { } return ( -
- -
mutation.mutate(v))} noValidate className="grid max-w-2xl gap-4"> - -
- - + mutation.mutate(v))} noValidate> + } + title={isEdit ? `Edit user: ${user?.uid ?? ''}` : 'New user'} + subtitle={ + isEdit + ? 'Update profile details, password, status, and group memberships.' + : 'Provision a new account directly without sending an invitation.' + } + backTo={{ label: 'Back to users', to: '/users' }} + actions={ + <> + + + + } + > + +
+ +
- +
- Groups - {groups?.map((g) => ( - toggleGroup(g.gidNumber)} - /> - ))} + Groups + {groups && groups.length > 0 ? ( + groups.map((g) => ( + toggleGroup(g.gidNumber)} + /> + )) + ) : ( +

No groups available.

+ )}
- - {mutation.error && {(mutation.error as ApiError).message}} -
- - -
- -
+ {mutation.error && ( + + {(mutation.error as ApiError).message} + + )} + + ); } diff --git a/create-a-container/client/src/pages/users/UsersListPage.tsx b/create-a-container/client/src/pages/users/UsersListPage.tsx index bd0c5464..24db50a2 100644 --- a/create-a-container/client/src/pages/users/UsersListPage.tsx +++ b/create-a-container/client/src/pages/users/UsersListPage.tsx @@ -39,7 +39,7 @@ export function UsersListPage() { title="Users" icon={} actions={ -
+
@@ -77,7 +77,7 @@ export function UsersListPage() { {u.isAdmin ? Admin : '—'} - + diff --git a/create-a-container/client/vite.config.ts b/create-a-container/client/vite.config.ts index b6610742..033ed665 100644 --- a/create-a-container/client/vite.config.ts +++ b/create-a-container/client/vite.config.ts @@ -16,7 +16,8 @@ export default defineConfig({ port: 5173, strictPort: true, proxy: { - '/api': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, + // Use a regex so /api/* is proxied but client routes like /apikeys are not. + '^/api(/|$)': { target: EXPRESS_TARGET, changeOrigin: false, secure: false }, }, }, build: { From 97190e48a3885e5d560d033430bdd59e9e983ad2 Mon Sep 17 00:00:00 2001 From: William Reiske Date: Tue, 19 May 2026 00:12:33 -0700 Subject: [PATCH 09/11] feat(api,client): port main features missed during EJS cutover - POST /api/v1/auth/dev: one-click dev login (admin/user) when NODE_ENV != production - GET /api/v1/health: now returns isDev flag - GET /api/v1/session: admins also receive pushNotificationUrl - POST /api/v1/users/email-all: broadcast email via existing sendBulkEmail util - LoginPage: dev-mode login buttons gated on /health.isDev - UsersListPage: 'Email all' action + modal (subject + message) - Sidebar: external 'MFA Admin' link rendered for admins when push URL configured --- create-a-container/client/src/app/Sidebar.tsx | 14 +++ create-a-container/client/src/lib/auth.ts | 43 +++++++ .../client/src/pages/auth/LoginPage.tsx | 49 +++++++- .../client/src/pages/users/EmailAllModal.tsx | 113 ++++++++++++++++++ .../client/src/pages/users/UsersListPage.tsx | 18 ++- create-a-container/routers/api/v1/auth.js | 54 +++++++++ create-a-container/routers/api/v1/index.js | 29 +++-- create-a-container/routers/api/v1/users.js | 40 ++++++- 8 files changed, 350 insertions(+), 10 deletions(-) create mode 100644 create-a-container/client/src/pages/users/EmailAllModal.tsx diff --git a/create-a-container/client/src/app/Sidebar.tsx b/create-a-container/client/src/app/Sidebar.tsx index e5ff88ca..3eb09082 100644 --- a/create-a-container/client/src/app/Sidebar.tsx +++ b/create-a-container/client/src/app/Sidebar.tsx @@ -11,10 +11,12 @@ import { import { Box, Building2, + ExternalLink, Globe, KeyRound, LogOut, Settings, + ShieldCheck, Users, UsersRound, } from 'lucide-react'; @@ -59,6 +61,8 @@ export function AppSidebar() { const { data: session } = useSession(); const logout = useLogoutMutation(); const isAdmin = !!session?.isAdmin; + const mfaAdminUrl = + isAdmin && session?.pushNotificationUrl ? `${session.pushNotificationUrl}/admin` : null; const isActive = (link: NavLink) => { const prefix = link.match ?? link.to; @@ -85,6 +89,16 @@ export function AppSidebar() { {PRIMARY.map(renderLink)} + {mfaAdminUrl && ( + } + badge={