@aurostack/stacks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/cli/src/extract.mjs +165 -0
- package/cli/src/fsx.mjs +197 -0
- package/cli/src/generate.mjs +235 -0
- package/cli/src/hooks.mjs +55 -0
- package/cli/src/manifest.mjs +163 -0
- package/cli/src/strip.mjs +208 -0
- package/cli/src/tokens.mjs +102 -0
- package/cli/stack.mjs +672 -0
- package/package.json +37 -0
- package/templates/nest-api/files/.bin/db.ts +123 -0
- package/templates/nest-api/files/.bin/dc.ts +74 -0
- package/templates/nest-api/files/.bin/helpers.ts +49 -0
- package/templates/nest-api/files/.bin/secrets.ts +68 -0
- package/templates/nest-api/files/.bin/start.ts +47 -0
- package/templates/nest-api/files/.dockerignore +7 -0
- package/templates/nest-api/files/.env.example +91 -0
- package/templates/nest-api/files/.env.test.example +75 -0
- package/templates/nest-api/files/.forgejo/workflows/build-and-push.yml +41 -0
- package/templates/nest-api/files/.forgejo/workflows/test-e2e.yml +83 -0
- package/templates/nest-api/files/.forgejo/workflows/test.yml +53 -0
- package/templates/nest-api/files/.npmrc.example +1 -0
- package/templates/nest-api/files/.nvmrc +1 -0
- package/templates/nest-api/files/.prettierrc +8 -0
- package/templates/nest-api/files/.swcrc +15 -0
- package/templates/nest-api/files/.watchmanconfig +3 -0
- package/templates/nest-api/files/.yarnrc.yml +1 -0
- package/templates/nest-api/files/Dockerfile +40 -0
- package/templates/nest-api/files/__mocks__/activation-twofa.js +7 -0
- package/templates/nest-api/files/__mocks__/activation.js +7 -0
- package/templates/nest-api/files/__mocks__/forgot-password.js +7 -0
- package/templates/nest-api/files/__mocks__/main-layout.js +7 -0
- package/templates/nest-api/files/__mocks__/password-changed.js +7 -0
- package/templates/nest-api/files/__mocks__/reset-password.js +7 -0
- package/templates/nest-api/files/__mocks__/set-password.js +7 -0
- package/templates/nest-api/files/__mocks__/welcome.js +7 -0
- package/templates/nest-api/files/_gitignore +62 -0
- package/templates/nest-api/files/compose.test.yml +33 -0
- package/templates/nest-api/files/compose.yml +39 -0
- package/templates/nest-api/files/emails/activation-twofa.tsx +58 -0
- package/templates/nest-api/files/emails/activation.tsx +36 -0
- package/templates/nest-api/files/emails/forgot-password.tsx +29 -0
- package/templates/nest-api/files/emails/main-layout.tsx +134 -0
- package/templates/nest-api/files/emails/password-changed.tsx +24 -0
- package/templates/nest-api/files/emails/reset-password.tsx +24 -0
- package/templates/nest-api/files/emails/set-password.tsx +25 -0
- package/templates/nest-api/files/emails/welcome.tsx +39 -0
- package/templates/nest-api/files/eslint.config.mjs +28 -0
- package/templates/nest-api/files/global.d.ts +5 -0
- package/templates/nest-api/files/nest-cli.json +8 -0
- package/templates/nest-api/files/package.json +150 -0
- package/templates/nest-api/files/prisma/schema/auth.prisma +75 -0
- package/templates/nest-api/files/prisma/schema/base.prisma +17 -0
- package/templates/nest-api/files/prisma/schema/enum.prisma +17 -0
- package/templates/nest-api/files/prisma/schema/notification.prisma +14 -0
- package/templates/nest-api/files/prisma.config.ts +13 -0
- package/templates/nest-api/files/public/.gitkeep +0 -0
- package/templates/nest-api/files/seeders/index.ts +27 -0
- package/templates/nest-api/files/seeders/util.ts +88 -0
- package/templates/nest-api/files/src/__tests__/app.controller.spec.ts +29 -0
- package/templates/nest-api/files/src/app.controller.ts +40 -0
- package/templates/nest-api/files/src/app.module.ts +268 -0
- package/templates/nest-api/files/src/app.resolver.ts +32 -0
- package/templates/nest-api/files/src/app.setup.ts +178 -0
- package/templates/nest-api/files/src/common/__tests__/auth.service.spec.ts +31 -0
- package/templates/nest-api/files/src/common/__tests__/cache.service.spec.ts +214 -0
- package/templates/nest-api/files/src/common/__tests__/config.service.spec.ts +56 -0
- package/templates/nest-api/files/src/common/__tests__/generator.service.spec.ts +114 -0
- package/templates/nest-api/files/src/common/__tests__/health.controller.spec.ts +72 -0
- package/templates/nest-api/files/src/common/__tests__/helpers.spec.ts +124 -0
- package/templates/nest-api/files/src/common/__tests__/mail.service.spec.ts +75 -0
- package/templates/nest-api/files/src/common/__tests__/prisma.indicator.spec.ts +39 -0
- package/templates/nest-api/files/src/common/__tests__/prisma.service.spec.ts +15 -0
- package/templates/nest-api/files/src/common/__tests__/records.spec.ts +87 -0
- package/templates/nest-api/files/src/common/__tests__/schemas.spec.ts +142 -0
- package/templates/nest-api/files/src/common/__tests__/throttler-storage.service.spec.ts +80 -0
- package/templates/nest-api/files/src/common/common.module.ts +72 -0
- package/templates/nest-api/files/src/common/constants/index.ts +2 -0
- package/templates/nest-api/files/src/common/constants/options.ts +52 -0
- package/templates/nest-api/files/src/common/constants/pino.ts +24 -0
- package/templates/nest-api/files/src/common/controllers/health.controller.ts +54 -0
- package/templates/nest-api/files/src/common/controllers/index.ts +2 -0
- package/templates/nest-api/files/src/common/controllers/metrics.controller.ts +21 -0
- package/templates/nest-api/files/src/common/decorators/index.ts +2 -0
- package/templates/nest-api/files/src/common/decorators/misc.decorator.ts +49 -0
- package/templates/nest-api/files/src/common/decorators/multi-part.decorator.ts +6 -0
- package/templates/nest-api/files/src/common/dto/filters.dto.ts +25 -0
- package/templates/nest-api/files/src/common/dto/index.ts +1 -0
- package/templates/nest-api/files/src/common/entity/index.ts +2 -0
- package/templates/nest-api/files/src/common/entity/ok.entity.ts +11 -0
- package/templates/nest-api/files/src/common/entity/pagination.entity.ts +24 -0
- package/templates/nest-api/files/src/common/exceptions/index.ts +1 -0
- package/templates/nest-api/files/src/common/exceptions/prisma.exception.ts +57 -0
- package/templates/nest-api/files/src/common/guards/feature-flag.guard.ts +51 -0
- package/templates/nest-api/files/src/common/guards/index.ts +1 -0
- package/templates/nest-api/files/src/common/interceptors/index.ts +1 -0
- package/templates/nest-api/files/src/common/interceptors/metrics.interceptor.ts +37 -0
- package/templates/nest-api/files/src/common/misc/helpers.ts +26 -0
- package/templates/nest-api/files/src/common/misc/index.ts +3 -0
- package/templates/nest-api/files/src/common/misc/origin-builder.ts +56 -0
- package/templates/nest-api/files/src/common/misc/prisma.indicator.ts +24 -0
- package/templates/nest-api/files/src/common/modules/index.ts +3 -0
- package/templates/nest-api/files/src/common/modules/logger.module.ts +104 -0
- package/templates/nest-api/files/src/common/modules/metrics.module.ts +28 -0
- package/templates/nest-api/files/src/common/modules/queue.module.ts +23 -0
- package/templates/nest-api/files/src/common/processors/index.ts +1 -0
- package/templates/nest-api/files/src/common/processors/mail/index.ts +47 -0
- package/templates/nest-api/files/src/common/processors/mail/process.ts +225 -0
- package/templates/nest-api/files/src/common/schemas/fields.ts +76 -0
- package/templates/nest-api/files/src/common/schemas/index.ts +1 -0
- package/templates/nest-api/files/src/common/services/auth-rate-limit.storage.ts +79 -0
- package/templates/nest-api/files/src/common/services/auth.service.ts +16 -0
- package/templates/nest-api/files/src/common/services/cache.service.ts +118 -0
- package/templates/nest-api/files/src/common/services/config.service.ts +388 -0
- package/templates/nest-api/files/src/common/services/feature-flag.service.ts +117 -0
- package/templates/nest-api/files/src/common/services/generator.service.ts +54 -0
- package/templates/nest-api/files/src/common/services/index.ts +10 -0
- package/templates/nest-api/files/src/common/services/logger.service.ts +14 -0
- package/templates/nest-api/files/src/common/services/mail.service.ts +44 -0
- package/templates/nest-api/files/src/common/services/prisma.service.ts +106 -0
- package/templates/nest-api/files/src/common/services/throttler-storage.service.ts +112 -0
- package/templates/nest-api/files/src/common/types/email.ts +55 -0
- package/templates/nest-api/files/src/common/types/index.ts +3 -0
- package/templates/nest-api/files/src/common/types/misc.ts +20 -0
- package/templates/nest-api/files/src/common/types/prisma.ts +18 -0
- package/templates/nest-api/files/src/common/utils/enum.ts +39 -0
- package/templates/nest-api/files/src/common/utils/helpers.ts +139 -0
- package/templates/nest-api/files/src/common/utils/index.ts +5 -0
- package/templates/nest-api/files/src/common/utils/ip.ts +22 -0
- package/templates/nest-api/files/src/common/utils/records.ts +66 -0
- package/templates/nest-api/files/src/common/utils/redis.keys.ts +31 -0
- package/templates/nest-api/files/src/lib/__tests__/access.spec.ts +33 -0
- package/templates/nest-api/files/src/lib/access.ts +114 -0
- package/templates/nest-api/files/src/lib/auth.ts +76 -0
- package/templates/nest-api/files/src/lib/rate-limit.ts +20 -0
- package/templates/nest-api/files/src/main.ts +28 -0
- package/templates/nest-api/files/src/media/__tests__/media.service.spec.ts +60 -0
- package/templates/nest-api/files/src/media/__tests__/s3.service.spec.ts +159 -0
- package/templates/nest-api/files/src/media/entity/file.entity.ts +7 -0
- package/templates/nest-api/files/src/media/entity/index.ts +1 -0
- package/templates/nest-api/files/src/media/media.module.ts +20 -0
- package/templates/nest-api/files/src/media/processors/index.ts +44 -0
- package/templates/nest-api/files/src/media/services/index.ts +2 -0
- package/templates/nest-api/files/src/media/services/media.service.ts +29 -0
- package/templates/nest-api/files/src/media/services/s3.service.ts +168 -0
- package/templates/nest-api/files/src/notifications/controllers/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/controllers/notifications.controller.ts +72 -0
- package/templates/nest-api/files/src/notifications/dto/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/dto/notification.dto.ts +11 -0
- package/templates/nest-api/files/src/notifications/entity/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/entity/notification.entity.ts +37 -0
- package/templates/nest-api/files/src/notifications/notifications.module.ts +11 -0
- package/templates/nest-api/files/src/notifications/services/index.ts +1 -0
- package/templates/nest-api/files/src/notifications/services/notifications.service.ts +98 -0
- package/templates/nest-api/files/src/realtime/__tests__/events.gateway.spec.ts +88 -0
- package/templates/nest-api/files/src/realtime/__tests__/rooms.spec.ts +29 -0
- package/templates/nest-api/files/src/realtime/__tests__/ws-permissions.guard.spec.ts +54 -0
- package/templates/nest-api/files/src/realtime/adapters/redis-io.adapter.ts +57 -0
- package/templates/nest-api/files/src/realtime/constants/index.ts +68 -0
- package/templates/nest-api/files/src/realtime/decorators/index.ts +1 -0
- package/templates/nest-api/files/src/realtime/decorators/ws-permissions.decorator.ts +14 -0
- package/templates/nest-api/files/src/realtime/gateways/events.gateway.ts +187 -0
- package/templates/nest-api/files/src/realtime/guards/index.ts +2 -0
- package/templates/nest-api/files/src/realtime/guards/ws-auth.guard.ts +20 -0
- package/templates/nest-api/files/src/realtime/guards/ws-permissions.guard.ts +35 -0
- package/templates/nest-api/files/src/realtime/misc/session-from-socket.ts +54 -0
- package/templates/nest-api/files/src/realtime/realtime.module.ts +26 -0
- package/templates/nest-api/files/src/realtime/services/presence.service.ts +56 -0
- package/templates/nest-api/files/src/realtime/services/realtime.service.ts +72 -0
- package/templates/nest-api/files/src/users/__tests__/current-user.controller.spec.ts +77 -0
- package/templates/nest-api/files/src/users/__tests__/current-user.service.spec.ts +159 -0
- package/templates/nest-api/files/src/users/__tests__/user.entity.spec.ts +175 -0
- package/templates/nest-api/files/src/users/__tests__/users.controller.spec.ts +89 -0
- package/templates/nest-api/files/src/users/__tests__/users.service.spec.ts +398 -0
- package/templates/nest-api/files/src/users/controllers/current-user.controller.ts +82 -0
- package/templates/nest-api/files/src/users/controllers/index.ts +2 -0
- package/templates/nest-api/files/src/users/controllers/users.controller.ts +40 -0
- package/templates/nest-api/files/src/users/decorators/index.ts +1 -0
- package/templates/nest-api/files/src/users/decorators/permissions.decorator.ts +24 -0
- package/templates/nest-api/files/src/users/dto/avatar.dto.ts +13 -0
- package/templates/nest-api/files/src/users/dto/filters.dto.ts +8 -0
- package/templates/nest-api/files/src/users/dto/index.ts +2 -0
- package/templates/nest-api/files/src/users/entity/index.ts +1 -0
- package/templates/nest-api/files/src/users/entity/user.entity.ts +53 -0
- package/templates/nest-api/files/src/users/services/current-user.service.ts +46 -0
- package/templates/nest-api/files/src/users/services/index.ts +2 -0
- package/templates/nest-api/files/src/users/services/users.service.ts +88 -0
- package/templates/nest-api/files/src/users/users.module.ts +10 -0
- package/templates/nest-api/files/test/app.spec.ts +102 -0
- package/templates/nest-api/files/test/auth.spec.ts +108 -0
- package/templates/nest-api/files/test/current-user.spec.ts +154 -0
- package/templates/nest-api/files/test/factory/app.ts +172 -0
- package/templates/nest-api/files/test/factory/constants.ts +22 -0
- package/templates/nest-api/files/test/factory/dto.ts +27 -0
- package/templates/nest-api/files/test/factory/entity.ts +70 -0
- package/templates/nest-api/files/test/factory/gen.ts +75 -0
- package/templates/nest-api/files/test/factory/util.ts +21 -0
- package/templates/nest-api/files/test/notifications.spec.ts +176 -0
- package/templates/nest-api/files/test/rate-limit.spec.ts +180 -0
- package/templates/nest-api/files/test/setup.ts +1 -0
- package/templates/nest-api/files/test/users.spec.ts +437 -0
- package/templates/nest-api/files/tsconfig.build.json +13 -0
- package/templates/nest-api/files/tsconfig.json +35 -0
- package/templates/nest-api/files/vitest.config.ts +53 -0
- package/templates/nest-api/template.json +452 -0
- package/templates/node-worker/files/.bin/db.ts +123 -0
- package/templates/node-worker/files/.bin/helpers.ts +49 -0
- package/templates/node-worker/files/.bin/secrets.ts +68 -0
- package/templates/node-worker/files/.dockerignore +5 -0
- package/templates/node-worker/files/.env.example +21 -0
- package/templates/node-worker/files/.forgejo/workflows/build-and-push.yml +41 -0
- package/templates/node-worker/files/.nvmrc +1 -0
- package/templates/node-worker/files/.prettierignore +4 -0
- package/templates/node-worker/files/.prettierrc +8 -0
- package/templates/node-worker/files/.yarnrc.yml +5 -0
- package/templates/node-worker/files/Dockerfile +63 -0
- package/templates/node-worker/files/Dockerfile.dev +59 -0
- package/templates/node-worker/files/_gitignore +48 -0
- package/templates/node-worker/files/ecosystem.config.cjs +10 -0
- package/templates/node-worker/files/eslint.config.js +37 -0
- package/templates/node-worker/files/package.json +67 -0
- package/templates/node-worker/files/prisma/_gitignore +1 -0
- package/templates/node-worker/files/prisma/schema/base.prisma +36 -0
- package/templates/node-worker/files/prisma.config.ts +13 -0
- package/templates/node-worker/files/src/config.ts +55 -0
- package/templates/node-worker/files/src/db.ts +118 -0
- package/templates/node-worker/files/src/index.ts +7 -0
- package/templates/node-worker/files/src/logger.ts +45 -0
- package/templates/node-worker/files/src/types.ts +23 -0
- package/templates/node-worker/files/src/utils/browser.ts +16 -0
- package/templates/node-worker/files/src/utils/date.ts +68 -0
- package/templates/node-worker/files/src/utils/helpers.ts +17 -0
- package/templates/node-worker/files/src/utils/number.ts +47 -0
- package/templates/node-worker/files/src/utils/worker.ts +29 -0
- package/templates/node-worker/files/src/workers/constants.ts +6 -0
- package/templates/node-worker/files/src/workers/example/index.ts +27 -0
- package/templates/node-worker/files/src/workers/index.ts +1 -0
- package/templates/node-worker/files/tsconfig.json +30 -0
- package/templates/node-worker/template.json +135 -0
- package/templates/py-worker/files/.dockerignore +176 -0
- package/templates/py-worker/files/.env.example +20 -0
- package/templates/py-worker/files/.forgejo/workflows/build-amd-push.yml +39 -0
- package/templates/py-worker/files/Dockerfile +10 -0
- package/templates/py-worker/files/README.md +3 -0
- package/templates/py-worker/files/_gitignore +176 -0
- package/templates/py-worker/files/config.py +89 -0
- package/templates/py-worker/files/models/decorators.py +42 -0
- package/templates/py-worker/files/models/enums.py +32 -0
- package/templates/py-worker/files/models/mixins.py +61 -0
- package/templates/py-worker/files/requirements.txt +18 -0
- package/templates/py-worker/files/utils/db.py +67 -0
- package/templates/py-worker/files/utils/logger.py +97 -0
- package/templates/py-worker/files/utils/misc.py +45 -0
- package/templates/py-worker/files/utils/worker.py +80 -0
- package/templates/py-worker/files/worker.py +41 -0
- package/templates/py-worker/files/workers/hello/process.py +22 -0
- package/templates/py-worker/template.json +87 -0
- package/templates/react-app/README.md +78 -0
- package/templates/react-app/derive.sh +128 -0
- package/templates/react-app/files/.bin/openapi.js +32 -0
- package/templates/react-app/files/.bin/secrets.js +47 -0
- package/templates/react-app/files/.dockerignore +16 -0
- package/templates/react-app/files/.editorconfig +12 -0
- package/templates/react-app/files/.env.example +34 -0
- package/templates/react-app/files/.forgejo/workflows/build-and-push.yml +55 -0
- package/templates/react-app/files/.forgejo/workflows/ci.yml +39 -0
- package/templates/react-app/files/.husky/pre-commit +1 -0
- package/templates/react-app/files/.nvmrc +1 -0
- package/templates/react-app/files/.prettierignore +6 -0
- package/templates/react-app/files/.prettierrc +9 -0
- package/templates/react-app/files/.yarnrc.yml +2 -0
- package/templates/react-app/files/Dockerfile +52 -0
- package/templates/react-app/files/_gitignore +33 -0
- package/templates/react-app/files/build.sh +53 -0
- package/templates/react-app/files/docker/nginx.conf +134 -0
- package/templates/react-app/files/eslint.config.mjs +75 -0
- package/templates/react-app/files/index.html +62 -0
- package/templates/react-app/files/openapi-ts.config.ts +22 -0
- package/templates/react-app/files/package.json +80 -0
- package/templates/react-app/files/src/app/nav.tsx +32 -0
- package/templates/react-app/files/src/app/providers.tsx +39 -0
- package/templates/react-app/files/src/app/router.tsx +142 -0
- package/templates/react-app/files/src/app/store.ts +20 -0
- package/templates/react-app/files/src/features/auth/components/auth-card.tsx +23 -0
- package/templates/react-app/files/src/features/auth/components/checkbox-field.tsx +32 -0
- package/templates/react-app/files/src/features/auth/components/google-button.tsx +41 -0
- package/templates/react-app/files/src/features/auth/components/or-divider.tsx +9 -0
- package/templates/react-app/files/src/features/auth/components/password-field.tsx +70 -0
- package/templates/react-app/files/src/features/auth/components/submit-button.tsx +16 -0
- package/templates/react-app/files/src/features/auth/lib/auth-actions.ts +68 -0
- package/templates/react-app/files/src/features/auth/lib/password-strength.ts +20 -0
- package/templates/react-app/files/src/features/auth/lib/redirect.ts +6 -0
- package/templates/react-app/files/src/features/auth/lib/schemas.ts +37 -0
- package/templates/react-app/files/src/features/seo/routes.ts +65 -0
- package/templates/react-app/files/src/lib/env.ts +28 -0
- package/templates/react-app/files/src/lib/paths.ts +58 -0
- package/templates/react-app/files/src/main.tsx +19 -0
- package/templates/react-app/files/src/routes/admin/home.tsx +24 -0
- package/templates/react-app/files/src/routes/auth/forgot-password.tsx +93 -0
- package/templates/react-app/files/src/routes/auth/login.tsx +87 -0
- package/templates/react-app/files/src/routes/auth/reset-password.tsx +83 -0
- package/templates/react-app/files/src/routes/auth/signup.tsx +117 -0
- package/templates/react-app/files/src/routes/auth/verified.tsx +31 -0
- package/templates/react-app/files/src/routes/auth/verify-email.tsx +97 -0
- package/templates/react-app/files/src/routes/home.tsx +17 -0
- package/templates/react-app/files/src/routes/legal/privacy.tsx +26 -0
- package/templates/react-app/files/src/routes/legal/terms.tsx +28 -0
- package/templates/react-app/files/src/routes/marketing/home.tsx +18 -0
- package/templates/react-app/files/src/routes/settings.tsx +10 -0
- package/templates/react-app/files/src/shared/api/base-api.ts +24 -0
- package/templates/react-app/files/src/shared/api/base-query.ts +29 -0
- package/templates/react-app/files/src/shared/api/config.ts +21 -0
- package/templates/react-app/files/src/shared/api/index.ts +15 -0
- package/templates/react-app/files/src/shared/api/realtime-events.ts +34 -0
- package/templates/react-app/files/src/shared/api/socket.ts +41 -0
- package/templates/react-app/files/src/shared/api/store.ts +39 -0
- package/templates/react-app/files/src/shared/api/upload-query.ts +115 -0
- package/templates/react-app/files/src/shared/auth/client.ts +27 -0
- package/templates/react-app/files/src/shared/auth/config.ts +28 -0
- package/templates/react-app/files/src/shared/auth/guards.tsx +75 -0
- package/templates/react-app/files/src/shared/auth/hooks.ts +13 -0
- package/templates/react-app/files/src/shared/auth/index.ts +20 -0
- package/templates/react-app/files/src/shared/auth/logout.ts +23 -0
- package/templates/react-app/files/src/shared/auth/provider.tsx +52 -0
- package/templates/react-app/files/src/shared/auth/redirect.ts +65 -0
- package/templates/react-app/files/src/shared/auth/slice.ts +54 -0
- package/templates/react-app/files/src/shared/hooks/index.ts +6 -0
- package/templates/react-app/files/src/shared/layouts/app-shell/app-shell.tsx +225 -0
- package/templates/react-app/files/src/shared/layouts/app-shell/user-menu.tsx +74 -0
- package/templates/react-app/files/src/shared/layouts/auth-layout.tsx +137 -0
- package/templates/react-app/files/src/shared/layouts/error-boundary.tsx +38 -0
- package/templates/react-app/files/src/shared/layouts/index.ts +23 -0
- package/templates/react-app/files/src/shared/layouts/marketing/marketing-layout.tsx +29 -0
- package/templates/react-app/files/src/shared/layouts/marketing/section-heading.tsx +38 -0
- package/templates/react-app/files/src/shared/layouts/marketing/section.tsx +30 -0
- package/templates/react-app/files/src/shared/layouts/states/app-shell-skeleton.tsx +47 -0
- package/templates/react-app/files/src/shared/layouts/states/empty.tsx +22 -0
- package/templates/react-app/files/src/shared/layouts/states/forbidden.tsx +14 -0
- package/templates/react-app/files/src/shared/layouts/states/loading.tsx +11 -0
- package/templates/react-app/files/src/shared/layouts/states/not-found.tsx +21 -0
- package/templates/react-app/files/src/shared/layouts/states/page-skeleton.tsx +39 -0
- package/templates/react-app/files/src/shared/layouts/states/route-fallback.tsx +26 -0
- package/templates/react-app/files/src/shared/layouts/states/state-screen.tsx +22 -0
- package/templates/react-app/files/src/shared/types/README.md +28 -0
- package/templates/react-app/files/src/shared/types/api/index.ts +9 -0
- package/templates/react-app/files/src/shared/types/api/zod.gen.ts +5 -0
- package/templates/react-app/files/src/shared/types/index.ts +4 -0
- package/templates/react-app/files/src/shared/ui/components/brand/logo.tsx +51 -0
- package/templates/react-app/files/src/shared/ui/components/brand/social-icons.tsx +37 -0
- package/templates/react-app/files/src/shared/ui/components/form/form.tsx +120 -0
- package/templates/react-app/files/src/shared/ui/components/ui/alert.tsx +25 -0
- package/templates/react-app/files/src/shared/ui/components/ui/button.tsx +43 -0
- package/templates/react-app/files/src/shared/ui/components/ui/calendar.tsx +78 -0
- package/templates/react-app/files/src/shared/ui/components/ui/card.tsx +44 -0
- package/templates/react-app/files/src/shared/ui/components/ui/checkbox.tsx +16 -0
- package/templates/react-app/files/src/shared/ui/components/ui/date-picker.tsx +76 -0
- package/templates/react-app/files/src/shared/ui/components/ui/input.tsx +22 -0
- package/templates/react-app/files/src/shared/ui/components/ui/label.tsx +19 -0
- package/templates/react-app/files/src/shared/ui/components/ui/modal.tsx +94 -0
- package/templates/react-app/files/src/shared/ui/components/ui/popover.tsx +31 -0
- package/templates/react-app/files/src/shared/ui/components/ui/progress.tsx +33 -0
- package/templates/react-app/files/src/shared/ui/components/ui/search-input.tsx +69 -0
- package/templates/react-app/files/src/shared/ui/components/ui/select.tsx +80 -0
- package/templates/react-app/files/src/shared/ui/components/ui/skeleton.tsx +12 -0
- package/templates/react-app/files/src/shared/ui/components/ui/sonner.tsx +23 -0
- package/templates/react-app/files/src/shared/ui/components/ui/spinner.tsx +8 -0
- package/templates/react-app/files/src/shared/ui/components/ui/textarea.tsx +22 -0
- package/templates/react-app/files/src/shared/ui/index.ts +45 -0
- package/templates/react-app/files/src/shared/ui/lib/utils.ts +7 -0
- package/templates/react-app/files/src/shared/ui/lib/zod-setup.ts +28 -0
- package/templates/react-app/files/src/shared/ui/meta/brand.ts +18 -0
- package/templates/react-app/files/src/shared/ui/meta/meta-context.ts +30 -0
- package/templates/react-app/files/src/shared/ui/meta/meta-provider.tsx +24 -0
- package/templates/react-app/files/src/shared/ui/meta/page-meta.tsx +69 -0
- package/templates/react-app/files/src/shared/ui/meta/use-meta-config.ts +11 -0
- package/templates/react-app/files/src/shared/ui/styles/globals.css +285 -0
- package/templates/react-app/files/src/shared/ui/styles/theme.css +48 -0
- package/templates/react-app/files/src/shared/ui/theme/init-script.ts +23 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-colors.ts +14 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-context.ts +60 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-provider.tsx +101 -0
- package/templates/react-app/files/src/shared/ui/theme/theme-toggle.tsx +40 -0
- package/templates/react-app/files/src/shared/ui/theme/use-theme.ts +11 -0
- package/templates/react-app/files/src/vite-env.d.ts +13 -0
- package/templates/react-app/files/tsconfig.json +26 -0
- package/templates/react-app/files/tsconfig.node.json +25 -0
- package/templates/react-app/files/vite.config.ts +344 -0
- package/templates/react-app/overrides/src/routes/auth/forgot-password.tsx +93 -0
- package/templates/react-app/overrides/src/routes/auth/login.tsx +87 -0
- package/templates/react-app/overrides/src/routes/auth/signup.tsx +117 -0
- package/templates/react-app/overrides/src/routes/auth/verified.tsx +31 -0
- package/templates/react-app/overrides/src/routes/auth/verify-email.tsx +97 -0
- package/templates/react-app/overrides/src/shared/auth/config.ts +28 -0
- package/templates/react-app/overrides/src/shared/auth/guards.tsx +75 -0
- package/templates/react-app/overrides/src/shared/auth/index.ts +20 -0
- package/templates/react-app/overrides/src/shared/auth/redirect.ts +65 -0
- package/templates/react-app/overrides/src/shared/hooks/index.ts +6 -0
- package/templates/react-app/overrides/src/shared/types/README.md +28 -0
- package/templates/react-app/template.json +222 -0
- package/templates/react-monorepo/files/.dockerignore +19 -0
- package/templates/react-monorepo/files/.editorconfig +12 -0
- package/templates/react-monorepo/files/.forgejo/workflows/build-and-push.yml +98 -0
- package/templates/react-monorepo/files/.forgejo/workflows/ci.yml +35 -0
- package/templates/react-monorepo/files/.husky/pre-commit +1 -0
- package/templates/react-monorepo/files/.nvmrc +1 -0
- package/templates/react-monorepo/files/.prettierignore +6 -0
- package/templates/react-monorepo/files/.prettierrc +9 -0
- package/templates/react-monorepo/files/.yarnrc.yml +2 -0
- package/templates/react-monorepo/files/Dockerfile +90 -0
- package/templates/react-monorepo/files/_gitignore +35 -0
- package/templates/react-monorepo/files/apps/admin/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/admin/.env.example +16 -0
- package/templates/react-monorepo/files/apps/admin/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/admin/index.html +57 -0
- package/templates/react-monorepo/files/apps/admin/package.json +33 -0
- package/templates/react-monorepo/files/apps/admin/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/admin/src/app/nav.tsx +14 -0
- package/templates/react-monorepo/files/apps/admin/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/admin/src/app/router.tsx +50 -0
- package/templates/react-monorepo/files/apps/admin/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/admin/src/lib/env.ts +12 -0
- package/templates/react-monorepo/files/apps/admin/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/admin/src/routes/home.tsx +17 -0
- package/templates/react-monorepo/files/apps/admin/src/routes/settings.tsx +10 -0
- package/templates/react-monorepo/files/apps/admin/src/vite-env.d.ts +14 -0
- package/templates/react-monorepo/files/apps/admin/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/admin/vite.config.ts +13 -0
- package/templates/react-monorepo/files/apps/auth/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/auth/.env.example +16 -0
- package/templates/react-monorepo/files/apps/auth/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/auth/index.html +57 -0
- package/templates/react-monorepo/files/apps/auth/package.json +32 -0
- package/templates/react-monorepo/files/apps/auth/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/auth/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/auth/src/app/router.tsx +19 -0
- package/templates/react-monorepo/files/apps/auth/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/auth/src/components/auth-card.tsx +23 -0
- package/templates/react-monorepo/files/apps/auth/src/components/checkbox-field.tsx +32 -0
- package/templates/react-monorepo/files/apps/auth/src/components/google-button.tsx +41 -0
- package/templates/react-monorepo/files/apps/auth/src/components/or-divider.tsx +9 -0
- package/templates/react-monorepo/files/apps/auth/src/components/password-field.tsx +70 -0
- package/templates/react-monorepo/files/apps/auth/src/components/submit-button.tsx +16 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/auth-actions.ts +68 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/env.ts +8 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/password-strength.ts +20 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/redirect.ts +6 -0
- package/templates/react-monorepo/files/apps/auth/src/lib/schemas.ts +37 -0
- package/templates/react-monorepo/files/apps/auth/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/forgot-password.tsx +91 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/login.tsx +82 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/reset-password.tsx +83 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/signup.tsx +117 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/verified.tsx +30 -0
- package/templates/react-monorepo/files/apps/auth/src/routes/verify-email.tsx +96 -0
- package/templates/react-monorepo/files/apps/auth/src/vite-env.d.ts +14 -0
- package/templates/react-monorepo/files/apps/auth/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/auth/vite.config.ts +13 -0
- package/templates/react-monorepo/files/apps/client/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/client/.env.example +16 -0
- package/templates/react-monorepo/files/apps/client/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/client/index.html +57 -0
- package/templates/react-monorepo/files/apps/client/package.json +35 -0
- package/templates/react-monorepo/files/apps/client/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/client/src/app/nav.tsx +14 -0
- package/templates/react-monorepo/files/apps/client/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/client/src/app/router.tsx +50 -0
- package/templates/react-monorepo/files/apps/client/src/app/store.ts +15 -0
- package/templates/react-monorepo/files/apps/client/src/lib/env.ts +12 -0
- package/templates/react-monorepo/files/apps/client/src/main.tsx +19 -0
- package/templates/react-monorepo/files/apps/client/src/routes/home.tsx +17 -0
- package/templates/react-monorepo/files/apps/client/src/routes/settings.tsx +10 -0
- package/templates/react-monorepo/files/apps/client/src/vite-env.d.ts +15 -0
- package/templates/react-monorepo/files/apps/client/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/client/vite.config.ts +120 -0
- package/templates/react-monorepo/files/apps/landing/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/apps/landing/.env.example +20 -0
- package/templates/react-monorepo/files/apps/landing/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/apps/landing/index.html +51 -0
- package/templates/react-monorepo/files/apps/landing/package.json +33 -0
- package/templates/react-monorepo/files/apps/landing/src/App.tsx +3 -0
- package/templates/react-monorepo/files/apps/landing/src/app/providers.tsx +42 -0
- package/templates/react-monorepo/files/apps/landing/src/app/router.tsx +21 -0
- package/templates/react-monorepo/files/apps/landing/src/app/store.ts +18 -0
- package/templates/react-monorepo/files/apps/landing/src/features/seo/routes.ts +65 -0
- package/templates/react-monorepo/files/apps/landing/src/lib/env.ts +10 -0
- package/templates/react-monorepo/files/apps/landing/src/main.tsx +14 -0
- package/templates/react-monorepo/files/apps/landing/src/routes/home.tsx +18 -0
- package/templates/react-monorepo/files/apps/landing/src/vite-env.d.ts +15 -0
- package/templates/react-monorepo/files/apps/landing/tsconfig.json +10 -0
- package/templates/react-monorepo/files/apps/landing/vite.config.ts +169 -0
- package/templates/react-monorepo/files/build.sh +61 -0
- package/templates/react-monorepo/files/docker/nginx.conf +136 -0
- package/templates/react-monorepo/files/package.json +37 -0
- package/templates/react-monorepo/files/packages/.gitkeep +0 -0
- package/templates/react-monorepo/files/packages/api/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/api/package.json +30 -0
- package/templates/react-monorepo/files/packages/api/src/base-api.ts +24 -0
- package/templates/react-monorepo/files/packages/api/src/base-query.ts +29 -0
- package/templates/react-monorepo/files/packages/api/src/config.ts +21 -0
- package/templates/react-monorepo/files/packages/api/src/index.ts +15 -0
- package/templates/react-monorepo/files/packages/api/src/realtime-events.ts +34 -0
- package/templates/react-monorepo/files/packages/api/src/socket.ts +41 -0
- package/templates/react-monorepo/files/packages/api/src/store.ts +39 -0
- package/templates/react-monorepo/files/packages/api/src/upload-query.ts +115 -0
- package/templates/react-monorepo/files/packages/api/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/auth/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/auth/package.json +33 -0
- package/templates/react-monorepo/files/packages/auth/src/client.ts +27 -0
- package/templates/react-monorepo/files/packages/auth/src/config.ts +22 -0
- package/templates/react-monorepo/files/packages/auth/src/guards.tsx +61 -0
- package/templates/react-monorepo/files/packages/auth/src/hooks.ts +13 -0
- package/templates/react-monorepo/files/packages/auth/src/index.ts +20 -0
- package/templates/react-monorepo/files/packages/auth/src/logout.ts +23 -0
- package/templates/react-monorepo/files/packages/auth/src/provider.tsx +52 -0
- package/templates/react-monorepo/files/packages/auth/src/redirect.ts +37 -0
- package/templates/react-monorepo/files/packages/auth/src/slice.ts +54 -0
- package/templates/react-monorepo/files/packages/auth/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/config/README.md +47 -0
- package/templates/react-monorepo/files/packages/config/eslint/base.mjs +83 -0
- package/templates/react-monorepo/files/packages/config/package.json +37 -0
- package/templates/react-monorepo/files/packages/config/tailwind/theme.css +48 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/base.json +22 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/node.json +9 -0
- package/templates/react-monorepo/files/packages/config/tsconfig/react.json +9 -0
- package/templates/react-monorepo/files/packages/config/vite/index.d.ts +18 -0
- package/templates/react-monorepo/files/packages/config/vite/index.mjs +129 -0
- package/templates/react-monorepo/files/packages/hooks/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/hooks/package.json +16 -0
- package/templates/react-monorepo/files/packages/hooks/src/index.ts +3 -0
- package/templates/react-monorepo/files/packages/hooks/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/layouts/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/layouts/package.json +30 -0
- package/templates/react-monorepo/files/packages/layouts/src/app-shell/app-shell.tsx +225 -0
- package/templates/react-monorepo/files/packages/layouts/src/app-shell/user-menu.tsx +74 -0
- package/templates/react-monorepo/files/packages/layouts/src/auth-layout.tsx +137 -0
- package/templates/react-monorepo/files/packages/layouts/src/error-boundary.tsx +38 -0
- package/templates/react-monorepo/files/packages/layouts/src/index.ts +23 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/marketing-layout.tsx +29 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/section-heading.tsx +38 -0
- package/templates/react-monorepo/files/packages/layouts/src/marketing/section.tsx +30 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/app-shell-skeleton.tsx +47 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/empty.tsx +22 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/forbidden.tsx +14 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/loading.tsx +11 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/not-found.tsx +21 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/page-skeleton.tsx +39 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/route-fallback.tsx +26 -0
- package/templates/react-monorepo/files/packages/layouts/src/states/state-screen.tsx +22 -0
- package/templates/react-monorepo/files/packages/layouts/tsconfig.json +4 -0
- package/templates/react-monorepo/files/packages/types/.bin/openapi.js +32 -0
- package/templates/react-monorepo/files/packages/types/.bin/secrets.js +57 -0
- package/templates/react-monorepo/files/packages/types/README.md +28 -0
- package/templates/react-monorepo/files/packages/types/eslint.config.mjs +3 -0
- package/templates/react-monorepo/files/packages/types/openapi-ts.config.ts +22 -0
- package/templates/react-monorepo/files/packages/types/package.json +29 -0
- package/templates/react-monorepo/files/packages/types/src/index.ts +4 -0
- package/templates/react-monorepo/files/packages/types/src/types/api/index.ts +9 -0
- package/templates/react-monorepo/files/packages/types/src/types/api/zod.gen.ts +5 -0
- package/templates/react-monorepo/files/packages/types/tsconfig.json +7 -0
- package/templates/react-monorepo/files/packages/ui/components.json +21 -0
- package/templates/react-monorepo/files/packages/ui/eslint.config.mjs +13 -0
- package/templates/react-monorepo/files/packages/ui/package.json +44 -0
- package/templates/react-monorepo/files/packages/ui/src/components/brand/logo.tsx +51 -0
- package/templates/react-monorepo/files/packages/ui/src/components/brand/social-icons.tsx +37 -0
- package/templates/react-monorepo/files/packages/ui/src/components/form/form.tsx +120 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/alert.tsx +25 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/button.tsx +43 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/calendar.tsx +78 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/card.tsx +44 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/checkbox.tsx +16 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/date-picker.tsx +76 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/input.tsx +22 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/label.tsx +19 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/modal.tsx +94 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/popover.tsx +31 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/progress.tsx +33 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/search-input.tsx +69 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/select.tsx +80 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/skeleton.tsx +12 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/sonner.tsx +23 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/spinner.tsx +8 -0
- package/templates/react-monorepo/files/packages/ui/src/components/ui/textarea.tsx +22 -0
- package/templates/react-monorepo/files/packages/ui/src/index.ts +45 -0
- package/templates/react-monorepo/files/packages/ui/src/lib/utils.ts +7 -0
- package/templates/react-monorepo/files/packages/ui/src/lib/zod-setup.ts +28 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/brand.ts +18 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/meta-context.ts +30 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/meta-provider.tsx +24 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/page-meta.tsx +69 -0
- package/templates/react-monorepo/files/packages/ui/src/meta/use-meta-config.ts +11 -0
- package/templates/react-monorepo/files/packages/ui/src/styles/globals.css +285 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/init-script.ts +23 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-colors.ts +14 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-context.ts +60 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-provider.tsx +101 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/theme-toggle.tsx +40 -0
- package/templates/react-monorepo/files/packages/ui/src/theme/use-theme.ts +11 -0
- package/templates/react-monorepo/files/packages/ui/tsconfig.json +9 -0
- package/templates/react-monorepo/files/turbo.json +24 -0
- package/templates/react-monorepo/template.json +230 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Static build of the app, served by nginx.
|
|
2
|
+
#
|
|
3
|
+
# docker build --build-arg VITE_APP_API_URL=https://api.example.com -t app .
|
|
4
|
+
#
|
|
5
|
+
# IMPORTANT: Vite inlines `VITE_*` values into the JS bundle at BUILD time, so a
|
|
6
|
+
# built image is pinned to one environment. These are build args, not runtime
|
|
7
|
+
# env — setting them in compose does nothing. Build one image per environment,
|
|
8
|
+
# or move to runtime config.
|
|
9
|
+
|
|
10
|
+
# ---------- deps ----------
|
|
11
|
+
# Manifests only, so `yarn install` re-runs solely when a dependency changes.
|
|
12
|
+
FROM node:24-alpine AS deps
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
|
|
15
|
+
COPY package.json yarn.lock .yarnrc.yml ./
|
|
16
|
+
RUN corepack enable && yarn install --immutable
|
|
17
|
+
|
|
18
|
+
# ---------- build ----------
|
|
19
|
+
FROM node:24-alpine AS build
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
ENV NODE_ENV=production
|
|
22
|
+
# Each stage is a fresh image, so corepack has to be enabled again here —
|
|
23
|
+
# without it `yarn` is the bundled 1.x and refuses the packageManager field.
|
|
24
|
+
RUN corepack enable
|
|
25
|
+
|
|
26
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
27
|
+
COPY . .
|
|
28
|
+
|
|
29
|
+
# Build args become env vars so Vite's define step picks them up. Process env
|
|
30
|
+
# takes precedence over a committed .env, so a stale checked-in .env cannot leak
|
|
31
|
+
# into the image as long as the arg is supplied.
|
|
32
|
+
ARG VITE_APP_API_URL
|
|
33
|
+
ARG VITE_APP_SITE_URL
|
|
34
|
+
ARG VITE_APP_SUPPORT_EMAIL
|
|
35
|
+
ENV VITE_APP_API_URL=$VITE_APP_API_URL \
|
|
36
|
+
VITE_APP_SITE_URL=$VITE_APP_SITE_URL \
|
|
37
|
+
VITE_APP_SUPPORT_EMAIL=$VITE_APP_SUPPORT_EMAIL
|
|
38
|
+
|
|
39
|
+
# Fail loudly rather than silently producing a bundle with `undefined` URLs baked
|
|
40
|
+
# in — that failure is invisible until the app 404s at runtime.
|
|
41
|
+
RUN test -n "$VITE_APP_API_URL" || (echo "ERROR: --build-arg VITE_APP_API_URL is required" && exit 1)
|
|
42
|
+
|
|
43
|
+
RUN yarn build
|
|
44
|
+
|
|
45
|
+
# ---------- runtime ----------
|
|
46
|
+
FROM nginx:stable-alpine AS runtime
|
|
47
|
+
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
|
48
|
+
COPY --from=build /app/dist /usr/share/nginx/html
|
|
49
|
+
|
|
50
|
+
EXPOSE 80
|
|
51
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
|
|
52
|
+
CMD wget -qO- http://127.0.0.1/healthz || exit 1
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# yarn (berry, node-modules linker)
|
|
5
|
+
.yarn/*
|
|
6
|
+
!.yarn/patches
|
|
7
|
+
!.yarn/plugins
|
|
8
|
+
!.yarn/releases
|
|
9
|
+
!.yarn/sdks
|
|
10
|
+
!.yarn/versions
|
|
11
|
+
.pnp.*
|
|
12
|
+
yarn-error.log
|
|
13
|
+
|
|
14
|
+
# build output
|
|
15
|
+
dist/
|
|
16
|
+
dev-dist/
|
|
17
|
+
*.tsbuildinfo
|
|
18
|
+
|
|
19
|
+
# env
|
|
20
|
+
.env
|
|
21
|
+
.env.local
|
|
22
|
+
.env.*.local
|
|
23
|
+
.env.test
|
|
24
|
+
.env.production
|
|
25
|
+
|
|
26
|
+
# editor / os
|
|
27
|
+
.DS_Store
|
|
28
|
+
*.log
|
|
29
|
+
|
|
30
|
+
# generated OpenAPI schema dump. The types generated FROM it are committed —
|
|
31
|
+
# they are the contract this app compiles against — but the dump itself is a
|
|
32
|
+
# build artefact of whatever backend happened to be running.
|
|
33
|
+
openapi.json
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Build + push the app image.
|
|
3
|
+
#
|
|
4
|
+
# ./build.sh [env] [tag]
|
|
5
|
+
#
|
|
6
|
+
# ./build.sh # uses .env -> :latest
|
|
7
|
+
# ./build.sh prod # uses .env.production
|
|
8
|
+
# ./build.sh prod 26_07_26_1
|
|
9
|
+
#
|
|
10
|
+
# The VITE_* URLs are compiled INTO the bundle, so the image is specific to the
|
|
11
|
+
# environment it was built for. That is why the env name is an argument: the same
|
|
12
|
+
# image cannot be promoted from staging to prod. Pull the env file first with
|
|
13
|
+
# yarn secrets -e <env>
|
|
14
|
+
set -eu
|
|
15
|
+
|
|
16
|
+
ENV_NAME="${1:-dev}"
|
|
17
|
+
TAG="${2:-latest}"
|
|
18
|
+
REGISTRY="${REGISTRY:-registry.example.com/acme}"
|
|
19
|
+
IMAGE="${IMAGE:-web}"
|
|
20
|
+
PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
|
|
21
|
+
|
|
22
|
+
case "$ENV_NAME" in
|
|
23
|
+
dev) ENV_FILE=".env" ;;
|
|
24
|
+
test) ENV_FILE=".env.test" ;;
|
|
25
|
+
staging) ENV_FILE=".env.staging" ;;
|
|
26
|
+
prod | production) ENV_FILE=".env.production" ;;
|
|
27
|
+
*) echo "unknown env '$ENV_NAME' (dev|test|staging|prod)" >&2; exit 1 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
[ -f "$ENV_FILE" ] || {
|
|
31
|
+
echo "missing $ENV_FILE — run: yarn secrets -e $ENV_NAME" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Read a var out of the env file, tolerating the quoted style these files use.
|
|
36
|
+
read_var() {
|
|
37
|
+
sed -n "s/^$1=['\"]\{0,1\}\([^'\"]*\)['\"]\{0,1\}$/\1/p" "$ENV_FILE" | tail -1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
BUILD_ARGS=""
|
|
41
|
+
for VAR in VITE_APP_API_URL VITE_APP_SITE_URL VITE_APP_SUPPORT_EMAIL; do
|
|
42
|
+
VALUE="$(read_var "$VAR")"
|
|
43
|
+
[ -n "$VALUE" ] && BUILD_ARGS="$BUILD_ARGS --build-arg $VAR=$VALUE"
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
echo "Building for '$ENV_NAME' -> $REGISTRY/$IMAGE:$TAG"
|
|
47
|
+
echo " (URLs baked in from $ENV_FILE)"
|
|
48
|
+
|
|
49
|
+
# shellcheck disable=SC2086
|
|
50
|
+
docker buildx build --push --platform "$PLATFORMS" \
|
|
51
|
+
$BUILD_ARGS \
|
|
52
|
+
-t "$REGISTRY/$IMAGE:$TAG" \
|
|
53
|
+
.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Static host for the built Vite SPA.
|
|
2
|
+
#
|
|
3
|
+
# The cache rules are load-bearing, not cosmetic: get them wrong and returning
|
|
4
|
+
# users are pinned to a stale build. Rule of thumb — anything with a content
|
|
5
|
+
# hash in its name is immutable forever; anything with a stable name must be
|
|
6
|
+
# revalidated every time.
|
|
7
|
+
|
|
8
|
+
server {
|
|
9
|
+
listen 80;
|
|
10
|
+
server_name _;
|
|
11
|
+
root /usr/share/nginx/html;
|
|
12
|
+
index index.html;
|
|
13
|
+
|
|
14
|
+
# Compose/orchestrator health probe. Must not be logged or it floods access.log.
|
|
15
|
+
location = /healthz {
|
|
16
|
+
access_log off;
|
|
17
|
+
add_header Content-Type text/plain;
|
|
18
|
+
return 200 "ok\n";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
gzip on;
|
|
22
|
+
gzip_vary on;
|
|
23
|
+
gzip_min_length 1024;
|
|
24
|
+
gzip_proxied any;
|
|
25
|
+
gzip_types text/plain text/css application/javascript application/json
|
|
26
|
+
image/svg+xml application/manifest+json;
|
|
27
|
+
|
|
28
|
+
# Vite emits content-hashed filenames here, so the content can never change
|
|
29
|
+
# under a given URL — safe to cache for a year.
|
|
30
|
+
# NB: no `expires` directive. `expires` emits its own Cache-Control, so
|
|
31
|
+
# combining it with add_header sends the header twice and clients disagree on
|
|
32
|
+
# which wins — `immutable` can be dropped. One explicit header instead.
|
|
33
|
+
location /assets/ {
|
|
34
|
+
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
35
|
+
try_files $uri =404;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# The service worker and its Workbox runtime keep STABLE names across builds.
|
|
39
|
+
# If these are cached the browser keeps running the old worker, which keeps
|
|
40
|
+
# serving the old precached app shell — users get stuck on a previous version
|
|
41
|
+
# with no way out but a hard reload. Only a PWA build ships them; the rule is
|
|
42
|
+
# harmless otherwise.
|
|
43
|
+
location ~* ^/(sw\.js|registerSW\.js|workbox-[^/]+\.js)$ {
|
|
44
|
+
add_header Cache-Control "no-cache, must-revalidate" always;
|
|
45
|
+
expires off;
|
|
46
|
+
try_files $uri =404;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# Re-checked on every load so a deploy is picked up immediately, and so the
|
|
50
|
+
# manifest's start_url/icons stay in step with the installed PWA.
|
|
51
|
+
location ~* ^/(manifest\.webmanifest|manifest\.json)$ {
|
|
52
|
+
add_header Cache-Control "no-cache, must-revalidate" always;
|
|
53
|
+
expires off;
|
|
54
|
+
try_files $uri =404;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Long-lived but not immutable: stable names, so they must stay revalidatable,
|
|
58
|
+
# but they rarely change.
|
|
59
|
+
location ~* \.(png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ {
|
|
60
|
+
add_header Cache-Control "public, max-age=604800" always;
|
|
61
|
+
try_files $uri =404;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# SPA fallback. index.html is never cached — it references the hashed bundles,
|
|
65
|
+
# so a cached copy would keep pointing at assets from the previous deploy.
|
|
66
|
+
#
|
|
67
|
+
# `$uri/index.html` comes BEFORE `$uri/` deliberately. If you add a prerender
|
|
68
|
+
# pass that emits `about/index.html`, matching `$uri/` on a directory makes
|
|
69
|
+
# nginx 301 to the trailing-slash form — verified: `/about` redirected to
|
|
70
|
+
# `/about/`. That fights the canonical tag and the sitemap, which both use the
|
|
71
|
+
# slashless `/about`, and adds a hop for every crawler. Serving the file
|
|
72
|
+
# directly returns 200 at the canonical URL. Harmless for a pure SPA: it ships
|
|
73
|
+
# no such directories, so the extra probe never matches.
|
|
74
|
+
location / {
|
|
75
|
+
try_files $uri $uri/index.html $uri/ @fallback;
|
|
76
|
+
add_header Cache-Control "no-cache, must-revalidate" always;
|
|
77
|
+
expires off;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# What an unknown path gets, decided by what the build actually emitted rather
|
|
81
|
+
# than by a config flag that can disagree with it.
|
|
82
|
+
#
|
|
83
|
+
# A pure SPA ships no `404.html`, so this falls through to `index.html` and the
|
|
84
|
+
# router renders its own not-found page. Answering a real 404 here instead
|
|
85
|
+
# would break every client-side route.
|
|
86
|
+
#
|
|
87
|
+
# If you add a prerender pass, emit `404.html` and this starts answering 404
|
|
88
|
+
# automatically. That matters: without one, a prerendered homepage means every
|
|
89
|
+
# typo'd or stale URL returns 200 with a full copy of `/`, and a soft 404 with
|
|
90
|
+
# a body is worse than one without — there is something for a crawler to
|
|
91
|
+
# index.
|
|
92
|
+
#
|
|
93
|
+
# `if` is limited here to `return` and `rewrite ... last`, the two directives
|
|
94
|
+
# documented as safe inside a location.
|
|
95
|
+
location @fallback {
|
|
96
|
+
error_page 404 /404.html;
|
|
97
|
+
if (-f $document_root/404.html) {
|
|
98
|
+
return 404;
|
|
99
|
+
}
|
|
100
|
+
rewrite ^ /index.html last;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Reachable only through error_page, so the page cannot also be served as a
|
|
104
|
+
# regular 200 at /404.html and be indexed on its own.
|
|
105
|
+
location = /404.html {
|
|
106
|
+
internal;
|
|
107
|
+
add_header Cache-Control "no-cache, must-revalidate" always;
|
|
108
|
+
expires off;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Modest hardening. Deliberately NO Content-Security-Policy header: the apps
|
|
112
|
+
# ship an inline <script> in index.html that applies the saved theme before
|
|
113
|
+
# first paint, and a default CSP without 'unsafe-inline' would block it and
|
|
114
|
+
# reintroduce the light-mode flash. Add CSP only with a nonce/hash for that
|
|
115
|
+
# script. HSTS belongs on the TLS-terminating proxy in front of this, not here.
|
|
116
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
117
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
118
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
119
|
+
|
|
120
|
+
# RFC 9116 puts security.txt under /.well-known/, which the deny below would
|
|
121
|
+
# otherwise 403 — that is the whole point of this block. `^~` is what makes the
|
|
122
|
+
# exception stick: it tells nginx that once this prefix matches, regex
|
|
123
|
+
# locations are skipped entirely, so `location ~ /\.` never gets a look at it.
|
|
124
|
+
# Deliberately adds no headers of its own, because `add_header` in a location
|
|
125
|
+
# REPLACES the inherited set rather than extending it — the server-level
|
|
126
|
+
# hardening headers above would silently stop applying here.
|
|
127
|
+
location ^~ /.well-known/ {
|
|
128
|
+
try_files $uri =404;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
location ~ /\. {
|
|
132
|
+
deny all;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import globals from 'globals';
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
5
|
+
import tseslint from 'typescript-eslint';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Flat ESLint config for a React 19 + TypeScript app.
|
|
9
|
+
*/
|
|
10
|
+
export default tseslint.config(
|
|
11
|
+
// `dev-dist` is vite-plugin-pwa's dev output (Workbox sources). Generated and
|
|
12
|
+
// gitignored — linting it only produces "rule not found" noise that makes
|
|
13
|
+
// `yarn lint` fail permanently, hiding real findings. `src/shared/types/api`
|
|
14
|
+
// is generated from the OpenAPI schema and formatted by its own pipeline.
|
|
15
|
+
{ ignores: ['dist', 'dev-dist', 'node_modules', 'src/shared/types/api'] },
|
|
16
|
+
{
|
|
17
|
+
files: ['**/*.{ts,tsx}'],
|
|
18
|
+
extends: [
|
|
19
|
+
js.configs.recommended,
|
|
20
|
+
tseslint.configs.recommended,
|
|
21
|
+
reactHooks.configs.flat.recommended,
|
|
22
|
+
reactRefresh.configs.vite
|
|
23
|
+
],
|
|
24
|
+
languageOptions: {
|
|
25
|
+
ecmaVersion: 2023,
|
|
26
|
+
globals: globals.browser
|
|
27
|
+
},
|
|
28
|
+
rules: {
|
|
29
|
+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
|
30
|
+
'@typescript-eslint/no-empty-object-type': 'off',
|
|
31
|
+
|
|
32
|
+
// The shadcn primitives export their cva variants next to the component
|
|
33
|
+
// (`buttonVariants`), which is what `allowConstantExport` exists for.
|
|
34
|
+
// Left as a warning rather than an error: a fast-refresh hint should not
|
|
35
|
+
// be able to fail CI on a file that works.
|
|
36
|
+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
|
37
|
+
|
|
38
|
+
// iOS auto-zooms a focused text control whose font-size is under 16px and
|
|
39
|
+
// never restores the zoom on blur, so one tap leaves the user zoomed for
|
|
40
|
+
// the session — in the installed PWA too. `text-sm` is 14px.
|
|
41
|
+
//
|
|
42
|
+
// The lookbehind lets `sm:text-sm` through: `text-base sm:text-sm` is the
|
|
43
|
+
// correct fix (16px on mobile, the intended density from `sm` up).
|
|
44
|
+
//
|
|
45
|
+
// Only catches literal className strings on raw `<input>`/`<textarea>`/
|
|
46
|
+
// `<select>` — that is exactly how this regresses. Prefer `SearchInput` /
|
|
47
|
+
// `Input` from `@/shared/ui`, which already handle it.
|
|
48
|
+
'no-restricted-syntax': [
|
|
49
|
+
'error',
|
|
50
|
+
{
|
|
51
|
+
selector:
|
|
52
|
+
"JSXOpeningElement[name.name=/^(input|textarea|select)$/] JSXAttribute[name.name='className'] Literal[value=/(?<![:-])(text-xs|text-sm)(?![a-zA-Z0-9-])/]",
|
|
53
|
+
message:
|
|
54
|
+
'A text input under 16px makes iOS zoom in on focus and never zoom back out. Use `text-base sm:text-sm`, or the SearchInput / Input components from @/shared/ui.'
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
// `router.tsx` exports the router object alongside the shell components it
|
|
61
|
+
// mounts. Splitting them would mean a second file existing only to satisfy a
|
|
62
|
+
// heuristic — the router is not a component and is never hot-swapped.
|
|
63
|
+
files: ['**/app/router.tsx'],
|
|
64
|
+
rules: { 'react-refresh/only-export-components': 'off' }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
// The shadcn primitives export their cva variants beside the component
|
|
68
|
+
// (`buttonVariants`, `alertVariants`) — that is how the upstream components
|
|
69
|
+
// are written, and it is why consumers can compose them. `allowConstantExport`
|
|
70
|
+
// does not cover a `cva()` call, so without this the rule warns permanently on
|
|
71
|
+
// unmodified upstream code, which is how a lint report stops being read.
|
|
72
|
+
files: ['**/components/ui/**'],
|
|
73
|
+
rules: { 'react-refresh/only-export-components': 'off' }
|
|
74
|
+
}
|
|
75
|
+
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
7
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
8
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
9
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
10
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
11
|
+
<meta name="theme-color" content="%THEME_LIGHT%" />
|
|
12
|
+
<!--
|
|
13
|
+
Applies the saved (or system) theme before first paint, so the app never
|
|
14
|
+
flashes light then flips to dark. Mirrors `themeInitScript` in
|
|
15
|
+
src/shared/ui — inlined because it must run before any module loads. Keep
|
|
16
|
+
the two in sync. %THEME_LIGHT/DARK% come from THEME_COLORS via vite.config.
|
|
17
|
+
-->
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
try {
|
|
21
|
+
var m = localStorage.getItem('acme-theme');
|
|
22
|
+
var d =
|
|
23
|
+
m === 'dark' || ((m === null || m === 'system') && matchMedia('(prefers-color-scheme: dark)').matches);
|
|
24
|
+
var r = document.documentElement;
|
|
25
|
+
r.classList.toggle('dark', d);
|
|
26
|
+
r.style.colorScheme = d ? 'dark' : 'light';
|
|
27
|
+
var t = document.querySelector('meta[name="theme-color"]');
|
|
28
|
+
if (t) t.setAttribute('content', d ? '%THEME_DARK%' : '%THEME_LIGHT%');
|
|
29
|
+
} catch (e) {}
|
|
30
|
+
})();
|
|
31
|
+
</script>
|
|
32
|
+
<!--
|
|
33
|
+
globals.css only styles `body`, so without this the UA canvas paints white
|
|
34
|
+
for the moment before the stylesheet lands — a white flash on a dark load.
|
|
35
|
+
-->
|
|
36
|
+
<style>
|
|
37
|
+
html {
|
|
38
|
+
background: %THEME_LIGHT%;
|
|
39
|
+
}
|
|
40
|
+
html.dark {
|
|
41
|
+
background: %THEME_DARK%;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
<meta name="description" content="Acme Corp." />
|
|
45
|
+
<!--
|
|
46
|
+
Every route this app serves is private, so nothing here should be indexed.
|
|
47
|
+
`robots.txt` only discourages crawling, while `noindex` prevents listing,
|
|
48
|
+
so both belong in a deployment. This app's routes include password-reset
|
|
49
|
+
and email-verify URLs.
|
|
50
|
+
|
|
51
|
+
With the marketing site included this tag goes away: indexing is then
|
|
52
|
+
decided per route by `PageMeta`, and the build emits a `noindex` of its own
|
|
53
|
+
on any non-production origin. See vite.config.ts.
|
|
54
|
+
-->
|
|
55
|
+
<meta name="robots" content="noindex, nofollow" /> <!-- @feature !marketing -->
|
|
56
|
+
<title>%APP_TITLE%</title>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div id="root"></div>
|
|
60
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
61
|
+
</body>
|
|
62
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from '@hey-api/openapi-ts';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
input: 'openapi.json',
|
|
5
|
+
output: {
|
|
6
|
+
format: 'prettier',
|
|
7
|
+
path: 'src/shared/types/api',
|
|
8
|
+
lint: 'eslint'
|
|
9
|
+
},
|
|
10
|
+
exportCore: false,
|
|
11
|
+
client: '@hey-api/client-fetch',
|
|
12
|
+
experimentalParser: true,
|
|
13
|
+
plugins: [
|
|
14
|
+
{
|
|
15
|
+
name: 'zod'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: '@hey-api/typescript',
|
|
19
|
+
enums: 'typescript'
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "acme-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "yarn@4.5.3",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "vite",
|
|
12
|
+
"build": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.node.json && vite build",
|
|
13
|
+
"preview": "vite preview",
|
|
14
|
+
"lint": "eslint .",
|
|
15
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.node.json",
|
|
16
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,mjs,json,md,css}\"",
|
|
17
|
+
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mjs,json,md,css}\"",
|
|
18
|
+
"prepare": "husky",
|
|
19
|
+
"secrets": "node .bin/secrets.js",
|
|
20
|
+
"gen:schema": "node .bin/openapi.js",
|
|
21
|
+
"gen:types": "openapi-ts",
|
|
22
|
+
"gen": "node .bin/openapi.js && openapi-ts"
|
|
23
|
+
},
|
|
24
|
+
"lint-staged": {
|
|
25
|
+
"*.{ts,tsx,js,jsx,mjs,json,md,css}": "prettier --write"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
29
|
+
"@radix-ui/react-label": "^2.1.7",
|
|
30
|
+
"@radix-ui/react-popover": "^1.1.21",
|
|
31
|
+
"@radix-ui/react-select": "^2.3.4",
|
|
32
|
+
"@radix-ui/react-slot": "^1.2.5",
|
|
33
|
+
"@reduxjs/toolkit": "^2.12.0",
|
|
34
|
+
"apexcharts": "^6.5.0",
|
|
35
|
+
"better-auth": "^1.6.2",
|
|
36
|
+
"class-variance-authority": "^0.7.1",
|
|
37
|
+
"clsx": "^2.1.1",
|
|
38
|
+
"date-fns": "^3",
|
|
39
|
+
"formik": "^2.4.9",
|
|
40
|
+
"lucide-react": "^1.17.0",
|
|
41
|
+
"react": "^19.2.6",
|
|
42
|
+
"react-apexcharts": "^2.1.1",
|
|
43
|
+
"react-day-picker": "^9",
|
|
44
|
+
"react-dom": "^19.2.6",
|
|
45
|
+
"react-redux": "^9.3.0",
|
|
46
|
+
"react-router": "^8.2.0",
|
|
47
|
+
"socket.io-client": "^4.8.1",
|
|
48
|
+
"sonner": "^2.0.7",
|
|
49
|
+
"tailwind-merge": "^3.6.0",
|
|
50
|
+
"zod": "^4.4.3",
|
|
51
|
+
"zod-formik-adapter": "^2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^10.0.1",
|
|
55
|
+
"@hey-api/client-fetch": "^0.5.2",
|
|
56
|
+
"@hey-api/openapi-ts": "^0.59.1",
|
|
57
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
58
|
+
"@types/node": "^24.10.1",
|
|
59
|
+
"@types/react": "^19.2.14",
|
|
60
|
+
"@types/react-dom": "^19.2.3",
|
|
61
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
62
|
+
"axios": "^1.7.9",
|
|
63
|
+
"dotenv": "^16.4.7",
|
|
64
|
+
"eslint": "^10.3.0",
|
|
65
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
66
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
67
|
+
"globals": "^17.6.0",
|
|
68
|
+
"husky": "^9.1.7",
|
|
69
|
+
"lint-staged": "^15.3.0",
|
|
70
|
+
"prettier": "^3.4.2",
|
|
71
|
+
"tiny-invariant": "^1.3.3",
|
|
72
|
+
"typescript": "~6.0.2",
|
|
73
|
+
"typescript-eslint": "^8.59.2",
|
|
74
|
+
"vite": "^8.0.12",
|
|
75
|
+
"vite-plugin-pwa": "^1.3.0"
|
|
76
|
+
},
|
|
77
|
+
"resolutions": {
|
|
78
|
+
"typescript": "~6.0.2"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Home, Settings } from 'lucide-react';
|
|
2
|
+
import { ShieldCheck } from 'lucide-react'; // @feature admin
|
|
3
|
+
import type { NavItem } from '@/shared/layouts';
|
|
4
|
+
import { appPath } from '@/lib/paths';
|
|
5
|
+
import { ADMIN_BASE } from '@/lib/paths'; // @feature admin
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drives the app shell's navigation. Keep it in step with `router.tsx` — the
|
|
9
|
+
* shell derives the sidebar from this list, not from the router, so a route
|
|
10
|
+
* added in one place and not the other is simply unreachable or dead.
|
|
11
|
+
*
|
|
12
|
+
* Paths come from `appPath` rather than being written out, because the app's
|
|
13
|
+
* mount point moves when the marketing site is included. `icon` is a rendered
|
|
14
|
+
* element, not a component reference.
|
|
15
|
+
*/
|
|
16
|
+
export const NAV_ITEMS: NavItem[] = [
|
|
17
|
+
{ to: appPath(), label: 'Home', icon: <Home className="size-4" />, end: true },
|
|
18
|
+
{ to: appPath('settings'), label: 'Settings', icon: <Settings className="size-4" /> }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// @feature:start admin
|
|
22
|
+
/**
|
|
23
|
+
* The console's own nav. Separate from `NAV_ITEMS` on purpose: the admin section
|
|
24
|
+
* is a different branch of the router behind a different guard, and mixing an
|
|
25
|
+
* admin link into the member sidebar would render it for members too — a link
|
|
26
|
+
* that 403s is a worse answer than no link.
|
|
27
|
+
*/
|
|
28
|
+
export const ADMIN_NAV_ITEMS: NavItem[] = [
|
|
29
|
+
{ to: ADMIN_BASE, label: 'Overview', icon: <ShieldCheck className="size-4" />, end: true },
|
|
30
|
+
{ to: appPath(), label: 'Back to app', icon: <Home className="size-4" /> }
|
|
31
|
+
];
|
|
32
|
+
// @feature:end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Provider } from 'react-redux';
|
|
3
|
+
import { configureApi } from '@/shared/api';
|
|
4
|
+
import { AuthProvider, configureAuth, redirectToLogin } from '@/shared/auth';
|
|
5
|
+
import { MetaProvider, ThemeProvider, Toaster, installZodErrorMap } from '@/shared/ui';
|
|
6
|
+
import { API_URL } from '@/lib/env';
|
|
7
|
+
import { appPath } from '@/lib/paths';
|
|
8
|
+
import { store } from './store';
|
|
9
|
+
|
|
10
|
+
// Wired at module scope, before anything renders. The first thing this app does
|
|
11
|
+
// is ask the backend who the user is, and doing this in an effect would put that
|
|
12
|
+
// request behind a render pass for no gain.
|
|
13
|
+
configureApi({ baseUrl: API_URL, onUnauthorized: () => redirectToLogin() });
|
|
14
|
+
configureAuth({
|
|
15
|
+
authApiBaseUrl: `${API_URL}/auth`,
|
|
16
|
+
// `trustedOrigins` is left to its default of this app's own origin — one
|
|
17
|
+
// deployment, one origin. Add to it only for a deliberate hand-off elsewhere;
|
|
18
|
+
// every entry is a URL an attacker may put in `?redirect=`.
|
|
19
|
+
defaultRedirect: appPath()
|
|
20
|
+
});
|
|
21
|
+
installZodErrorMap();
|
|
22
|
+
|
|
23
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
24
|
+
return (
|
|
25
|
+
// ThemeProvider above the Redux Provider: it only touches the document
|
|
26
|
+
// (`dark` class, color-scheme, theme-color meta) and must not re-render
|
|
27
|
+
// through the store.
|
|
28
|
+
<ThemeProvider>
|
|
29
|
+
<MetaProvider>
|
|
30
|
+
<Provider store={store}>
|
|
31
|
+
<AuthProvider>
|
|
32
|
+
{children}
|
|
33
|
+
<Toaster />
|
|
34
|
+
</AuthProvider>
|
|
35
|
+
</Provider>
|
|
36
|
+
</MetaProvider>
|
|
37
|
+
</ThemeProvider>
|
|
38
|
+
);
|
|
39
|
+
}
|