@starci/hfs 5.1.0 → 5.3.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/CHANGELOG.md +54 -0
- package/README.md +11 -2
- package/cache/hfs-family.d.mts +2 -3
- package/cache/hfs-family.mjs +4 -9
- package/cache/secret-records.d.mts +7 -0
- package/cache/secret-records.mjs +12 -0
- package/cache/session.mjs +4 -2
- package/codemod/account-doors.d.mts +36 -0
- package/codemod/account-doors.mjs +128 -0
- package/codemod/main.mjs +31 -1
- package/codemod/module-definition.d.mts +52 -0
- package/codemod/module-definition.mjs +242 -0
- package/codemod/quality-binding.d.mts +33 -0
- package/codemod/quality-binding.mjs +38 -0
- package/codemod/secrets-cache.d.mts +35 -0
- package/codemod/secrets-cache.mjs +70 -0
- package/codemod/sops-rules.d.mts +34 -0
- package/codemod/sops-rules.mjs +69 -0
- package/codemod/theme-tokens.mjs +48 -2
- package/db/postgres.mjs +9 -1
- package/lint/run.d.mts +6 -0
- package/lint/run.mjs +63 -10
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +65 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +9 -9
- package/runtime/knowledge/hfs/fork-images.yaml +7 -0
- package/runtime/knowledge/hfs/rules.yaml +67 -15
- package/runtime/knowledge/hfs/slots.yaml +174 -30
- package/runtime/modules/kernel/failure-codes.yaml +67 -4
- package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
- package/runtime/scripts/hfs/architecture/config.d.mts +4 -0
- package/runtime/scripts/hfs/architecture/config.mjs +25 -1
- package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
- package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +1 -1
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +6 -1
- package/runtime/scripts/hfs/architecture/root-packages.d.mts +26 -0
- package/runtime/scripts/hfs/architecture/root-packages.mjs +78 -0
- package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
- package/runtime/scripts/hfs/architecture/type-context.d.mts +4 -1
- package/runtime/scripts/hfs/architecture/type-context.mjs +30 -0
- package/runtime/scripts/hfs/architecture/typescript.d.mts +7 -1
- package/runtime/scripts/hfs/architecture/typescript.mjs +21 -1
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +1 -1
- package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
- package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
- package/runtime/scripts/hfs/rules/database-access.mjs +39 -1
- package/runtime/scripts/hfs/rules/monorepo.d.mts +4 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +10 -5
- package/runtime/scripts/hfs/rules/secrets.mjs +11 -0
- package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
- package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
- package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
- package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
- package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
- package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
- package/runtime/scripts/hfs/secret-errors.d.mts +3 -0
- package/runtime/scripts/hfs/secret-errors.mjs +8 -0
- package/runtime/scripts/hfs/secret-init.d.mts +6 -0
- package/runtime/scripts/hfs/secret-init.mjs +23 -0
- package/runtime/scripts/hfs/secret-sops.mjs +9 -1
- package/runtime/scripts/hfs/secret-standard.d.mts +20 -0
- package/runtime/scripts/hfs/secret-standard.mjs +106 -0
- package/runtime/scripts/hfs/secret.mjs +15 -8
- package/runtime/scripts/hfs/slots.mjs +1 -1
- package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
- package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
- package/runtime/scripts/hfs/view.d.mts +23 -0
- package/runtime/scripts/hfs/view.mjs +68 -0
- package/runtime/scripts/lib/declaration-surface.d.mts +4 -0
- package/runtime/scripts/lib/declaration-surface.mjs +55 -1
- package/runtime/scripts/lib/secret-records.d.mts +13 -0
- package/runtime/scripts/lib/secret-records.mjs +83 -0
- package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
- package/runtime/scripts/lib/swarm-stack.mjs +2 -2
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-module.d.mts +16 -0
- package/scaffold/add-module.mjs +232 -0
- package/scaffold/add-queue-lite.mjs +50 -3
- package/scaffold/add.mjs +5 -0
- package/scaffold/app.d.mts +2 -2
- package/scaffold/app.mjs +16 -18
- package/scaffold/lite-exports.mjs +1 -15
- package/scaffold/module-registry.d.mts +19 -0
- package/scaffold/module-registry.mjs +84 -0
- package/scripts/sync-bundles.d.mts +8 -0
- package/src/main.mjs +23 -2
- package/stack/cli.mjs +6 -1
- package/stack/context.d.mts +15 -0
- package/stack/context.mjs +1 -0
- package/stack/images/openclaw-runtime/Dockerfile +18 -0
- package/stack/images/openclaw-runtime/entrypoint.sh +23 -0
- package/stack/images/openclaw-runtime/openclaw-init.mjs +101 -0
- package/stack/images/openclaw-runtime/openclaw-relay.mjs +25 -0
- package/stack/local-cli.d.mts +25 -0
- package/stack/local-cli.mjs +89 -0
- package/stack/local-config.d.mts +17 -0
- package/stack/local-config.mjs +37 -0
- package/stack/local-custody.d.mts +24 -0
- package/stack/local-custody.mjs +109 -0
- package/stack/local-doctor.d.mts +6 -0
- package/stack/local-doctor.mjs +49 -0
- package/stack/local-down.d.mts +8 -0
- package/stack/local-down.mjs +20 -0
- package/stack/local-host.d.mts +42 -0
- package/stack/local-host.mjs +163 -0
- package/stack/local-logs.d.mts +8 -0
- package/stack/local-logs.mjs +32 -0
- package/stack/local-ports.d.mts +7 -0
- package/stack/local-ports.mjs +39 -0
- package/stack/local-prepare.d.mts +22 -0
- package/stack/local-prepare.mjs +62 -0
- package/stack/local-run.d.mts +71 -0
- package/stack/local-run.mjs +200 -0
- package/stack/local-state.d.mts +35 -0
- package/stack/local-state.mjs +29 -0
- package/stack/local-status.d.mts +6 -0
- package/stack/local-status.mjs +36 -0
- package/stack/local-up.d.mts +11 -0
- package/stack/local-up.mjs +169 -0
- package/stack/local-verify.d.mts +6 -0
- package/stack/local-verify.mjs +21 -0
- package/stack/runtime-layers.d.mts +21 -0
- package/stack/runtime-layers.mjs +67 -0
- package/sync/hygiene.mjs +3 -1
- package/sync/index.d.mts +15 -12
- package/sync/index.mjs +114 -28
- package/templates/app/auth-doors.sql.partial +115 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
- package/templates/app/module/jest.config.js.tpl +8 -0
- package/templates/app/module/src/index.ts.tpl +2 -0
- package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
- package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
- package/templates/app/module/src/manifest/index.ts.tpl +1 -0
- package/templates/app/module/src/public/index.ts.tpl +2 -0
- package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
- package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
- package/templates/app/module/src/template/index.ts.tpl +1 -0
- package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
- package/templates/app/module/src/workbench/index.ts.tpl +2 -0
- package/templates/app/package-scripts/core.json +1 -1
- package/templates/app/package-scripts/package.json +1 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +85 -70
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +24 -12
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +43 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +21 -26
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +6 -1
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +76 -13
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +44 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +6 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +21 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +6 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +145 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/forgot.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/resend.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/reset.request.ts +18 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/signup.request.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/token.request.ts +11 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +19 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +209 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +84 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.spec.ts +233 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.ts +176 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-sign-in.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/account-doors.port.ts +98 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-email-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +10 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +95 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +93 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +40 -0
- package/templates/be/inbox/module-definition.ts.tpl +2 -9
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +2 -11
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +3 -9
- package/templates/be/platform/graphql/graphql.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +3 -13
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +4 -9
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +5 -11
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +2 -9
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +3 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +12 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +40 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +22 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +50 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity-mail.messages.ts +65 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.config.ts +33 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.decorators.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module.ts +27 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.options.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +5 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
- package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/rate-limit.guard.spec.ts +11 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +22 -4
- package/templates/be/table/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/table/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/table/module-definition.ts.tpl +2 -9
- package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
- package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -4
- package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/email-doors.ts +16 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/index.ts +5 -0
- package/templates/fe/common/site-shell.tsx.partial +3 -3
- package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +15 -12
- package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +30 -67
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +6 -21
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +10 -16
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/confirm/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/forgot/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/resend/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/reset/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/signup/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +5 -9
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/component.tsx +51 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/index.tsx +84 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppConfirmPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppForgotPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +11 -3
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +4 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResendPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResetPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppSignupPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/emailAuth.shared.ts +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/useEmailDoor.ts +32 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +7 -30
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +13 -14
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +4 -17
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -41
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -40
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
|
@@ -42,78 +42,93 @@ components:
|
|
|
42
42
|
environments:
|
|
43
43
|
dev:
|
|
44
44
|
status: supported
|
|
45
|
-
runtime: docker-
|
|
46
|
-
composeFiles: [infra/
|
|
45
|
+
runtime: docker-compose
|
|
46
|
+
composeFiles: [infra/compose/compose.yaml]
|
|
47
47
|
components:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ingress: {replicas: 1, port: 80}
|
|
48
|
+
db: {port: 54322}
|
|
49
|
+
api: {port: 3001}
|
|
50
|
+
web: {port: 3000}
|
|
52
51
|
runbook: README.md
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
52
|
+
# Dev custody is generated once, dev-only, by the owner-armed `starci app secret init --env dev --execute`, which also declares each key here.
|
|
53
|
+
secrets: []
|
|
54
|
+
# `starci app stack up --env dev --execute` reads this block (PROJECT-STACKS-6): host prerequisites, dev custody, setup, the compose services,
|
|
55
|
+
# the environment's own database role, migrations, native api and web processes, readiness, origins.
|
|
56
|
+
local:
|
|
57
|
+
ports:
|
|
58
|
+
db: 54322
|
|
59
|
+
api: 3001
|
|
60
|
+
web: 3000
|
|
61
|
+
mail: 1025
|
|
62
|
+
mailui: 8025
|
|
63
|
+
services:
|
|
64
|
+
- {name: db, wait: healthy}
|
|
65
|
+
- {name: mail, wait: healthy}
|
|
66
|
+
database:
|
|
67
|
+
service: db
|
|
68
|
+
port: db
|
|
69
|
+
urlEnv: PRIMARY_DB_URL
|
|
70
|
+
appRole: app_be
|
|
71
|
+
password: {member: db, key: DEV_DB_OWNER_PASSWORD}
|
|
72
|
+
adminPassword: {member: db, key: POSTGRES_PASSWORD}
|
|
73
|
+
appPassword: {member: db, key: DEV_DB_APP_PASSWORD}
|
|
74
|
+
grants:
|
|
75
|
+
- {name: db, keys: [POSTGRES_PASSWORD]}
|
|
76
|
+
custody:
|
|
77
|
+
- {member: db, key: POSTGRES_PASSWORD, kind: password}
|
|
78
|
+
- {member: db, key: DEV_DB_OWNER_PASSWORD, kind: password}
|
|
79
|
+
- {member: db, key: DEV_DB_APP_PASSWORD, kind: password}
|
|
80
|
+
- {member: auth, key: AUTH_JWT_PRIVATE_KEY, kind: ed25519-pem}
|
|
81
|
+
- {member: google, key: GOOGLE_CLIENT_SECRET, kind: hex}
|
|
82
|
+
- {member: storage, key: STORAGE_ACCESS_KEY_ID, kind: hex, bytes: 12}
|
|
83
|
+
- {member: storage, key: STORAGE_SECRET_ACCESS_KEY, kind: hex}
|
|
84
|
+
setup:
|
|
85
|
+
- {name: install, argv: [npm, ci], heavy: install, skipIfExists: node_modules}
|
|
86
|
+
- {name: codegen, argv: [npm, run, codegen, --silent], heavy: build}
|
|
87
|
+
processes:
|
|
88
|
+
- name: api
|
|
89
|
+
argv: [npx, ts-node-dev, --respawn, -r, tsconfig-paths/register, apps/api/src/main.ts]
|
|
90
|
+
cwd: be
|
|
91
|
+
port: api
|
|
92
|
+
files: [GOOGLE_CLIENT_SECRET, AUTH_JWT_PRIVATE_KEY, STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY]
|
|
93
|
+
env:
|
|
94
|
+
PORT: "${port.api}"
|
|
95
|
+
HTTP_SECURITY_ALLOWED_ORIGINS: "http://localhost:${port.web}"
|
|
96
|
+
PRIMARY_DB_SCHEMA: public
|
|
97
|
+
GOOGLE_CLIENT_ID: dev-local-client-id
|
|
98
|
+
GOOGLE_CALLBACK_URL: "http://localhost:${port.api}/auth/google/callback"
|
|
99
|
+
AUTH_JWT_ISSUER: "http://localhost:${port.api}"
|
|
100
|
+
AUTH_WEB_ORIGIN: "http://localhost:${port.web}"
|
|
101
|
+
AUTH_JWT_AUDIENCE: {{project}}
|
|
102
|
+
AUTH_JWT_KEY_ID: dev-1
|
|
103
|
+
MAIL_HOST: 127.0.0.1
|
|
104
|
+
MAIL_PORT: "${port.mail}"
|
|
105
|
+
MAIL_SECURITY: none
|
|
106
|
+
MAIL_FROM: no-reply@{{project}}.example.invalid
|
|
107
|
+
MAIL_FROM_NAME: {{project}}
|
|
108
|
+
STORAGE_ENDPOINT: "http://127.0.0.1:9000"
|
|
109
|
+
STORAGE_REGION: us-east-1
|
|
110
|
+
STORAGE_BUCKET: {{project}}-dev
|
|
111
|
+
health: {url: "http://localhost:${port.api}/health/live"}
|
|
112
|
+
- name: web
|
|
113
|
+
argv: [npm, run, dev:fe]
|
|
114
|
+
port: web
|
|
115
|
+
env:
|
|
116
|
+
NEXT_PUBLIC_SITE_URL: "http://localhost:${port.web}"
|
|
117
|
+
NEXT_PUBLIC_API_URL: "http://localhost:${port.api}"
|
|
118
|
+
NEXT_PUBLIC_GRAPHQL_URL: "http://localhost:${port.api}/graphql"
|
|
119
|
+
health: {url: "http://localhost:${port.web}/health/live"}
|
|
120
|
+
ready:
|
|
121
|
+
- {name: api-live, url: "http://localhost:${port.api}/health/live"}
|
|
122
|
+
- {name: api-ready, url: "http://localhost:${port.api}/health/ready"}
|
|
123
|
+
- {name: web-live, url: "http://localhost:${port.web}/health/live"}
|
|
124
|
+
verify:
|
|
125
|
+
- {name: api-live, url: "http://localhost:${port.api}/health/live"}
|
|
126
|
+
- {name: api-ready, url: "http://localhost:${port.api}/health/ready"}
|
|
127
|
+
- {name: web-live, url: "http://localhost:${port.web}/health/live"}
|
|
128
|
+
origins:
|
|
129
|
+
- {name: web, url: "http://localhost:${port.web}"}
|
|
130
|
+
- {name: api, url: "http://localhost:${port.api}"}
|
|
131
|
+
- {name: mail, url: "http://localhost:${port.mailui}"}
|
|
117
132
|
production:
|
|
118
133
|
status: supported
|
|
119
134
|
runtime: docker-swarm
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
# Development stack
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The dev environment is one command: `starci app stack up --env dev --execute`. It checks the machine, opens the dev custody, runs the setup
|
|
4
|
+
commands, starts the dependencies in Docker Linux containers (the `db` service: the official postgres image plus pgvector, built from
|
|
5
|
+
`.starcistacks/images/db/Dockerfile`; the `mail` service: Mailpit, the dev mail sink), gives the environment its own database role, migrates, runs the seed, starts the api and web
|
|
6
|
+
processes natively and waits for readiness. What it runs is the `local` block of `environments.dev` in `.starcistacks/application-stacks.yaml`
|
|
7
|
+
(PROJECT-STACKS-6 in contexts/project/stacks.md); this runbook never carries a docker command, an agent never runs docker by hand.
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
**A new machine.** Install what `starci app stack doctor --env dev` reports missing (it prints the exact winget, brew or apt step per tool; Node
|
|
10
|
+
from `.nvmrc`, npm, git, Docker with Compose v2 running Linux containers, WSL2 on Windows, sops and age), then run `up`. A fresh clone needs
|
|
11
|
+
nothing else: `up` runs `npm ci` and the codegen itself.
|
|
12
|
+
|
|
13
|
+
**Custody.** Dev values are sealed like any secret and generated once, dev-only: the owner arms `starci app secret init --env dev --execute`, which
|
|
14
|
+
creates every value `local.custody` declares, declares its key in the manifest and never replaces a value that is already sealed. A machine that
|
|
15
|
+
lacks the age identity of an existing dev member is refused with the owner step to obtain it. The `db` service takes `POSTGRES_PASSWORD`, the
|
|
16
|
+
migrations run as the environment's own owner role and the api connects as the `app_be` role with its own password, so no role is shared with
|
|
17
|
+
another environment.
|
|
7
18
|
|
|
8
19
|
| Command | Operation |
|
|
9
20
|
| --- | --- |
|
|
10
|
-
| prepare |
|
|
11
|
-
| doctor |
|
|
12
|
-
| up |
|
|
13
|
-
| status |
|
|
14
|
-
| logs |
|
|
15
|
-
| down |
|
|
16
|
-
| verification |
|
|
21
|
+
| prepare | `starci app stack prepare --env dev --execute`: checks the machine, opens the custody, runs the setup commands and builds the images. |
|
|
22
|
+
| doctor | `starci app stack doctor --env dev`: read-only table of host prerequisites with the owner-run install step per OS, custody, compose render and ports. |
|
|
23
|
+
| up | `starci app stack up --env dev --execute`: prepare, then the services, the database role, migrations with a receipt, the seed, the processes and readiness; prints the origins. |
|
|
24
|
+
| status | `starci app stack status --env dev`: desired against running, the migration receipt and every readiness probe. |
|
|
25
|
+
| logs | `starci app stack logs --env dev --service api` (a process or a compose service, bounded and redacted). |
|
|
26
|
+
| down | `starci app stack down --env dev --execute`: ends the processes and stops the compose project; volumes and sealed values stay. |
|
|
27
|
+
| verification | `starci app stack verify --env dev`: the declared liveness and readiness probes after a real start, then `npm run lint` from the app root. |
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
**Mail.** The confirmation and reset mails of the sign-in go to the `mail` service on its SMTP port (`MAIL_HOST`, `MAIL_PORT`, `MAIL_SECURITY: none`: a plain channel that only the loopback sink accepts) and nothing leaves the machine. Open the web UI at the `mail` origin `up` prints (`http://localhost:8025` by default), sign up in the web app and follow the link from the message there. A deployed environment names a real SMTP server and `MAIL_SECURITY: starttls` or `tls`.
|
|
19
30
|
|
|
20
|
-
The
|
|
31
|
+
The database volume `db-data` is rebuildable development data: `starci app db migrate` recreates its schema from `db/bootstrap.sql` and
|
|
32
|
+
`db/migrations`. Key names of the environment are in `.starcistacks/dev/runtime/env/KEYS.md`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# The dev environment's dependencies, run by `starci app stack up --env dev --execute` (PROJECT-STACKS-3, PROJECT-STACKS-6).
|
|
2
|
+
# Host ports come from environments.dev.local.ports through STACK_PORT_<NAME>; the superuser password is read from a file through the least
|
|
3
|
+
# grant STACK_GRANT_DB (a directory holding only POSTGRES_PASSWORD), never from the environment. Every port publishes on loopback only.
|
|
4
|
+
# `mail` is the dev mail sink (Mailpit, an unmodified upstream image pinned by digest in contexts/stack/pins.yaml): the api sends the
|
|
5
|
+
# confirmation and reset mails to its SMTP port, over a plain channel that exists on loopback only, and a person reads them in its web UI. The api and web run natively against these containers.
|
|
6
|
+
name: {{project}}-dev
|
|
7
|
+
services:
|
|
8
|
+
db:
|
|
9
|
+
build:
|
|
10
|
+
context: ../../../images/db
|
|
11
|
+
image: {{project}}-db:local
|
|
12
|
+
environment:
|
|
13
|
+
POSTGRES_PASSWORD_FILE: /run/secrets/POSTGRES_PASSWORD
|
|
14
|
+
ports:
|
|
15
|
+
- "127.0.0.1:${STACK_PORT_DB}:5432"
|
|
16
|
+
volumes:
|
|
17
|
+
- db-data:/var/lib/postgresql/data
|
|
18
|
+
- ${STACK_GRANT_DB}:/run/secrets:ro
|
|
19
|
+
healthcheck:
|
|
20
|
+
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
|
21
|
+
interval: 5s
|
|
22
|
+
timeout: 5s
|
|
23
|
+
retries: 30
|
|
24
|
+
start_period: 10s
|
|
25
|
+
restart: unless-stopped
|
|
26
|
+
mail:
|
|
27
|
+
image: axllent/mailpit:v1.31.6@sha256:23fe39e81bde596a918000b3135c0f85dc8bb4c9ea6be274b051143f3e300387
|
|
28
|
+
environment:
|
|
29
|
+
MP_SMTP_BIND_ADDR: 0.0.0.0:1025
|
|
30
|
+
MP_UI_BIND_ADDR: 0.0.0.0:8025
|
|
31
|
+
MP_MAX_MESSAGES: "500"
|
|
32
|
+
ports:
|
|
33
|
+
- "127.0.0.1:${STACK_PORT_MAIL}:1025"
|
|
34
|
+
- "127.0.0.1:${STACK_PORT_MAILUI}:8025"
|
|
35
|
+
healthcheck:
|
|
36
|
+
test: ["CMD", "/mailpit", "readyz"]
|
|
37
|
+
interval: 5s
|
|
38
|
+
timeout: 5s
|
|
39
|
+
retries: 20
|
|
40
|
+
start_period: 5s
|
|
41
|
+
restart: unless-stopped
|
|
42
|
+
volumes:
|
|
43
|
+
db-data: {}
|
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
# Environment key roster
|
|
1
|
+
# Environment key roster (dev)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Names only. Dev values are generated once by the owner-armed `starci app secret init --env dev --execute`, sealed in
|
|
4
|
+
`.starcistacks/dev/secrets/<member>.enc.yaml` and opened by `starci app stack up --env dev --execute` into private files outside the repository
|
|
5
|
+
(`<KEY>_FILE`, which the back end reads first). The public front-end configuration is supplied by the manifest's `local.processes` environment.
|
|
4
6
|
|
|
5
7
|
| Key | Consumer |
|
|
6
8
|
| --- | --- |
|
|
7
|
-
| `PORT` | API listener |
|
|
8
|
-
| `
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `GOOGLE_CLIENT_ID` | API Google OAuth client |
|
|
13
|
-
| `GOOGLE_CLIENT_SECRET` | API Google OAuth client
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `STORAGE_SECRET_ACCESS_KEY` | API storage credential; `.starcistacks/dev/secrets/storage-secret-access-key.enc` maps to external `{{project}}-dev-storage-secret-access-key-v1` and mounted `/run/secrets/storage-secret-access-key` |
|
|
25
|
-
| `DATABASE_URL` | `starci app db` verbs (migrate, dump) and `contract:emit`, from the operator's environment |
|
|
26
|
-
| `NEXT_PUBLIC_SITE_URL` | Front-end site URL |
|
|
27
|
-
| `NEXT_PUBLIC_API_URL` | Front-end auth doors of the API |
|
|
28
|
-
| `NEXT_PUBLIC_GRAPHQL_URL` | Front-end GraphQL transport |
|
|
29
|
-
|
|
30
|
-
Public front-end configuration is supplied at build time and stays outside sealed custody. The database verbs read `DATABASE_URL` from the operator's environment; the GitHub secret store carries only the age identity.
|
|
9
|
+
| `PORT`, `HTTP_SECURITY_ALLOWED_ORIGINS`, `PRIMARY_DB_SCHEMA` | API listener, origin allowlist, primary schema (manifest `local.processes.api.env`) |
|
|
10
|
+
| `PRIMARY_DB_URL` | API least-privilege connection as `app_be` with this environment's own password; composed by the stack verb, read through `PRIMARY_DB_URL_FILE` |
|
|
11
|
+
| `POSTGRES_PASSWORD` | The db service's superuser password (member `db`) |
|
|
12
|
+
| `DEV_DB_OWNER_PASSWORD` | The environment's own owner role, which runs the migrations (member `db`) |
|
|
13
|
+
| `DEV_DB_APP_PASSWORD` | The password of the `app_be` role in this environment's database (member `db`) |
|
|
14
|
+
| `GOOGLE_CLIENT_ID`, `GOOGLE_CALLBACK_URL` | API Google OAuth client and callback (local placeholders in the manifest) |
|
|
15
|
+
| `GOOGLE_CLIENT_SECRET` | API Google OAuth client secret (member `google`) |
|
|
16
|
+
| `AUTH_JWT_ISSUER`, `AUTH_JWT_AUDIENCE`, `AUTH_JWT_KEY_ID` | API access-token claims and key id (the key id is published in the JWKS) |
|
|
17
|
+
| `AUTH_WEB_ORIGIN` | API origin of the web app: where the Google callback lands and the only origin a return address may name |
|
|
18
|
+
| `AUTH_JWT_PRIVATE_KEY` | API Ed25519 (EdDSA) signing key (member `auth`) |
|
|
19
|
+
| `STORAGE_ENDPOINT`, `STORAGE_REGION`, `STORAGE_BUCKET` | API storage address |
|
|
20
|
+
| `STORAGE_ACCESS_KEY_ID`, `STORAGE_SECRET_ACCESS_KEY` | API storage credentials (member `storage`) |
|
|
21
|
+
| `MAIL_HOST`, `MAIL_PORT`, `MAIL_SECURITY`, `MAIL_FROM`, `MAIL_FROM_NAME` | API outgoing mail server (the dev `mail` service, Mailpit, on its SMTP port: a plain channel on loopback only) and the sender of the confirmation and reset mails |
|
|
22
|
+
| `MAIL_DEFAULT_LANGUAGE`, `MAIL_CONFIRM_TTL_SECONDS`, `MAIL_RESET_TTL_SECONDS` | API mail language (`vi` or `en`) and link lifetimes; optional |
|
|
23
|
+
| `MAIL_USER`, `MAIL_PASSWORD` | API mail server login; optional, the password reads `MAIL_PASSWORD_FILE` |
|
|
24
|
+
| `NEXT_PUBLIC_SITE_URL`, `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_GRAPHQL_URL` | Front-end site, auth doors and GraphQL transport |
|
|
25
|
+
| `DATABASE_URL` | `starci app db` verbs; the stack verb composes it in memory for the migration |
|
|
@@ -12,10 +12,16 @@ services:
|
|
|
12
12
|
GOOGLE_CLIENT_SECRET_FILE: /run/secrets/google-client-secret
|
|
13
13
|
GOOGLE_CALLBACK_URL: https://api.{{project}}.example.invalid/auth/google/callback
|
|
14
14
|
AUTH_JWT_ISSUER: https://api.{{project}}.example.invalid
|
|
15
|
+
AUTH_WEB_ORIGIN: https://{{project}}.example.invalid
|
|
15
16
|
AUTH_JWT_AUDIENCE: {{project}}
|
|
16
17
|
AUTH_JWT_KEY_ID: production-1
|
|
17
18
|
AUTH_COOKIE_DOMAIN: {{project}}.example.invalid
|
|
18
19
|
AUTH_JWT_PRIVATE_KEY_FILE: /run/secrets/auth-jwt-private-key
|
|
20
|
+
MAIL_HOST: smtp.{{project}}.example.invalid
|
|
21
|
+
MAIL_PORT: "587"
|
|
22
|
+
MAIL_SECURITY: starttls
|
|
23
|
+
MAIL_FROM: no-reply@{{project}}.example.invalid
|
|
24
|
+
MAIL_FROM_NAME: {{project}}
|
|
19
25
|
STORAGE_ENDPOINT: https://storage.{{project}}.example.invalid
|
|
20
26
|
STORAGE_REGION: us-east-1
|
|
21
27
|
STORAGE_BUCKET: {{project}}-production
|
|
@@ -13,15 +13,20 @@ This inventory declares names, consumers and mounted file paths. Scaffolding gen
|
|
|
13
13
|
| `GOOGLE_CLIENT_SECRET` | API Google OAuth client; reads `GOOGLE_CLIENT_SECRET_FILE` at `/run/secrets/google-client-secret` |
|
|
14
14
|
| `GOOGLE_CALLBACK_URL` | API Google OAuth callback door |
|
|
15
15
|
| `AUTH_JWT_ISSUER` | API access-token issuer |
|
|
16
|
+
| `AUTH_WEB_ORIGIN` | API origin of the web app: where the Google callback lands and the only origin a return address may name |
|
|
16
17
|
| `AUTH_JWT_AUDIENCE` | API access-token audience |
|
|
17
18
|
| `AUTH_JWT_KEY_ID` | API signing key id, published in the JWKS |
|
|
18
19
|
| `AUTH_COOKIE_DOMAIN` | API cookie `Domain` (a bare domain such as `example.com`, no scheme or port); optional while the web and the API share one host, required when they are served on different hosts, because a host-only refresh cookie never reaches the web server's forwarded `Cookie` header |
|
|
19
|
-
| `AUTH_JWT_PRIVATE_KEY` | API
|
|
20
|
+
| `AUTH_JWT_PRIVATE_KEY` | API Ed25519 (EdDSA) signing key; reads `AUTH_JWT_PRIVATE_KEY_FILE` at `/run/secrets/auth-jwt-private-key` |
|
|
20
21
|
| `STORAGE_ENDPOINT` | API S3-compatible storage endpoint |
|
|
21
22
|
| `STORAGE_REGION` | API storage signing region |
|
|
22
23
|
| `STORAGE_BUCKET` | API storage bucket |
|
|
23
24
|
| `STORAGE_ACCESS_KEY_ID` | API storage credential; reads `STORAGE_ACCESS_KEY_ID_FILE` at `/run/secrets/storage-access-key-id` |
|
|
24
25
|
| `STORAGE_SECRET_ACCESS_KEY` | API storage credential; reads `STORAGE_SECRET_ACCESS_KEY_FILE` at `/run/secrets/storage-secret-access-key` |
|
|
26
|
+
| `MAIL_HOST`, `MAIL_PORT`, `MAIL_SECURITY` | API outgoing mail server and how its channel is secured (`starttls` is required to succeed, `tls` is implicit TLS) |
|
|
27
|
+
| `MAIL_FROM`, `MAIL_FROM_NAME` | API sender address and name of the confirmation and reset mails |
|
|
28
|
+
| `MAIL_DEFAULT_LANGUAGE`, `MAIL_CONFIRM_TTL_SECONDS`, `MAIL_RESET_TTL_SECONDS` | API mail language (`vi` or `en`) and link lifetimes; optional |
|
|
29
|
+
| `MAIL_USER`, `MAIL_PASSWORD` | API mail server login; optional, the password reads `MAIL_PASSWORD_FILE` at `/run/secrets/mail-password` |
|
|
25
30
|
| `DATABASE_URL` | `starci app db` verbs (migrate, dump) and `contract:emit`, from the operator's environment |
|
|
26
31
|
| `NEXT_PUBLIC_SITE_URL` | Front-end site URL |
|
|
27
32
|
| `NEXT_PUBLIC_API_URL` | Front-end auth doors of the API |
|
|
@@ -24,25 +24,88 @@ begin
|
|
|
24
24
|
end;
|
|
25
25
|
$$;
|
|
26
26
|
|
|
27
|
-
-- The rotating refresh
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
|
|
27
|
+
-- The device sessions and rotating refresh tokens of @starci/auth. A token is stored by its sha256 only; a rotated token stays,
|
|
28
|
+
-- marked used, so a replay is detected (a replay inside the grace window is a racing tab, an older one ends the session).
|
|
29
|
+
-- identity_attempts counts the sign-in attempts of one address in a window every replica shares (the budget of failures).
|
|
30
|
+
-- The app owns auth.users (db/bootstrap.sql) and the role app_be.
|
|
31
|
+
|
|
32
|
+
create table public.identity_sessions (
|
|
33
|
+
id uuid primary key,
|
|
34
|
+
user_id uuid not null references auth.users (id) on delete cascade,
|
|
35
|
+
created_at timestamptz not null default now(),
|
|
36
|
+
refreshed_at timestamptz not null default now(),
|
|
37
|
+
user_agent text,
|
|
38
|
+
ip text
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
create index identity_sessions_user_idx on public.identity_sessions (user_id);
|
|
42
|
+
|
|
43
|
+
create table public.identity_refresh_tokens (
|
|
31
44
|
id uuid primary key default gen_random_uuid(),
|
|
32
|
-
|
|
45
|
+
session_id uuid not null references public.identity_sessions (id) on delete cascade,
|
|
46
|
+
user_id uuid not null,
|
|
47
|
+
token_hash text not null,
|
|
48
|
+
created_at timestamptz not null default now(),
|
|
49
|
+
expires_at timestamptz not null,
|
|
50
|
+
used_at timestamptz,
|
|
51
|
+
constraint identity_refresh_tokens_hash_key unique (token_hash)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
create index identity_refresh_tokens_session_idx on public.identity_refresh_tokens (session_id);
|
|
55
|
+
create index identity_refresh_tokens_expiry_idx on public.identity_refresh_tokens (user_id, expires_at);
|
|
56
|
+
|
|
57
|
+
create table public.identity_attempts (
|
|
58
|
+
key varchar(128) primary key,
|
|
59
|
+
hits integer not null,
|
|
60
|
+
reset_at timestamptz not null
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
create index identity_attempts_reset_idx on public.identity_attempts (reset_at);
|
|
64
|
+
|
|
65
|
+
alter table public.identity_sessions enable row level security;
|
|
66
|
+
alter table public.identity_sessions force row level security;
|
|
67
|
+
alter table public.identity_refresh_tokens enable row level security;
|
|
68
|
+
alter table public.identity_refresh_tokens force row level security;
|
|
69
|
+
alter table public.identity_attempts enable row level security;
|
|
70
|
+
alter table public.identity_attempts force row level security;
|
|
71
|
+
|
|
72
|
+
revoke all on table public.identity_sessions from public;
|
|
73
|
+
revoke all on table public.identity_refresh_tokens from public;
|
|
74
|
+
revoke all on table public.identity_attempts from public;
|
|
75
|
+
grant select, insert, update, delete on table public.identity_sessions to app_be;
|
|
76
|
+
grant select, insert, update, delete on table public.identity_refresh_tokens to app_be;
|
|
77
|
+
grant select, insert, update, delete on table public.identity_attempts to app_be;
|
|
78
|
+
|
|
79
|
+
create policy identity_sessions_app_be_all on public.identity_sessions
|
|
80
|
+
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
81
|
+
create policy identity_refresh_tokens_app_be_all on public.identity_refresh_tokens
|
|
82
|
+
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
83
|
+
create policy identity_attempts_app_be_all on public.identity_attempts
|
|
84
|
+
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
85
|
+
|
|
86
|
+
{{> app/auth-doors.sql.partial}}
|
|
87
|
+
|
|
88
|
+
-- The single-use links that confirm a mailbox or reset a password (the email doors of sign-in): only the sha256 of the token is
|
|
89
|
+
-- stored, a spent link stays marked used, and a new link of the same purpose ends the older ones.
|
|
90
|
+
create table public.identity_email_tokens (
|
|
91
|
+
id uuid primary key,
|
|
33
92
|
user_id uuid not null references auth.users (id) on delete cascade,
|
|
34
|
-
|
|
93
|
+
purpose varchar(16) not null check (purpose in ('confirm', 'reset')),
|
|
94
|
+
token_hash varchar(64) not null,
|
|
35
95
|
expires_at timestamptz not null,
|
|
36
|
-
|
|
37
|
-
created_at timestamptz not null
|
|
96
|
+
used_at timestamptz,
|
|
97
|
+
created_at timestamptz not null,
|
|
98
|
+
constraint identity_email_tokens_hash_key unique (token_hash)
|
|
38
99
|
);
|
|
39
100
|
|
|
40
|
-
create index
|
|
101
|
+
create index identity_email_tokens_user_idx on public.identity_email_tokens (user_id, purpose);
|
|
41
102
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
grant select, insert, update on table
|
|
103
|
+
alter table public.identity_email_tokens enable row level security;
|
|
104
|
+
alter table public.identity_email_tokens force row level security;
|
|
105
|
+
revoke all on table public.identity_email_tokens from public;
|
|
106
|
+
grant select, insert, update, delete on table public.identity_email_tokens to app_be;
|
|
107
|
+
create policy identity_email_tokens_app_be_all on public.identity_email_tokens
|
|
108
|
+
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
46
109
|
|
|
47
110
|
create table public.profiles (
|
|
48
111
|
id uuid primary key references auth.users (id) on delete cascade,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import type { MailLanguage } from "@modules/integrations/mail"
|
|
3
|
+
import type { HttpExchange } from "@modules/integrations/passport"
|
|
4
|
+
|
|
5
|
+
/** One cookie door of sign-in: the request and response the strategy or the refresh cookie work on. */
|
|
6
|
+
export interface ExchangeRequest {
|
|
7
|
+
/** The request and response. */
|
|
8
|
+
readonly exchange: HttpExchange
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** A request with nothing in it. */
|
|
12
|
+
export type EmptyRequest = Record<string, never>
|
|
13
|
+
|
|
14
|
+
/** The posted data of the signup door. */
|
|
15
|
+
export interface SignupData {
|
|
16
|
+
/** The sign-in address. */
|
|
17
|
+
readonly email: string
|
|
18
|
+
/** The chosen password. */
|
|
19
|
+
readonly password: string
|
|
20
|
+
/** The language of the confirmation mail, when the request names one. */
|
|
21
|
+
readonly language: MailLanguage | undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The posted data of the resend and forgot doors. */
|
|
25
|
+
export interface AddressData {
|
|
26
|
+
/** The address the mail goes to. */
|
|
27
|
+
readonly email: string
|
|
28
|
+
/** The language of the mail, when the request names one. */
|
|
29
|
+
readonly language: MailLanguage | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The link token of the confirm door. */
|
|
33
|
+
export interface TokenData {
|
|
34
|
+
/** The token the link carries. */
|
|
35
|
+
readonly token: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The posted data of the reset door. */
|
|
39
|
+
export interface ResetData {
|
|
40
|
+
/** The token the link carries. */
|
|
41
|
+
readonly token: string
|
|
42
|
+
/** The new password. */
|
|
43
|
+
readonly password: string
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { TokenData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Spends a confirmation link and confirms the mailbox. */
|
|
7
|
+
export class ConfirmCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<TokenData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ConfirmCommand } from "./confirm.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ConfirmCommand)
|
|
10
|
+
/** Spends a confirmation link and confirms the mailbox. */
|
|
11
|
+
export class ConfirmHandler extends ICQRSHandler<ConfirmCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ConfirmCommand): Promise<void> {
|
|
20
|
+
return this.email.confirm(command.params.request.token)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { AddressData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Mails a password reset link when the address has an account; the answer never says whether it does. */
|
|
7
|
+
export class ForgotCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<AddressData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ForgotCommand } from "./forgot.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ForgotCommand)
|
|
10
|
+
/** Mails a password reset link when the address has an account; the answer never says whether it does. */
|
|
11
|
+
export class ForgotHandler extends ICQRSHandler<ForgotCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ForgotCommand): Promise<void> {
|
|
20
|
+
return this.email.forgot(command.params.request.email, command.params.request.language)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { QueryHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentitySignInService } from "@modules/domain/identity"
|
|
4
|
+
import type { JwksDocument } from "@modules/integrations/passport"
|
|
5
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
6
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
7
|
+
import type { Logger } from "@modules/platform/logging"
|
|
8
|
+
import { GetJwksQuery } from "./get-jwks.query"
|
|
9
|
+
|
|
10
|
+
@QueryHandler(GetJwksQuery)
|
|
11
|
+
/** Answers the JWKS. */
|
|
12
|
+
export class GetJwksHandler extends ICQRSHandler<GetJwksQuery, JwksDocument> {
|
|
13
|
+
constructor(
|
|
14
|
+
@InjectLogger() logger: Logger,
|
|
15
|
+
private readonly signIn: IdentitySignInService,
|
|
16
|
+
) {
|
|
17
|
+
super(logger)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected override process(): Promise<JwksDocument> {
|
|
21
|
+
return this.signIn.jwks()
|
|
22
|
+
}
|
|
23
|
+
}
|