@portaidentity/server 1.7.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 +11 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/applications/cache.d.ts +64 -0
- package/dist/applications/cache.d.ts.map +1 -0
- package/dist/applications/cache.js +161 -0
- package/dist/applications/cache.js.map +1 -0
- package/dist/applications/errors.d.ts +25 -0
- package/dist/applications/errors.d.ts.map +1 -0
- package/dist/applications/errors.js +31 -0
- package/dist/applications/errors.js.map +1 -0
- package/dist/applications/index.d.ts +17 -0
- package/dist/applications/index.d.ts.map +1 -0
- package/dist/applications/index.js +19 -0
- package/dist/applications/index.js.map +1 -0
- package/dist/applications/repository.d.ts +184 -0
- package/dist/applications/repository.d.ts.map +1 -0
- package/dist/applications/repository.js +367 -0
- package/dist/applications/repository.js.map +1 -0
- package/dist/applications/service.d.ts +160 -0
- package/dist/applications/service.d.ts.map +1 -0
- package/dist/applications/service.js +388 -0
- package/dist/applications/service.js.map +1 -0
- package/dist/applications/slugs.d.ts +61 -0
- package/dist/applications/slugs.d.ts.map +1 -0
- package/dist/applications/slugs.js +115 -0
- package/dist/applications/slugs.js.map +1 -0
- package/dist/applications/types.d.ts +129 -0
- package/dist/applications/types.d.ts.map +1 -0
- package/dist/applications/types.js +62 -0
- package/dist/applications/types.js.map +1 -0
- package/dist/auth/csrf.d.ts +71 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +112 -0
- package/dist/auth/csrf.js.map +1 -0
- package/dist/auth/email-renderer.d.ts +37 -0
- package/dist/auth/email-renderer.d.ts.map +1 -0
- package/dist/auth/email-renderer.js +116 -0
- package/dist/auth/email-renderer.js.map +1 -0
- package/dist/auth/email-service.d.ts +154 -0
- package/dist/auth/email-service.d.ts.map +1 -0
- package/dist/auth/email-service.js +415 -0
- package/dist/auth/email-service.js.map +1 -0
- package/dist/auth/email-transport.d.ts +65 -0
- package/dist/auth/email-transport.d.ts.map +1 -0
- package/dist/auth/email-transport.js +82 -0
- package/dist/auth/email-transport.js.map +1 -0
- package/dist/auth/i18n.d.ts +81 -0
- package/dist/auth/i18n.d.ts.map +1 -0
- package/dist/auth/i18n.js +395 -0
- package/dist/auth/i18n.js.map +1 -0
- package/dist/auth/index.d.ts +28 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +31 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/interaction-authority.d.ts +39 -0
- package/dist/auth/interaction-authority.d.ts.map +1 -0
- package/dist/auth/interaction-authority.js +30 -0
- package/dist/auth/interaction-authority.js.map +1 -0
- package/dist/auth/magic-link-session.d.ts +81 -0
- package/dist/auth/magic-link-session.d.ts.map +1 -0
- package/dist/auth/magic-link-session.js +183 -0
- package/dist/auth/magic-link-session.js.map +1 -0
- package/dist/auth/rate-limiter.d.ts +143 -0
- package/dist/auth/rate-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limiter.js +220 -0
- package/dist/auth/rate-limiter.js.map +1 -0
- package/dist/auth/recovery-crypto.d.ts +34 -0
- package/dist/auth/recovery-crypto.d.ts.map +1 -0
- package/dist/auth/recovery-crypto.js +103 -0
- package/dist/auth/recovery-crypto.js.map +1 -0
- package/dist/auth/recovery-job-processor.d.ts +24 -0
- package/dist/auth/recovery-job-processor.d.ts.map +1 -0
- package/dist/auth/recovery-job-processor.js +123 -0
- package/dist/auth/recovery-job-processor.js.map +1 -0
- package/dist/auth/recovery-job-repository.d.ts +219 -0
- package/dist/auth/recovery-job-repository.d.ts.map +1 -0
- package/dist/auth/recovery-job-repository.js +340 -0
- package/dist/auth/recovery-job-repository.js.map +1 -0
- package/dist/auth/recovery-service.d.ts +27 -0
- package/dist/auth/recovery-service.d.ts.map +1 -0
- package/dist/auth/recovery-service.js +53 -0
- package/dist/auth/recovery-service.js.map +1 -0
- package/dist/auth/recovery-worker.d.ts +158 -0
- package/dist/auth/recovery-worker.d.ts.map +1 -0
- package/dist/auth/recovery-worker.js +306 -0
- package/dist/auth/recovery-worker.js.map +1 -0
- package/dist/auth/template-engine.d.ts +134 -0
- package/dist/auth/template-engine.d.ts.map +1 -0
- package/dist/auth/template-engine.js +225 -0
- package/dist/auth/template-engine.js.map +1 -0
- package/dist/auth/token-repository.d.ts +238 -0
- package/dist/auth/token-repository.d.ts.map +1 -0
- package/dist/auth/token-repository.js +475 -0
- package/dist/auth/token-repository.js.map +1 -0
- package/dist/auth/tokens.d.ts +45 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +51 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +55 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +90 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/commands/health.d.ts +20 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +80 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +349 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +19 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +88 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +19 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +71 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/user-2fa.d.ts +29 -0
- package/dist/cli/commands/user-2fa.d.ts.map +1 -0
- package/dist/cli/commands/user-2fa.js +113 -0
- package/dist/cli/commands/user-2fa.js.map +1 -0
- package/dist/cli/error-handler.d.ts +31 -0
- package/dist/cli/error-handler.d.ts.map +1 -0
- package/dist/cli/error-handler.js +65 -0
- package/dist/cli/error-handler.js.map +1 -0
- package/dist/cli/index.d.ts +48 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +94 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +124 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/prompt.d.ts +48 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +96 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/clients/cache.d.ts +63 -0
- package/dist/clients/cache.d.ts.map +1 -0
- package/dist/clients/cache.js +160 -0
- package/dist/clients/cache.js.map +1 -0
- package/dist/clients/crypto.d.ts +69 -0
- package/dist/clients/crypto.d.ts.map +1 -0
- package/dist/clients/crypto.js +83 -0
- package/dist/clients/crypto.js.map +1 -0
- package/dist/clients/errors.d.ts +27 -0
- package/dist/clients/errors.d.ts.map +1 -0
- package/dist/clients/errors.js +33 -0
- package/dist/clients/errors.js.map +1 -0
- package/dist/clients/index.d.ts +19 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +23 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/repository.d.ts +161 -0
- package/dist/clients/repository.d.ts.map +1 -0
- package/dist/clients/repository.js +318 -0
- package/dist/clients/repository.js.map +1 -0
- package/dist/clients/resolve-login-methods.d.ts +74 -0
- package/dist/clients/resolve-login-methods.d.ts.map +1 -0
- package/dist/clients/resolve-login-methods.js +68 -0
- package/dist/clients/resolve-login-methods.js.map +1 -0
- package/dist/clients/secret-repository.d.ts +109 -0
- package/dist/clients/secret-repository.d.ts.map +1 -0
- package/dist/clients/secret-repository.js +166 -0
- package/dist/clients/secret-repository.js.map +1 -0
- package/dist/clients/secret-service.d.ts +87 -0
- package/dist/clients/secret-service.d.ts.map +1 -0
- package/dist/clients/secret-service.js +204 -0
- package/dist/clients/secret-service.js.map +1 -0
- package/dist/clients/service.d.ts +158 -0
- package/dist/clients/service.d.ts.map +1 -0
- package/dist/clients/service.js +523 -0
- package/dist/clients/service.js.map +1 -0
- package/dist/clients/types.d.ts +214 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +77 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/clients/validators.d.ts +87 -0
- package/dist/clients/validators.d.ts.map +1 -0
- package/dist/clients/validators.js +164 -0
- package/dist/clients/validators.js.map +1 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +52 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +35 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +167 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/custom-claims/cache.d.ts +48 -0
- package/dist/custom-claims/cache.d.ts.map +1 -0
- package/dist/custom-claims/cache.js +122 -0
- package/dist/custom-claims/cache.js.map +1 -0
- package/dist/custom-claims/errors.d.ts +25 -0
- package/dist/custom-claims/errors.d.ts.map +1 -0
- package/dist/custom-claims/errors.js +31 -0
- package/dist/custom-claims/errors.js.map +1 -0
- package/dist/custom-claims/index.d.ts +14 -0
- package/dist/custom-claims/index.d.ts.map +1 -0
- package/dist/custom-claims/index.js +17 -0
- package/dist/custom-claims/index.js.map +1 -0
- package/dist/custom-claims/repository.d.ts +147 -0
- package/dist/custom-claims/repository.d.ts.map +1 -0
- package/dist/custom-claims/repository.js +338 -0
- package/dist/custom-claims/repository.js.map +1 -0
- package/dist/custom-claims/service.d.ts +129 -0
- package/dist/custom-claims/service.d.ts.map +1 -0
- package/dist/custom-claims/service.js +296 -0
- package/dist/custom-claims/service.js.map +1 -0
- package/dist/custom-claims/types.d.ts +132 -0
- package/dist/custom-claims/types.d.ts.map +1 -0
- package/dist/custom-claims/types.js +61 -0
- package/dist/custom-claims/types.js.map +1 -0
- package/dist/custom-claims/validators.d.ts +61 -0
- package/dist/custom-claims/validators.d.ts.map +1 -0
- package/dist/custom-claims/validators.js +136 -0
- package/dist/custom-claims/validators.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/admin-permissions.d.ts +147 -0
- package/dist/lib/admin-permissions.d.ts.map +1 -0
- package/dist/lib/admin-permissions.js +261 -0
- package/dist/lib/admin-permissions.js.map +1 -0
- package/dist/lib/audit-log.d.ts +45 -0
- package/dist/lib/audit-log.d.ts.map +1 -0
- package/dist/lib/audit-log.js +79 -0
- package/dist/lib/audit-log.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +47 -0
- package/dist/lib/branding-assets.d.ts.map +1 -0
- package/dist/lib/branding-assets.js +93 -0
- package/dist/lib/branding-assets.js.map +1 -0
- package/dist/lib/bulk-operations.d.ts +54 -0
- package/dist/lib/bulk-operations.d.ts.map +1 -0
- package/dist/lib/bulk-operations.js +201 -0
- package/dist/lib/bulk-operations.js.map +1 -0
- package/dist/lib/cursor.d.ts +110 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +115 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/data-export.d.ts +67 -0
- package/dist/lib/data-export.d.ts.map +1 -0
- package/dist/lib/data-export.js +272 -0
- package/dist/lib/data-export.js.map +1 -0
- package/dist/lib/data-import-plan.d.ts +50 -0
- package/dist/lib/data-import-plan.d.ts.map +1 -0
- package/dist/lib/data-import-plan.js +235 -0
- package/dist/lib/data-import-plan.js.map +1 -0
- package/dist/lib/data-import.d.ts +276 -0
- package/dist/lib/data-import.d.ts.map +1 -0
- package/dist/lib/data-import.js +1694 -0
- package/dist/lib/data-import.js.map +1 -0
- package/dist/lib/database.d.ts +22 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +103 -0
- package/dist/lib/database.js.map +1 -0
- package/dist/lib/entity-history.d.ts +52 -0
- package/dist/lib/entity-history.d.ts.map +1 -0
- package/dist/lib/entity-history.js +121 -0
- package/dist/lib/entity-history.js.map +1 -0
- package/dist/lib/etag.d.ts +92 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +155 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/image-validator.d.ts +44 -0
- package/dist/lib/image-validator.d.ts.map +1 -0
- package/dist/lib/image-validator.js +204 -0
- package/dist/lib/image-validator.js.map +1 -0
- package/dist/lib/logger.d.ts +15 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +84 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/migrator.d.ts +14 -0
- package/dist/lib/migrator.d.ts.map +1 -0
- package/dist/lib/migrator.js +34 -0
- package/dist/lib/migrator.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +29 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +38 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +50 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/session-tracking.d.ts +81 -0
- package/dist/lib/session-tracking.d.ts.map +1 -0
- package/dist/lib/session-tracking.js +145 -0
- package/dist/lib/session-tracking.js.map +1 -0
- package/dist/lib/signing-key-crypto.d.ts +53 -0
- package/dist/lib/signing-key-crypto.d.ts.map +1 -0
- package/dist/lib/signing-key-crypto.js +108 -0
- package/dist/lib/signing-key-crypto.js.map +1 -0
- package/dist/lib/signing-keys.d.ts +141 -0
- package/dist/lib/signing-keys.d.ts.map +1 -0
- package/dist/lib/signing-keys.js +206 -0
- package/dist/lib/signing-keys.js.map +1 -0
- package/dist/lib/stats.d.ts +83 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/stats.js +187 -0
- package/dist/lib/stats.js.map +1 -0
- package/dist/lib/super-admin-protection.d.ts +69 -0
- package/dist/lib/super-admin-protection.d.ts.map +1 -0
- package/dist/lib/super-admin-protection.js +102 -0
- package/dist/lib/super-admin-protection.js.map +1 -0
- package/dist/lib/system-config.d.ts +79 -0
- package/dist/lib/system-config.d.ts.map +1 -0
- package/dist/lib/system-config.js +159 -0
- package/dist/lib/system-config.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +100 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +241 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/admin-cors.d.ts +41 -0
- package/dist/middleware/admin-cors.d.ts.map +1 -0
- package/dist/middleware/admin-cors.js +83 -0
- package/dist/middleware/admin-cors.js.map +1 -0
- package/dist/middleware/admin-mutation-audit.d.ts +9 -0
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
- package/dist/middleware/admin-mutation-audit.js +76 -0
- package/dist/middleware/admin-mutation-audit.js.map +1 -0
- package/dist/middleware/admin-rate-limiter.d.ts +56 -0
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/admin-rate-limiter.js +97 -0
- package/dist/middleware/admin-rate-limiter.js.map +1 -0
- package/dist/middleware/client-secret-hash.d.ts +34 -0
- package/dist/middleware/client-secret-hash.d.ts.map +1 -0
- package/dist/middleware/client-secret-hash.js +74 -0
- package/dist/middleware/client-secret-hash.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +3 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +93 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/health.d.ts +3 -0
- package/dist/middleware/health.d.ts.map +1 -0
- package/dist/middleware/health.js +35 -0
- package/dist/middleware/health.js.map +1 -0
- package/dist/middleware/metrics.d.ts +40 -0
- package/dist/middleware/metrics.d.ts.map +1 -0
- package/dist/middleware/metrics.js +75 -0
- package/dist/middleware/metrics.js.map +1 -0
- package/dist/middleware/oidc-client-tenant.d.ts +37 -0
- package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
- package/dist/middleware/oidc-client-tenant.js +156 -0
- package/dist/middleware/oidc-client-tenant.js.map +1 -0
- package/dist/middleware/oidc-cors.d.ts +33 -0
- package/dist/middleware/oidc-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-cors.js +68 -0
- package/dist/middleware/oidc-cors.js.map +1 -0
- package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
- package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-preflight-cors.js +267 -0
- package/dist/middleware/oidc-preflight-cors.js.map +1 -0
- package/dist/middleware/prompt-login-reset.d.ts +103 -0
- package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
- package/dist/middleware/prompt-login-reset.js +204 -0
- package/dist/middleware/prompt-login-reset.js.map +1 -0
- package/dist/middleware/ready.d.ts +23 -0
- package/dist/middleware/ready.d.ts.map +1 -0
- package/dist/middleware/ready.js +60 -0
- package/dist/middleware/ready.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +27 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +46 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/require-permission.d.ts +28 -0
- package/dist/middleware/require-permission.d.ts.map +1 -0
- package/dist/middleware/require-permission.js +63 -0
- package/dist/middleware/require-permission.js.map +1 -0
- package/dist/middleware/require-user-organization.d.ts +18 -0
- package/dist/middleware/require-user-organization.d.ts.map +1 -0
- package/dist/middleware/require-user-organization.js +53 -0
- package/dist/middleware/require-user-organization.js.map +1 -0
- package/dist/middleware/root-page.d.ts +62 -0
- package/dist/middleware/root-page.d.ts.map +1 -0
- package/dist/middleware/root-page.js +134 -0
- package/dist/middleware/root-page.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +98 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +159 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/middleware/tenant-resolver.d.ts +37 -0
- package/dist/middleware/tenant-resolver.d.ts.map +1 -0
- package/dist/middleware/tenant-resolver.js +71 -0
- package/dist/middleware/tenant-resolver.js.map +1 -0
- package/dist/middleware/token-rate-limiter.d.ts +89 -0
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/token-rate-limiter.js +189 -0
- package/dist/middleware/token-rate-limiter.js.map +1 -0
- package/dist/oidc/account-finder.d.ts +52 -0
- package/dist/oidc/account-finder.d.ts.map +1 -0
- package/dist/oidc/account-finder.js +134 -0
- package/dist/oidc/account-finder.js.map +1 -0
- package/dist/oidc/adapter-factory.d.ts +90 -0
- package/dist/oidc/adapter-factory.d.ts.map +1 -0
- package/dist/oidc/adapter-factory.js +165 -0
- package/dist/oidc/adapter-factory.js.map +1 -0
- package/dist/oidc/configuration.d.ts +155 -0
- package/dist/oidc/configuration.d.ts.map +1 -0
- package/dist/oidc/configuration.js +531 -0
- package/dist/oidc/configuration.js.map +1 -0
- package/dist/oidc/postgres-adapter.d.ts +159 -0
- package/dist/oidc/postgres-adapter.d.ts.map +1 -0
- package/dist/oidc/postgres-adapter.js +222 -0
- package/dist/oidc/postgres-adapter.js.map +1 -0
- package/dist/oidc/protocol-security-observer.d.ts +38 -0
- package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
- package/dist/oidc/protocol-security-observer.js +93 -0
- package/dist/oidc/protocol-security-observer.js.map +1 -0
- package/dist/oidc/provider.d.ts +37 -0
- package/dist/oidc/provider.d.ts.map +1 -0
- package/dist/oidc/provider.js +71 -0
- package/dist/oidc/provider.js.map +1 -0
- package/dist/oidc/redis-adapter.d.ts +140 -0
- package/dist/oidc/redis-adapter.d.ts.map +1 -0
- package/dist/oidc/redis-adapter.js +344 -0
- package/dist/oidc/redis-adapter.js.map +1 -0
- package/dist/organizations/cache.d.ts +60 -0
- package/dist/organizations/cache.d.ts.map +1 -0
- package/dist/organizations/cache.js +157 -0
- package/dist/organizations/cache.js.map +1 -0
- package/dist/organizations/errors.d.ts +25 -0
- package/dist/organizations/errors.d.ts.map +1 -0
- package/dist/organizations/errors.js +31 -0
- package/dist/organizations/errors.js.map +1 -0
- package/dist/organizations/index.d.ts +15 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +15 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/repository.d.ts +183 -0
- package/dist/organizations/repository.d.ts.map +1 -0
- package/dist/organizations/repository.js +354 -0
- package/dist/organizations/repository.js.map +1 -0
- package/dist/organizations/service.d.ts +168 -0
- package/dist/organizations/service.d.ts.map +1 -0
- package/dist/organizations/service.js +511 -0
- package/dist/organizations/service.js.map +1 -0
- package/dist/organizations/slugs.d.ts +57 -0
- package/dist/organizations/slugs.d.ts.map +1 -0
- package/dist/organizations/slugs.js +130 -0
- package/dist/organizations/slugs.js.map +1 -0
- package/dist/organizations/types.d.ts +146 -0
- package/dist/organizations/types.d.ts.map +1 -0
- package/dist/organizations/types.js +49 -0
- package/dist/organizations/types.js.map +1 -0
- package/dist/rbac/cache.d.ts +110 -0
- package/dist/rbac/cache.d.ts.map +1 -0
- package/dist/rbac/cache.js +271 -0
- package/dist/rbac/cache.js.map +1 -0
- package/dist/rbac/errors.d.ts +34 -0
- package/dist/rbac/errors.d.ts.map +1 -0
- package/dist/rbac/errors.js +43 -0
- package/dist/rbac/errors.js.map +1 -0
- package/dist/rbac/index.d.ts +17 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +21 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/mapping-repository.d.ts +121 -0
- package/dist/rbac/mapping-repository.d.ts.map +1 -0
- package/dist/rbac/mapping-repository.js +224 -0
- package/dist/rbac/mapping-repository.js.map +1 -0
- package/dist/rbac/permission-repository.d.ts +100 -0
- package/dist/rbac/permission-repository.d.ts.map +1 -0
- package/dist/rbac/permission-repository.js +190 -0
- package/dist/rbac/permission-repository.js.map +1 -0
- package/dist/rbac/permission-service.d.ts +85 -0
- package/dist/rbac/permission-service.d.ts.map +1 -0
- package/dist/rbac/permission-service.js +179 -0
- package/dist/rbac/permission-service.js.map +1 -0
- package/dist/rbac/role-repository.d.ts +104 -0
- package/dist/rbac/role-repository.d.ts.map +1 -0
- package/dist/rbac/role-repository.js +202 -0
- package/dist/rbac/role-repository.js.map +1 -0
- package/dist/rbac/role-service.d.ts +111 -0
- package/dist/rbac/role-service.d.ts.map +1 -0
- package/dist/rbac/role-service.js +256 -0
- package/dist/rbac/role-service.js.map +1 -0
- package/dist/rbac/slugs.d.ts +116 -0
- package/dist/rbac/slugs.d.ts.map +1 -0
- package/dist/rbac/slugs.js +178 -0
- package/dist/rbac/slugs.js.map +1 -0
- package/dist/rbac/types.d.ts +187 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +75 -0
- package/dist/rbac/types.js.map +1 -0
- package/dist/rbac/user-role-service.d.ts +101 -0
- package/dist/rbac/user-role-service.d.ts.map +1 -0
- package/dist/rbac/user-role-service.js +166 -0
- package/dist/rbac/user-role-service.js.map +1 -0
- package/dist/routes/applications.d.ts +40 -0
- package/dist/routes/applications.d.ts.map +1 -0
- package/dist/routes/applications.js +284 -0
- package/dist/routes/applications.js.map +1 -0
- package/dist/routes/audit.d.ts +31 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +140 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/branding.d.ts +17 -0
- package/dist/routes/branding.d.ts.map +1 -0
- package/dist/routes/branding.js +104 -0
- package/dist/routes/branding.js.map +1 -0
- package/dist/routes/bulk.d.ts +37 -0
- package/dist/routes/bulk.d.ts.map +1 -0
- package/dist/routes/bulk.js +94 -0
- package/dist/routes/bulk.js.map +1 -0
- package/dist/routes/clients.d.ts +44 -0
- package/dist/routes/clients.d.ts.map +1 -0
- package/dist/routes/clients.js +353 -0
- package/dist/routes/clients.js.map +1 -0
- package/dist/routes/config.d.ts +35 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +115 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/custom-claims.d.ts +38 -0
- package/dist/routes/custom-claims.d.ts.map +1 -0
- package/dist/routes/custom-claims.js +208 -0
- package/dist/routes/custom-claims.js.map +1 -0
- package/dist/routes/exports.d.ts +21 -0
- package/dist/routes/exports.d.ts.map +1 -0
- package/dist/routes/exports.js +98 -0
- package/dist/routes/exports.js.map +1 -0
- package/dist/routes/imports.d.ts +18 -0
- package/dist/routes/imports.d.ts.map +1 -0
- package/dist/routes/imports.js +72 -0
- package/dist/routes/imports.js.map +1 -0
- package/dist/routes/interactions.d.ts +111 -0
- package/dist/routes/interactions.d.ts.map +1 -0
- package/dist/routes/interactions.js +1074 -0
- package/dist/routes/interactions.js.map +1 -0
- package/dist/routes/invitation.d.ts +31 -0
- package/dist/routes/invitation.d.ts.map +1 -0
- package/dist/routes/invitation.js +342 -0
- package/dist/routes/invitation.js.map +1 -0
- package/dist/routes/keys.d.ts +28 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +91 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/magic-link.d.ts +43 -0
- package/dist/routes/magic-link.d.ts.map +1 -0
- package/dist/routes/magic-link.js +264 -0
- package/dist/routes/magic-link.js.map +1 -0
- package/dist/routes/organizations.d.ts +38 -0
- package/dist/routes/organizations.d.ts.map +1 -0
- package/dist/routes/organizations.js +347 -0
- package/dist/routes/organizations.js.map +1 -0
- package/dist/routes/password-reset.d.ts +65 -0
- package/dist/routes/password-reset.d.ts.map +1 -0
- package/dist/routes/password-reset.js +493 -0
- package/dist/routes/password-reset.js.map +1 -0
- package/dist/routes/permissions.d.ts +33 -0
- package/dist/routes/permissions.d.ts.map +1 -0
- package/dist/routes/permissions.js +166 -0
- package/dist/routes/permissions.js.map +1 -0
- package/dist/routes/roles.d.ts +37 -0
- package/dist/routes/roles.d.ts.map +1 -0
- package/dist/routes/roles.js +222 -0
- package/dist/routes/roles.js.map +1 -0
- package/dist/routes/sessions.d.ts +37 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +121 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/stats.d.ts +28 -0
- package/dist/routes/stats.d.ts.map +1 -0
- package/dist/routes/stats.js +65 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/two-factor-admin.d.ts +54 -0
- package/dist/routes/two-factor-admin.d.ts.map +1 -0
- package/dist/routes/two-factor-admin.js +344 -0
- package/dist/routes/two-factor-admin.js.map +1 -0
- package/dist/routes/two-factor.d.ts +31 -0
- package/dist/routes/two-factor.d.ts.map +1 -0
- package/dist/routes/two-factor.js +595 -0
- package/dist/routes/two-factor.js.map +1 -0
- package/dist/routes/user-roles.d.ts +33 -0
- package/dist/routes/user-roles.d.ts.map +1 -0
- package/dist/routes/user-roles.js +129 -0
- package/dist/routes/user-roles.js.map +1 -0
- package/dist/routes/users.d.ts +61 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +868 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/security/decision-context.d.ts +52 -0
- package/dist/security/decision-context.d.ts.map +1 -0
- package/dist/security/decision-context.js +150 -0
- package/dist/security/decision-context.js.map +1 -0
- package/dist/security/decision-event.d.ts +128 -0
- package/dist/security/decision-event.d.ts.map +1 -0
- package/dist/security/decision-event.js +225 -0
- package/dist/security/decision-event.js.map +1 -0
- package/dist/security/transport-decision.d.ts +6 -0
- package/dist/security/transport-decision.d.ts.map +1 -0
- package/dist/security/transport-decision.js +37 -0
- package/dist/security/transport-decision.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +491 -0
- package/dist/server.js.map +1 -0
- package/dist/two-factor/cache.d.ts +47 -0
- package/dist/two-factor/cache.d.ts.map +1 -0
- package/dist/two-factor/cache.js +122 -0
- package/dist/two-factor/cache.js.map +1 -0
- package/dist/two-factor/crypto.d.ts +41 -0
- package/dist/two-factor/crypto.d.ts.map +1 -0
- package/dist/two-factor/crypto.js +94 -0
- package/dist/two-factor/crypto.js.map +1 -0
- package/dist/two-factor/errors.d.ts +82 -0
- package/dist/two-factor/errors.d.ts.map +1 -0
- package/dist/two-factor/errors.js +112 -0
- package/dist/two-factor/errors.js.map +1 -0
- package/dist/two-factor/index.d.ts +18 -0
- package/dist/two-factor/index.d.ts.map +1 -0
- package/dist/two-factor/index.js +25 -0
- package/dist/two-factor/index.js.map +1 -0
- package/dist/two-factor/otp.d.ts +43 -0
- package/dist/two-factor/otp.d.ts.map +1 -0
- package/dist/two-factor/otp.js +57 -0
- package/dist/two-factor/otp.js.map +1 -0
- package/dist/two-factor/recovery.d.ts +45 -0
- package/dist/two-factor/recovery.d.ts.map +1 -0
- package/dist/two-factor/recovery.js +104 -0
- package/dist/two-factor/recovery.js.map +1 -0
- package/dist/two-factor/repository.d.ts +156 -0
- package/dist/two-factor/repository.d.ts.map +1 -0
- package/dist/two-factor/repository.js +251 -0
- package/dist/two-factor/repository.js.map +1 -0
- package/dist/two-factor/service.d.ts +232 -0
- package/dist/two-factor/service.d.ts.map +1 -0
- package/dist/two-factor/service.js +599 -0
- package/dist/two-factor/service.js.map +1 -0
- package/dist/two-factor/totp.d.ts +57 -0
- package/dist/two-factor/totp.d.ts.map +1 -0
- package/dist/two-factor/totp.js +96 -0
- package/dist/two-factor/totp.js.map +1 -0
- package/dist/two-factor/types.d.ts +152 -0
- package/dist/two-factor/types.d.ts.map +1 -0
- package/dist/two-factor/types.js +64 -0
- package/dist/two-factor/types.js.map +1 -0
- package/dist/users/cache.d.ts +49 -0
- package/dist/users/cache.d.ts.map +1 -0
- package/dist/users/cache.js +140 -0
- package/dist/users/cache.js.map +1 -0
- package/dist/users/claims.d.ts +67 -0
- package/dist/users/claims.d.ts.map +1 -0
- package/dist/users/claims.js +104 -0
- package/dist/users/claims.js.map +1 -0
- package/dist/users/errors.d.ts +25 -0
- package/dist/users/errors.d.ts.map +1 -0
- package/dist/users/errors.js +31 -0
- package/dist/users/errors.js.map +1 -0
- package/dist/users/gdpr.d.ts +110 -0
- package/dist/users/gdpr.d.ts.map +1 -0
- package/dist/users/gdpr.js +211 -0
- package/dist/users/gdpr.js.map +1 -0
- package/dist/users/index.d.ts +20 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +19 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/password.d.ts +85 -0
- package/dist/users/password.d.ts.map +1 -0
- package/dist/users/password.js +138 -0
- package/dist/users/password.js.map +1 -0
- package/dist/users/repository.d.ts +263 -0
- package/dist/users/repository.d.ts.map +1 -0
- package/dist/users/repository.js +499 -0
- package/dist/users/repository.js.map +1 -0
- package/dist/users/service.d.ts +299 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +698 -0
- package/dist/users/service.js.map +1 -0
- package/dist/users/types.d.ts +220 -0
- package/dist/users/types.d.ts.map +1 -0
- package/dist/users/types.js +77 -0
- package/dist/users/types.js.map +1 -0
- package/locales/default/en/common.json +10 -0
- package/locales/default/en/consent.json +13 -0
- package/locales/default/en/emails.json +17 -0
- package/locales/default/en/errors.json +21 -0
- package/locales/default/en/forgot-password.json +11 -0
- package/locales/default/en/invitation.json +18 -0
- package/locales/default/en/login.json +17 -0
- package/locales/default/en/logout.json +9 -0
- package/locales/default/en/magic-link.json +12 -0
- package/locales/default/en/reset-password.json +16 -0
- package/locales/default/en/two-factor.json +29 -0
- package/migrations/.gitkeep +0 -0
- package/migrations/001_extensions.sql +22 -0
- package/migrations/002_organizations.sql +43 -0
- package/migrations/003_applications.sql +50 -0
- package/migrations/004_clients.sql +74 -0
- package/migrations/005_users.sql +119 -0
- package/migrations/006_roles_permissions.sql +80 -0
- package/migrations/007_custom_claims.sql +55 -0
- package/migrations/008_config.sql +51 -0
- package/migrations/009_audit_log.sql +31 -0
- package/migrations/010_oidc_adapter.sql +34 -0
- package/migrations/011_seed.sql +53 -0
- package/migrations/012_two_factor.sql +88 -0
- package/migrations/013_client_secret_sha256.sql +22 -0
- package/migrations/014_login_methods.sql +34 -0
- package/migrations/015_signing_key_encryption.sql +20 -0
- package/migrations/016_failed_login_count.sql +27 -0
- package/migrations/017_audit_retention.sql +11 -0
- package/migrations/018_admin_api_enhancements.sql +78 -0
- package/migrations/019_invitation_details.sql +27 -0
- package/migrations/020_two_factor_admin_permission.sql +108 -0
- package/migrations/021_auth_recovery_jobs.sql +74 -0
- package/migrations/022_recovery_artifact_ownership.sql +25 -0
- package/migrations/023_magic_link_authority.sql +39 -0
- package/package.json +106 -0
- package/templates/default/emails/invitation.hbs +39 -0
- package/templates/default/emails/invitation.txt.hbs +18 -0
- package/templates/default/emails/magic-link.hbs +30 -0
- package/templates/default/emails/magic-link.txt.hbs +11 -0
- package/templates/default/emails/otp-code.hbs +31 -0
- package/templates/default/emails/otp-code.txt.hbs +15 -0
- package/templates/default/emails/password-changed.hbs +24 -0
- package/templates/default/emails/password-changed.txt.hbs +7 -0
- package/templates/default/emails/password-reset.hbs +30 -0
- package/templates/default/emails/password-reset.txt.hbs +11 -0
- package/templates/default/emails/welcome.hbs +25 -0
- package/templates/default/emails/welcome.txt.hbs +9 -0
- package/templates/default/layouts/main.hbs +49 -0
- package/templates/default/pages/accept-invite.hbs +25 -0
- package/templates/default/pages/consent.hbs +27 -0
- package/templates/default/pages/error.hbs +15 -0
- package/templates/default/pages/forgot-password.hbs +21 -0
- package/templates/default/pages/invite-expired.hbs +6 -0
- package/templates/default/pages/invite-success.hbs +9 -0
- package/templates/default/pages/login.hbs +108 -0
- package/templates/default/pages/logout-success.hbs +5 -0
- package/templates/default/pages/logout.hbs +24 -0
- package/templates/default/pages/magic-link-sent.hbs +10 -0
- package/templates/default/pages/magic-link-success.hbs +17 -0
- package/templates/default/pages/reset-password.hbs +20 -0
- package/templates/default/pages/reset-success.hbs +9 -0
- package/templates/default/pages/two-factor-setup.hbs +65 -0
- package/templates/default/pages/two-factor-verify.hbs +77 -0
- package/templates/default/partials/flash-messages.hbs +10 -0
- package/templates/default/partials/footer.hbs +4 -0
- package/templates/default/partials/header.hbs +8 -0
package/dist/lib/etag.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETag generation and optimistic concurrency control utility.
|
|
3
|
+
*
|
|
4
|
+
* Provides ETag generation from entity metadata and If-Match header
|
|
5
|
+
* validation for optimistic concurrency on PUT/PATCH endpoints.
|
|
6
|
+
*
|
|
7
|
+
* ETags use MD5 hashing of entity type + ID + updatedAt timestamp.
|
|
8
|
+
* MD5 is used for speed, not security — ETags are not secrets, they
|
|
9
|
+
* are cache/concurrency markers. Weak ETags (W/"...") are used since
|
|
10
|
+
* the comparison is semantic equivalence, not byte-identical content.
|
|
11
|
+
*
|
|
12
|
+
* Integration pattern:
|
|
13
|
+
* GET /:id → setETagHeader(ctx, 'entity', id, updatedAt)
|
|
14
|
+
* PUT /:id → checkIfMatch(ctx, 'entity', id, current.updatedAt, current)
|
|
15
|
+
*
|
|
16
|
+
* @module lib/etag
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// ETag Generation
|
|
21
|
+
// ============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Generate a weak ETag from entity type, ID, and updatedAt timestamp.
|
|
24
|
+
*
|
|
25
|
+
* The ETag is a deterministic MD5 hash of the concatenation:
|
|
26
|
+
* `${entityType}:${id}:${updatedAt.toISOString()}`
|
|
27
|
+
*
|
|
28
|
+
* Format: `W/"<first 16 hex chars of MD5>"` (weak ETag, 64-bit precision)
|
|
29
|
+
*
|
|
30
|
+
* @param entityType - Entity type string (e.g., 'organization', 'user')
|
|
31
|
+
* @param id - Entity UUID
|
|
32
|
+
* @param updatedAt - Entity's last modification timestamp
|
|
33
|
+
* @returns Weak ETag string (e.g., `W/"a1b2c3d4e5f67890"`)
|
|
34
|
+
*/
|
|
35
|
+
export function generateETag(entityType, id, updatedAt) {
|
|
36
|
+
const input = `${entityType}:${id}:${updatedAt.toISOString()}`;
|
|
37
|
+
const hash = createHash('md5').update(input).digest('hex').substring(0, 16);
|
|
38
|
+
return `W/"${hash}"`;
|
|
39
|
+
}
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// ETag Comparison
|
|
42
|
+
// ============================================================================
|
|
43
|
+
/**
|
|
44
|
+
* Compare an If-Match header value against the current ETag.
|
|
45
|
+
*
|
|
46
|
+
* Returns true if the ETags match (safe to proceed with the update).
|
|
47
|
+
* Handles:
|
|
48
|
+
* - Quoted strings: `"abc"` matches `W/"abc"` and vice versa
|
|
49
|
+
* - Weak prefix: `W/"abc"` is treated equivalently to `"abc"` for weak comparison
|
|
50
|
+
* - Wildcard: `*` always matches
|
|
51
|
+
* - Multiple ETags: `"abc", "def"` matches if any one matches
|
|
52
|
+
*
|
|
53
|
+
* @param ifMatchHeader - The value of the If-Match HTTP header
|
|
54
|
+
* @param currentETag - The current entity's ETag
|
|
55
|
+
* @returns true if they match
|
|
56
|
+
*/
|
|
57
|
+
export function matchesETag(ifMatchHeader, currentETag) {
|
|
58
|
+
const trimmed = ifMatchHeader.trim();
|
|
59
|
+
// Wildcard matches everything
|
|
60
|
+
if (trimmed === '*') {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// Extract the hash portion from the current ETag for comparison
|
|
64
|
+
const currentHash = extractHash(currentETag);
|
|
65
|
+
if (!currentHash) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Split on comma for multiple ETags: If-Match: "a", "b", "c"
|
|
69
|
+
const candidates = trimmed.split(',').map((s) => s.trim());
|
|
70
|
+
return candidates.some((candidate) => {
|
|
71
|
+
const candidateHash = extractHash(candidate);
|
|
72
|
+
return candidateHash !== null && candidateHash === currentHash;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract the raw hash string from an ETag value.
|
|
77
|
+
*
|
|
78
|
+
* Strips the W/ prefix and surrounding quotes:
|
|
79
|
+
* `W/"abc123"` → `abc123`
|
|
80
|
+
* `"abc123"` → `abc123`
|
|
81
|
+
* `abc123` → `abc123`
|
|
82
|
+
*
|
|
83
|
+
* @param etag - ETag value (possibly with W/ prefix and quotes)
|
|
84
|
+
* @returns The raw hash string, or null if empty/invalid
|
|
85
|
+
*/
|
|
86
|
+
function extractHash(etag) {
|
|
87
|
+
let value = etag.trim();
|
|
88
|
+
// Remove weak prefix
|
|
89
|
+
if (value.startsWith('W/')) {
|
|
90
|
+
value = value.substring(2);
|
|
91
|
+
}
|
|
92
|
+
// Remove surrounding quotes
|
|
93
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
94
|
+
value = value.substring(1, value.length - 1);
|
|
95
|
+
}
|
|
96
|
+
return value.length > 0 ? value : null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Set the ETag header on a GET response.
|
|
100
|
+
*
|
|
101
|
+
* Call this in GET-single-entity handlers after retrieving the entity:
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const org = await getOrganization(id);
|
|
104
|
+
* setETagHeader(ctx, 'organization', id, org.updatedAt);
|
|
105
|
+
* ctx.body = { data: org };
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @param ctx - Koa context (or compatible object with set/get methods)
|
|
109
|
+
* @param entityType - Entity type string (e.g., 'organization')
|
|
110
|
+
* @param id - Entity UUID
|
|
111
|
+
* @param updatedAt - Entity's last modification timestamp
|
|
112
|
+
*/
|
|
113
|
+
export function setETagHeader(ctx, entityType, id, updatedAt) {
|
|
114
|
+
const etag = generateETag(entityType, id, updatedAt);
|
|
115
|
+
ctx.set('ETag', etag);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check the If-Match header on a PUT/PATCH request.
|
|
119
|
+
*
|
|
120
|
+
* If the If-Match header is present and does NOT match the current entity's
|
|
121
|
+
* ETag, sets a 409 Conflict response with the current entity state and
|
|
122
|
+
* returns false (caller should `return` without proceeding).
|
|
123
|
+
*
|
|
124
|
+
* If the header is absent or matches, returns true (safe to proceed).
|
|
125
|
+
* This makes If-Match opt-in — backward compatible with clients that
|
|
126
|
+
* don't send it.
|
|
127
|
+
*
|
|
128
|
+
* @param ctx - Koa context
|
|
129
|
+
* @param entityType - Entity type string
|
|
130
|
+
* @param id - Entity UUID
|
|
131
|
+
* @param updatedAt - Entity's current updatedAt timestamp
|
|
132
|
+
* @param currentEntity - The current entity (included in 409 response body)
|
|
133
|
+
* @returns true if the update may proceed, false if 409 was sent
|
|
134
|
+
*/
|
|
135
|
+
export function checkIfMatch(ctx, entityType, id, updatedAt, currentEntity) {
|
|
136
|
+
const ifMatchHeader = ctx.get('If-Match');
|
|
137
|
+
if (!ifMatchHeader) {
|
|
138
|
+
// No If-Match header — backward compatible, proceed without check
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const currentETag = generateETag(entityType, id, updatedAt);
|
|
142
|
+
if (matchesETag(ifMatchHeader, currentETag)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
// Conflict — entity was modified since the client's last read
|
|
146
|
+
ctx.status = 409;
|
|
147
|
+
ctx.body = {
|
|
148
|
+
error: 'Conflict',
|
|
149
|
+
message: 'Entity has been modified since your last read. Please refresh and try again.',
|
|
150
|
+
currentEntity,
|
|
151
|
+
currentETag,
|
|
152
|
+
};
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=etag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"etag.js","sourceRoot":"","sources":["../../src/lib/etag.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image validation for branding asset uploads.
|
|
3
|
+
*
|
|
4
|
+
* Validates uploaded images using magic byte signature detection, size limits,
|
|
5
|
+
* and SVG sanitization. This prevents:
|
|
6
|
+
* - Disguised file uploads (wrong content-type vs actual content)
|
|
7
|
+
* - Oversized files
|
|
8
|
+
* - SVG-based XSS via embedded scripts or event handlers
|
|
9
|
+
*
|
|
10
|
+
* @module image-validator
|
|
11
|
+
* @see 06-bulk-operations-branding.md
|
|
12
|
+
*/
|
|
13
|
+
export type AssetType = 'logo' | 'favicon';
|
|
14
|
+
/** Result of image validation — either valid or contains an error message */
|
|
15
|
+
export interface ImageValidationResult {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
/** The detected content type from magic bytes (may differ from declared) */
|
|
19
|
+
detectedType?: string;
|
|
20
|
+
/** True if SVG was sanitized (scripts removed) */
|
|
21
|
+
sanitized?: boolean;
|
|
22
|
+
/** Sanitized buffer (only for SVG; otherwise same as input) */
|
|
23
|
+
data?: Buffer;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate an uploaded image buffer against content type, size, and content rules.
|
|
27
|
+
*
|
|
28
|
+
* For binary formats (PNG, JPEG, WebP, ICO), validates magic byte signatures.
|
|
29
|
+
* For SVG, validates XML structure and sanitizes dangerous content (scripts,
|
|
30
|
+
* event handlers, javascript: URIs).
|
|
31
|
+
*
|
|
32
|
+
* @param buffer - Raw file data
|
|
33
|
+
* @param contentType - Declared content type from the upload
|
|
34
|
+
* @param assetType - Whether this is a logo or favicon (determines size limit)
|
|
35
|
+
* @returns Validation result with optional sanitized data for SVGs
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateImage(buffer: Buffer, contentType: string, assetType: AssetType): ImageValidationResult;
|
|
38
|
+
/**
|
|
39
|
+
* Get the size limit for an asset type.
|
|
40
|
+
* @param assetType - The asset type
|
|
41
|
+
* @returns Size limit in bytes
|
|
42
|
+
*/
|
|
43
|
+
export declare function getSizeLimit(assetType: AssetType): number;
|
|
44
|
+
//# sourceMappingURL=image-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-validator.d.ts","sourceRoot":"","sources":["../../src/lib/image-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3C,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAgDD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,GACnB,qBAAqB,CA8BvB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image validation for branding asset uploads.
|
|
3
|
+
*
|
|
4
|
+
* Validates uploaded images using magic byte signature detection, size limits,
|
|
5
|
+
* and SVG sanitization. This prevents:
|
|
6
|
+
* - Disguised file uploads (wrong content-type vs actual content)
|
|
7
|
+
* - Oversized files
|
|
8
|
+
* - SVG-based XSS via embedded scripts or event handlers
|
|
9
|
+
*
|
|
10
|
+
* @module image-validator
|
|
11
|
+
* @see 06-bulk-operations-branding.md
|
|
12
|
+
*/
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/** Size limits per asset type (in bytes) */
|
|
17
|
+
const SIZE_LIMITS = {
|
|
18
|
+
logo: 2 * 1024 * 1024, // 2 MB — logos can be larger for hi-DPI
|
|
19
|
+
favicon: 512 * 1024, // 512 KB — favicons should be small
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Magic byte signatures for supported image formats.
|
|
23
|
+
* Each entry maps a content type to its expected leading bytes.
|
|
24
|
+
* SVG is XML-based and has no magic bytes — validated separately.
|
|
25
|
+
*/
|
|
26
|
+
const MAGIC_SIGNATURES = [
|
|
27
|
+
{ type: 'image/png', bytes: [0x89, 0x50, 0x4E, 0x47] },
|
|
28
|
+
{ type: 'image/jpeg', bytes: [0xFF, 0xD8, 0xFF] },
|
|
29
|
+
{ type: 'image/webp', bytes: [0x52, 0x49, 0x46, 0x46] },
|
|
30
|
+
{ type: 'image/x-icon', bytes: [0x00, 0x00, 0x01, 0x00] },
|
|
31
|
+
{ type: 'image/vnd.microsoft.icon', bytes: [0x00, 0x00, 0x01, 0x00] },
|
|
32
|
+
];
|
|
33
|
+
/** Content types that are allowed for upload */
|
|
34
|
+
const ALLOWED_TYPES = new Set([
|
|
35
|
+
'image/png',
|
|
36
|
+
'image/jpeg',
|
|
37
|
+
'image/svg+xml',
|
|
38
|
+
'image/webp',
|
|
39
|
+
'image/x-icon',
|
|
40
|
+
'image/vnd.microsoft.icon',
|
|
41
|
+
]);
|
|
42
|
+
/**
|
|
43
|
+
* Dangerous SVG elements and attributes that could enable XSS.
|
|
44
|
+
* These are stripped during sanitization to prevent stored XSS attacks.
|
|
45
|
+
*/
|
|
46
|
+
const SVG_DANGEROUS_ELEMENTS = /(<script[\s>][\s\S]*?<\/script>|<script[\s>][\s\S]*?\/>)/gi;
|
|
47
|
+
const SVG_EVENT_HANDLERS = /\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi;
|
|
48
|
+
const SVG_JAVASCRIPT_HREFS = /\s+(href|xlink:href)\s*=\s*["']javascript:[^"']*["']/gi;
|
|
49
|
+
const SVG_DATA_URIS_SCRIPT = /\s+(href|xlink:href)\s*=\s*["']data:text\/html[^"']*["']/gi;
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Public API
|
|
52
|
+
// ============================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Validate an uploaded image buffer against content type, size, and content rules.
|
|
55
|
+
*
|
|
56
|
+
* For binary formats (PNG, JPEG, WebP, ICO), validates magic byte signatures.
|
|
57
|
+
* For SVG, validates XML structure and sanitizes dangerous content (scripts,
|
|
58
|
+
* event handlers, javascript: URIs).
|
|
59
|
+
*
|
|
60
|
+
* @param buffer - Raw file data
|
|
61
|
+
* @param contentType - Declared content type from the upload
|
|
62
|
+
* @param assetType - Whether this is a logo or favicon (determines size limit)
|
|
63
|
+
* @returns Validation result with optional sanitized data for SVGs
|
|
64
|
+
*/
|
|
65
|
+
export function validateImage(buffer, contentType, assetType) {
|
|
66
|
+
// Check content type is allowed
|
|
67
|
+
if (!ALLOWED_TYPES.has(contentType)) {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
error: `Unsupported content type: ${contentType}. Allowed: ${[...ALLOWED_TYPES].join(', ')}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Check buffer is not empty
|
|
74
|
+
if (!buffer || buffer.length === 0) {
|
|
75
|
+
return { valid: false, error: 'Image data cannot be empty' };
|
|
76
|
+
}
|
|
77
|
+
// Check size limit for the asset type
|
|
78
|
+
const sizeLimit = SIZE_LIMITS[assetType];
|
|
79
|
+
if (buffer.length > sizeLimit) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
error: `File too large: ${buffer.length} bytes. Maximum for ${assetType}: ${sizeLimit} bytes (${formatBytes(sizeLimit)})`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// SVG validation is content-based (no magic bytes)
|
|
86
|
+
if (contentType === 'image/svg+xml') {
|
|
87
|
+
return validateAndSanitizeSvg(buffer);
|
|
88
|
+
}
|
|
89
|
+
// Binary image format — validate magic bytes
|
|
90
|
+
return validateMagicBytes(buffer, contentType);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the size limit for an asset type.
|
|
94
|
+
* @param assetType - The asset type
|
|
95
|
+
* @returns Size limit in bytes
|
|
96
|
+
*/
|
|
97
|
+
export function getSizeLimit(assetType) {
|
|
98
|
+
return SIZE_LIMITS[assetType];
|
|
99
|
+
}
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Internal helpers
|
|
102
|
+
// ============================================================================
|
|
103
|
+
/**
|
|
104
|
+
* Validate a binary image by checking its magic byte signature matches
|
|
105
|
+
* the declared content type. Prevents uploading a PNG disguised as a JPEG, etc.
|
|
106
|
+
*/
|
|
107
|
+
function validateMagicBytes(buffer, contentType) {
|
|
108
|
+
const signature = MAGIC_SIGNATURES.find((sig) => sig.type === contentType);
|
|
109
|
+
if (!signature) {
|
|
110
|
+
// No signature defined for this type — allow it (shouldn't happen due to ALLOWED_TYPES check)
|
|
111
|
+
return { valid: true, detectedType: contentType, data: buffer };
|
|
112
|
+
}
|
|
113
|
+
// Check that buffer is at least long enough for the signature
|
|
114
|
+
if (buffer.length < signature.bytes.length) {
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
error: `File too small to be a valid ${contentType} image`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Compare magic bytes
|
|
121
|
+
const matches = signature.bytes.every((byte, i) => buffer[i] === byte);
|
|
122
|
+
if (!matches) {
|
|
123
|
+
// Try to detect what the file actually is
|
|
124
|
+
const detected = detectContentType(buffer);
|
|
125
|
+
return {
|
|
126
|
+
valid: false,
|
|
127
|
+
error: `File content does not match declared type ${contentType}${detected ? `. Detected: ${detected}` : ''}`,
|
|
128
|
+
detectedType: detected ?? undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { valid: true, detectedType: contentType, data: buffer };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Validate SVG content and sanitize dangerous elements.
|
|
135
|
+
* SVGs are XML-based, so we check for script tags, event handlers,
|
|
136
|
+
* and javascript: URIs that could enable stored XSS.
|
|
137
|
+
*/
|
|
138
|
+
function validateAndSanitizeSvg(buffer) {
|
|
139
|
+
let content;
|
|
140
|
+
try {
|
|
141
|
+
content = buffer.toString('utf-8');
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return { valid: false, error: 'SVG file contains invalid UTF-8 encoding' };
|
|
145
|
+
}
|
|
146
|
+
// Basic SVG structure check — must contain an <svg element
|
|
147
|
+
if (!/<svg[\s>]/i.test(content)) {
|
|
148
|
+
return { valid: false, error: 'File does not appear to be a valid SVG (no <svg> element found)' };
|
|
149
|
+
}
|
|
150
|
+
// Sanitize: strip dangerous elements and attributes
|
|
151
|
+
let sanitized = content;
|
|
152
|
+
let wasSanitized = false;
|
|
153
|
+
// Remove <script> tags and their content
|
|
154
|
+
if (SVG_DANGEROUS_ELEMENTS.test(sanitized)) {
|
|
155
|
+
sanitized = sanitized.replace(SVG_DANGEROUS_ELEMENTS, '');
|
|
156
|
+
wasSanitized = true;
|
|
157
|
+
}
|
|
158
|
+
// Remove on* event handler attributes (onclick, onload, onerror, etc.)
|
|
159
|
+
if (SVG_EVENT_HANDLERS.test(sanitized)) {
|
|
160
|
+
sanitized = sanitized.replace(SVG_EVENT_HANDLERS, '');
|
|
161
|
+
wasSanitized = true;
|
|
162
|
+
}
|
|
163
|
+
// Remove javascript: URIs in href/xlink:href attributes
|
|
164
|
+
if (SVG_JAVASCRIPT_HREFS.test(sanitized)) {
|
|
165
|
+
sanitized = sanitized.replace(SVG_JAVASCRIPT_HREFS, '');
|
|
166
|
+
wasSanitized = true;
|
|
167
|
+
}
|
|
168
|
+
// Remove data:text/html URIs (can embed scripts)
|
|
169
|
+
if (SVG_DATA_URIS_SCRIPT.test(sanitized)) {
|
|
170
|
+
sanitized = sanitized.replace(SVG_DATA_URIS_SCRIPT, '');
|
|
171
|
+
wasSanitized = true;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
valid: true,
|
|
175
|
+
detectedType: 'image/svg+xml',
|
|
176
|
+
sanitized: wasSanitized,
|
|
177
|
+
data: Buffer.from(sanitized, 'utf-8'),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Attempt to detect the actual content type of a buffer by its magic bytes.
|
|
182
|
+
* Returns null if no known format is detected.
|
|
183
|
+
*/
|
|
184
|
+
function detectContentType(buffer) {
|
|
185
|
+
for (const sig of MAGIC_SIGNATURES) {
|
|
186
|
+
if (buffer.length >= sig.bytes.length) {
|
|
187
|
+
const matches = sig.bytes.every((byte, i) => buffer[i] === byte);
|
|
188
|
+
if (matches)
|
|
189
|
+
return sig.type;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Format bytes into a human-readable string (e.g., "512 KB", "2 MB").
|
|
196
|
+
*/
|
|
197
|
+
function formatBytes(bytes) {
|
|
198
|
+
if (bytes >= 1024 * 1024)
|
|
199
|
+
return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
|
|
200
|
+
if (bytes >= 1024)
|
|
201
|
+
return `${(bytes / 1024).toFixed(0)} KB`;
|
|
202
|
+
return `${bytes} bytes`;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=image-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-validator.js","sourceRoot":"","sources":["../../src/lib/image-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Observer for ephemeral, in-process validation of actual structured logger calls. */
|
|
2
|
+
export type OperationalLogObserver = (serializedArguments: string) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Observe arguments delivered to the production logger boundary.
|
|
5
|
+
*
|
|
6
|
+
* This is intended for owner-controlled diagnostics and executable privacy validation. Observers
|
|
7
|
+
* are process-local, receive no replay buffer, and must detach immediately after their bounded
|
|
8
|
+
* operation. They cannot alter, suppress, or replace the actual log write.
|
|
9
|
+
*
|
|
10
|
+
* @param observer - Callback receiving one JSON representation per logger call.
|
|
11
|
+
* @returns A function which removes exactly this observer.
|
|
12
|
+
*/
|
|
13
|
+
export declare function observeOperationalLogOutput(observer: OperationalLogObserver): () => void;
|
|
14
|
+
export declare const logger: import("pino").Logger<never, boolean>;
|
|
15
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,CAAC,mBAAmB,EAAE,MAAM,KAAK,IAAI,CAAC;AAI3E;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI,CAGxF;AAKD,eAAO,MAAM,MAAM,uCAiDjB,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { pino, stdSerializers } from 'pino';
|
|
2
|
+
const operationalLogObservers = new Set();
|
|
3
|
+
/**
|
|
4
|
+
* Observe arguments delivered to the production logger boundary.
|
|
5
|
+
*
|
|
6
|
+
* This is intended for owner-controlled diagnostics and executable privacy validation. Observers
|
|
7
|
+
* are process-local, receive no replay buffer, and must detach immediately after their bounded
|
|
8
|
+
* operation. They cannot alter, suppress, or replace the actual log write.
|
|
9
|
+
*
|
|
10
|
+
* @param observer - Callback receiving one JSON representation per logger call.
|
|
11
|
+
* @returns A function which removes exactly this observer.
|
|
12
|
+
*/
|
|
13
|
+
export function observeOperationalLogOutput(observer) {
|
|
14
|
+
operationalLogObservers.add(observer);
|
|
15
|
+
return () => operationalLogObservers.delete(observer);
|
|
16
|
+
}
|
|
17
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
18
|
+
const isTest = process.env.NODE_ENV === 'test';
|
|
19
|
+
export const logger = pino({
|
|
20
|
+
level: process.env.LOG_LEVEL || (isTest ? 'silent' : 'info'),
|
|
21
|
+
transport: !isProduction ? { target: 'pino-pretty', options: { colorize: true } } : undefined,
|
|
22
|
+
serializers: {
|
|
23
|
+
err: stdSerializers.err,
|
|
24
|
+
req: stdSerializers.req,
|
|
25
|
+
res: stdSerializers.res,
|
|
26
|
+
},
|
|
27
|
+
hooks: {
|
|
28
|
+
logMethod(arguments_, method) {
|
|
29
|
+
if (operationalLogObservers.size > 0) {
|
|
30
|
+
const serialized = serializeOperationalLogArguments(arguments_);
|
|
31
|
+
for (const observer of operationalLogObservers)
|
|
32
|
+
observer(serialized);
|
|
33
|
+
}
|
|
34
|
+
return method.apply(this, arguments_);
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
// Redact PII and sensitive fields from log output to prevent accidental
|
|
38
|
+
// exposure of credentials, tokens, and personal data. Pino replaces the
|
|
39
|
+
// value at each path with "[Redacted]" before the log line is serialized.
|
|
40
|
+
// Paths use dot-notation with wildcards so nested objects are also covered.
|
|
41
|
+
redact: {
|
|
42
|
+
paths: [
|
|
43
|
+
'password',
|
|
44
|
+
'token',
|
|
45
|
+
'authorization',
|
|
46
|
+
'cookie',
|
|
47
|
+
'refresh_token',
|
|
48
|
+
'client_secret',
|
|
49
|
+
'req.headers.authorization',
|
|
50
|
+
'req.headers.cookie',
|
|
51
|
+
'*.password',
|
|
52
|
+
'*.token',
|
|
53
|
+
'*.refresh_token',
|
|
54
|
+
'*.client_secret',
|
|
55
|
+
'*.authorization',
|
|
56
|
+
'email',
|
|
57
|
+
'userId',
|
|
58
|
+
'organizationId',
|
|
59
|
+
'uid',
|
|
60
|
+
'interactionUid',
|
|
61
|
+
'*.email',
|
|
62
|
+
'*.userId',
|
|
63
|
+
'*.organizationId',
|
|
64
|
+
'*.uid',
|
|
65
|
+
'*.interactionUid',
|
|
66
|
+
],
|
|
67
|
+
censor: '[Redacted]',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
/** Serialize one bounded logger invocation without letting observation break the owning request. */
|
|
71
|
+
function serializeOperationalLogArguments(arguments_) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.stringify(arguments_, (_key, value) => {
|
|
74
|
+
if (value instanceof Error) {
|
|
75
|
+
return { name: value.name, message: value.message, stack: value.stack };
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return '[unserializable-operational-log]';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAK5C,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAA0B,CAAC;AAElE;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAgC;IAC1E,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,OAAO,GAAG,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC;AAE/C,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5D,SAAS,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS;IAC7F,WAAW,EAAE;QACX,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,GAAG,EAAE,cAAc,CAAC,GAAG;KACxB;IACD,KAAK,EAAE;QACL,SAAS,CAAC,UAAU,EAAE,MAAM;YAC1B,IAAI,uBAAuB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,UAAU,GAAG,gCAAgC,CAAC,UAAU,CAAC,CAAC;gBAChE,KAAK,MAAM,QAAQ,IAAI,uBAAuB;oBAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YACvE,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;KACF;IACD,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,EAAE;QACN,KAAK,EAAE;YACL,UAAU;YACV,OAAO;YACP,eAAe;YACf,QAAQ;YACR,eAAe;YACf,eAAe;YACf,2BAA2B;YAC3B,oBAAoB;YACpB,YAAY;YACZ,SAAS;YACT,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,OAAO;YACP,QAAQ;YACR,gBAAgB;YAChB,KAAK;YACL,gBAAgB;YAChB,SAAS;YACT,UAAU;YACV,kBAAkB;YAClB,OAAO;YACP,kBAAkB;SACnB;QACD,MAAM,EAAE,YAAY;KACrB;CACF,CAAC,CAAC;AAEH,oGAAoG;AACpG,SAAS,gCAAgC,CAAC,UAAqB;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAc,EAAE,EAAE;YACzD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAC1E,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,kCAAkC,CAAC;IAC5C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run database migrations using node-pg-migrate's programmatic API.
|
|
3
|
+
*
|
|
4
|
+
* Executes SQL migrations from the `migrations/` directory against the
|
|
5
|
+
* database specified by `DATABASE_URL`. This is useful for:
|
|
6
|
+
* - Integration test setup (run migrations before tests)
|
|
7
|
+
* - Application startup (auto-migrate in development)
|
|
8
|
+
* - Programmatic rollback in tests
|
|
9
|
+
*
|
|
10
|
+
* @param direction - 'up' to apply pending migrations, 'down' to rollback
|
|
11
|
+
* @param count - Number of migrations to run (undefined = all pending for 'up', 1 for 'down')
|
|
12
|
+
*/
|
|
13
|
+
export declare function runMigrations(direction?: 'up' | 'down', count?: number): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/lib/migrator.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,SAAS,GAAE,IAAI,GAAG,MAAa,EAC/B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runner } from 'node-pg-migrate';
|
|
2
|
+
import { config } from '../config/index.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
import { getMigrationsDirectory } from './runtime-paths.js';
|
|
5
|
+
/**
|
|
6
|
+
* Run database migrations using node-pg-migrate's programmatic API.
|
|
7
|
+
*
|
|
8
|
+
* Executes SQL migrations from the `migrations/` directory against the
|
|
9
|
+
* database specified by `DATABASE_URL`. This is useful for:
|
|
10
|
+
* - Integration test setup (run migrations before tests)
|
|
11
|
+
* - Application startup (auto-migrate in development)
|
|
12
|
+
* - Programmatic rollback in tests
|
|
13
|
+
*
|
|
14
|
+
* @param direction - 'up' to apply pending migrations, 'down' to rollback
|
|
15
|
+
* @param count - Number of migrations to run (undefined = all pending for 'up', 1 for 'down')
|
|
16
|
+
*/
|
|
17
|
+
export async function runMigrations(direction = 'up', count) {
|
|
18
|
+
const migrationsDir = getMigrationsDirectory();
|
|
19
|
+
const options = {
|
|
20
|
+
databaseUrl: config.databaseUrl,
|
|
21
|
+
migrationsTable: 'pgmigrations',
|
|
22
|
+
dir: migrationsDir,
|
|
23
|
+
direction,
|
|
24
|
+
// For 'up', run all pending migrations; for 'down', default to rolling back 1
|
|
25
|
+
count: count ?? (direction === 'up' ? Infinity : 1),
|
|
26
|
+
// Redirect migration log output to pino logger
|
|
27
|
+
log: (msg) => logger.debug({ migration: true }, msg),
|
|
28
|
+
// Don't decamelize SQL file names — we use snake_case already
|
|
29
|
+
decamelize: false,
|
|
30
|
+
};
|
|
31
|
+
await runner(options);
|
|
32
|
+
logger.info({ direction, count }, 'Migrations complete');
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=migrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.js","sourceRoot":"","sources":["../../src/lib/migrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,SAAS,GAAkB,IAAI,EAC/B,KAAc;IAEd,MAAM,aAAa,GAAG,sBAAsB,EAAE,CAAC;IAE/C,MAAM,OAAO,GAAiB;QAC5B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,eAAe,EAAE,cAAc;QAC/B,GAAG,EAAE,aAAa;QAClB,SAAS;QACT,8EAA8E;QAC9E,KAAK,EAAE,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,+CAA+C;QAC/C,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC;QAC5D,8DAA8D;QAC9D,UAAU,EAAE,KAAK;KAClB,CAAC;IAEF,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IACtB,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/lib/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAMhC,wBAAsB,YAAY,IAAI,OAAO,CAAC,KAAK,CAAC,CAcnD;AAED,wBAAgB,QAAQ,IAAI,KAAK,CAGhC;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAMrD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
import { config } from '../config/index.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
let redis = null;
|
|
5
|
+
export async function connectRedis() {
|
|
6
|
+
redis = new Redis(config.redisUrl, {
|
|
7
|
+
maxRetriesPerRequest: 3,
|
|
8
|
+
lazyConnect: true,
|
|
9
|
+
});
|
|
10
|
+
await redis.connect();
|
|
11
|
+
logger.info('Redis connected');
|
|
12
|
+
redis.on('error', (err) => {
|
|
13
|
+
logger.error({ err }, 'Redis error');
|
|
14
|
+
});
|
|
15
|
+
return redis;
|
|
16
|
+
}
|
|
17
|
+
export function getRedis() {
|
|
18
|
+
if (!redis)
|
|
19
|
+
throw new Error('Redis not connected. Call connectRedis() first.');
|
|
20
|
+
return redis;
|
|
21
|
+
}
|
|
22
|
+
export async function disconnectRedis() {
|
|
23
|
+
if (redis) {
|
|
24
|
+
await redis.quit();
|
|
25
|
+
redis = null;
|
|
26
|
+
logger.info('Redis disconnected');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/lib/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,IAAI,KAAK,GAAiB,IAAI,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;QACjC,oBAAoB,EAAE,CAAC;QACvB,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IACtB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAE/B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;QAC/B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,GAAG,IAAI,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
|