sovrium 0.0.2
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 +3497 -0
- package/LICENSE.md +147 -0
- package/LICENSE_EE.md +297 -0
- package/README.md +321 -0
- package/drizzle/0000_melted_kabuki.sql +163 -0
- package/drizzle/meta/0000_snapshot.json +1216 -0
- package/drizzle/meta/_journal.json +13 -0
- package/package.json +167 -0
- package/schemas/0.0.1/app.openapi.json +70 -0
- package/schemas/0.0.1/app.schema.json +7961 -0
- package/schemas/0.0.2/app.openapi.json +80 -0
- package/schemas/0.0.2/app.schema.json +8829 -0
- package/schemas/development/app.openapi.json +70 -0
- package/schemas/development/app.schema.json +7456 -0
- package/src/application/errors/app-validation-error.ts +14 -0
- package/src/application/errors/static-generation-error.ts +16 -0
- package/src/application/metadata/favicon-transformer.ts +127 -0
- package/src/application/models/server.ts +27 -0
- package/src/application/ports/models/user-metadata.ts +36 -0
- package/src/application/ports/models/user-session.ts +34 -0
- package/src/application/ports/repositories/activity-log-repository.ts +68 -0
- package/src/application/ports/repositories/activity-repository.ts +49 -0
- package/src/application/ports/repositories/analytics-repository.ts +164 -0
- package/src/application/ports/repositories/auth-repository.ts +33 -0
- package/src/application/ports/repositories/batch-repository.ts +86 -0
- package/src/application/ports/repositories/comment-repository.ts +150 -0
- package/src/application/ports/repositories/index.ts +41 -0
- package/src/application/ports/repositories/table-repository.ts +139 -0
- package/src/application/ports/services/css-compiler.ts +55 -0
- package/src/application/ports/services/index.ts +16 -0
- package/src/application/ports/services/page-renderer.ts +79 -0
- package/src/application/ports/services/server-factory.ts +80 -0
- package/src/application/ports/services/static-site-generator.ts +82 -0
- package/src/application/use-cases/activity/programs.ts +66 -0
- package/src/application/use-cases/analytics/collect-page-view.ts +114 -0
- package/src/application/use-cases/analytics/purge-old-data.ts +40 -0
- package/src/application/use-cases/analytics/query-campaigns.ts +43 -0
- package/src/application/use-cases/analytics/query-devices.ts +36 -0
- package/src/application/use-cases/analytics/query-overview.ts +50 -0
- package/src/application/use-cases/analytics/query-pages.ts +40 -0
- package/src/application/use-cases/analytics/query-referrers.ts +43 -0
- package/src/application/use-cases/analytics/ua-parser.ts +89 -0
- package/src/application/use-cases/analytics/visitor-hash.ts +77 -0
- package/src/application/use-cases/auth/bootstrap-admin.ts +270 -0
- package/src/application/use-cases/list-activity-logs.ts +123 -0
- package/src/application/use-cases/server/generate-static-helpers.ts +374 -0
- package/src/application/use-cases/server/generate-static.ts +287 -0
- package/src/application/use-cases/server/start-server.ts +118 -0
- package/src/application/use-cases/server/startup-error-handler.ts +69 -0
- package/src/application/use-cases/server/static-content-generators.ts +182 -0
- package/src/application/use-cases/server/static-language-generators.ts +181 -0
- package/src/application/use-cases/server/static-url-rewriter.ts +237 -0
- package/src/application/use-cases/server/translation-replacer.ts +164 -0
- package/src/application/use-cases/tables/activity-programs.ts +93 -0
- package/src/application/use-cases/tables/batch-operations.ts +156 -0
- package/src/application/use-cases/tables/comment-programs.ts +436 -0
- package/src/application/use-cases/tables/permissions/permissions.ts +25 -0
- package/src/application/use-cases/tables/programs.ts +435 -0
- package/src/application/use-cases/tables/table-operations.ts +412 -0
- package/src/application/use-cases/tables/user-role.ts +52 -0
- package/src/application/use-cases/tables/utils/display-formatter.ts +471 -0
- package/src/application/use-cases/tables/utils/field-read-filter.ts +189 -0
- package/src/application/use-cases/tables/utils/list-helpers.ts +122 -0
- package/src/application/use-cases/tables/utils/record-transformer.ts +319 -0
- package/src/cli.ts +370 -0
- package/src/domain/errors/create-tagged-error.ts +36 -0
- package/src/domain/errors/index.ts +78 -0
- package/src/domain/models/api/analytics.ts +179 -0
- package/src/domain/models/api/auth.ts +231 -0
- package/src/domain/models/api/common.ts +60 -0
- package/src/domain/models/api/error.ts +89 -0
- package/src/domain/models/api/health.ts +38 -0
- package/src/domain/models/api/index.ts +42 -0
- package/src/domain/models/api/request.ts +132 -0
- package/src/domain/models/api/tables.ts +444 -0
- package/src/domain/models/app/analytics/index.ts +129 -0
- package/src/domain/models/app/auth/config.ts +116 -0
- package/src/domain/models/app/auth/index.ts +230 -0
- package/src/domain/models/app/auth/methods/email-and-password.ts +67 -0
- package/src/domain/models/app/auth/methods/index.ts +11 -0
- package/src/domain/models/app/auth/methods/magic-link.ts +54 -0
- package/src/domain/models/app/auth/oauth/index.ts +8 -0
- package/src/domain/models/app/auth/oauth/providers.ts +105 -0
- package/src/domain/models/app/auth/plugins/admin.ts +130 -0
- package/src/domain/models/app/auth/plugins/index.ts +74 -0
- package/src/domain/models/app/auth/plugins/two-factor.ts +63 -0
- package/src/domain/models/app/auth/roles.ts +179 -0
- package/src/domain/models/app/auth/strategies.ts +191 -0
- package/src/domain/models/app/auth/validation.ts +127 -0
- package/src/domain/models/app/common/branded-ids.ts +200 -0
- package/src/domain/models/app/common/definitions.ts +187 -0
- package/src/domain/models/app/component/common/component-children.ts +119 -0
- package/src/domain/models/app/component/common/component-props.ts +89 -0
- package/src/domain/models/app/component/common/component-reference.ts +170 -0
- package/src/domain/models/app/component/component.ts +81 -0
- package/src/domain/models/app/components.ts +65 -0
- package/src/domain/models/app/description.ts +83 -0
- package/src/domain/models/app/index.ts +258 -0
- package/src/domain/models/app/language/language-config.ts +200 -0
- package/src/domain/models/app/languages.ts +205 -0
- package/src/domain/models/app/name.ts +66 -0
- package/src/domain/models/app/page/common/interactions/click-interaction.ts +116 -0
- package/src/domain/models/app/page/common/interactions/entrance-animation.ts +84 -0
- package/src/domain/models/app/page/common/interactions/hover-interaction.ts +144 -0
- package/src/domain/models/app/page/common/interactions/interactions.ts +64 -0
- package/src/domain/models/app/page/common/interactions/scroll-interaction.ts +93 -0
- package/src/domain/models/app/page/common/responsive.ts +114 -0
- package/src/domain/models/app/page/common/url.ts +35 -0
- package/src/domain/models/app/page/common/variable-reference.ts +53 -0
- package/src/domain/models/app/page/id.ts +44 -0
- package/src/domain/models/app/page/index.ts +270 -0
- package/src/domain/models/app/page/meta/analytics.ts +248 -0
- package/src/domain/models/app/page/meta/custom-elements.ts +180 -0
- package/src/domain/models/app/page/meta/dns-prefetch.ts +77 -0
- package/src/domain/models/app/page/meta/favicon-set.ts +203 -0
- package/src/domain/models/app/page/meta/favicon.ts +50 -0
- package/src/domain/models/app/page/meta/favicons-config.ts +73 -0
- package/src/domain/models/app/page/meta/index.ts +278 -0
- package/src/domain/models/app/page/meta/open-graph.ts +166 -0
- package/src/domain/models/app/page/meta/preload.ts +190 -0
- package/src/domain/models/app/page/meta/structured-data/article.ts +211 -0
- package/src/domain/models/app/page/meta/structured-data/breadcrumb.ts +115 -0
- package/src/domain/models/app/page/meta/structured-data/common-fields.ts +201 -0
- package/src/domain/models/app/page/meta/structured-data/education-event.ts +256 -0
- package/src/domain/models/app/page/meta/structured-data/faq-page.ts +127 -0
- package/src/domain/models/app/page/meta/structured-data/index.ts +95 -0
- package/src/domain/models/app/page/meta/structured-data/local-business.ts +247 -0
- package/src/domain/models/app/page/meta/structured-data/organization.ts +171 -0
- package/src/domain/models/app/page/meta/structured-data/person.ts +138 -0
- package/src/domain/models/app/page/meta/structured-data/postal-address.ts +106 -0
- package/src/domain/models/app/page/meta/structured-data/product.ts +214 -0
- package/src/domain/models/app/page/meta/twitter-card.ts +217 -0
- package/src/domain/models/app/page/name.ts +38 -0
- package/src/domain/models/app/page/path.ts +21 -0
- package/src/domain/models/app/page/scripts/external-scripts.ts +163 -0
- package/src/domain/models/app/page/scripts/features.ts +135 -0
- package/src/domain/models/app/page/scripts/inline-scripts.ts +114 -0
- package/src/domain/models/app/page/scripts/scripts.ts +102 -0
- package/src/domain/models/app/page/sections.ts +298 -0
- package/src/domain/models/app/pages.ts +61 -0
- package/src/domain/models/app/permissions/index.ts +61 -0
- package/src/domain/models/app/permissions/resource-action.ts +114 -0
- package/src/domain/models/app/permissions/roles.ts +120 -0
- package/src/domain/models/app/table/check-constraints.ts +105 -0
- package/src/domain/models/app/table/cycle-detection.ts +124 -0
- package/src/domain/models/app/table/database-identifier.ts +153 -0
- package/src/domain/models/app/table/field-name.ts +36 -0
- package/src/domain/models/app/table/field-types/advanced/array-field.ts +33 -0
- package/src/domain/models/app/table/field-types/advanced/autonumber-field.ts +54 -0
- package/src/domain/models/app/table/field-types/advanced/button-field.ts +56 -0
- package/src/domain/models/app/table/field-types/advanced/color-field.ts +57 -0
- package/src/domain/models/app/table/field-types/advanced/count-field.ts +54 -0
- package/src/domain/models/app/table/field-types/advanced/formula-field.ts +58 -0
- package/src/domain/models/app/table/field-types/advanced/geolocation-field.ts +49 -0
- package/src/domain/models/app/table/field-types/advanced/index.ts +16 -0
- package/src/domain/models/app/table/field-types/advanced/json-field.ts +25 -0
- package/src/domain/models/app/table/field-types/advanced/unknown-field.ts +85 -0
- package/src/domain/models/app/table/field-types/base-field.ts +42 -0
- package/src/domain/models/app/table/field-types/date-time/created-at-field.ts +49 -0
- package/src/domain/models/app/table/field-types/date-time/date-field.ts +95 -0
- package/src/domain/models/app/table/field-types/date-time/deleted-at-field.ts +56 -0
- package/src/domain/models/app/table/field-types/date-time/duration-field.ts +73 -0
- package/src/domain/models/app/table/field-types/date-time/index.ts +12 -0
- package/src/domain/models/app/table/field-types/date-time/updated-at-field.ts +50 -0
- package/src/domain/models/app/table/field-types/index.ts +19 -0
- package/src/domain/models/app/table/field-types/media/barcode-field.ts +58 -0
- package/src/domain/models/app/table/field-types/media/index.ts +10 -0
- package/src/domain/models/app/table/field-types/media/multiple-attachments-field.ts +80 -0
- package/src/domain/models/app/table/field-types/media/single-attachment-field.ts +81 -0
- package/src/domain/models/app/table/field-types/numeric/currency-field.ts +144 -0
- package/src/domain/models/app/table/field-types/numeric/decimal-field.ts +113 -0
- package/src/domain/models/app/table/field-types/numeric/index.ts +13 -0
- package/src/domain/models/app/table/field-types/numeric/integer-field.ts +98 -0
- package/src/domain/models/app/table/field-types/numeric/percentage-field.ts +115 -0
- package/src/domain/models/app/table/field-types/numeric/progress-field.ts +71 -0
- package/src/domain/models/app/table/field-types/numeric/rating-field.ts +74 -0
- package/src/domain/models/app/table/field-types/relational/index.ts +10 -0
- package/src/domain/models/app/table/field-types/relational/lookup-field.ts +46 -0
- package/src/domain/models/app/table/field-types/relational/relationship-field.ts +112 -0
- package/src/domain/models/app/table/field-types/relational/rollup-field.ts +58 -0
- package/src/domain/models/app/table/field-types/selection/checkbox-field.ts +51 -0
- package/src/domain/models/app/table/field-types/selection/index.ts +11 -0
- package/src/domain/models/app/table/field-types/selection/multi-select-field.ts +68 -0
- package/src/domain/models/app/table/field-types/selection/single-select-field.ts +54 -0
- package/src/domain/models/app/table/field-types/selection/status-field.ts +37 -0
- package/src/domain/models/app/table/field-types/text/email-field.ts +80 -0
- package/src/domain/models/app/table/field-types/text/index.ts +13 -0
- package/src/domain/models/app/table/field-types/text/long-text-field.ts +77 -0
- package/src/domain/models/app/table/field-types/text/phone-number-field.ts +82 -0
- package/src/domain/models/app/table/field-types/text/rich-text-field.ts +66 -0
- package/src/domain/models/app/table/field-types/text/single-line-text-field.ts +79 -0
- package/src/domain/models/app/table/field-types/text/url-field.ts +81 -0
- package/src/domain/models/app/table/field-types/user/created-by-field.ts +50 -0
- package/src/domain/models/app/table/field-types/user/deleted-by-field.ts +57 -0
- package/src/domain/models/app/table/field-types/user/index.ts +11 -0
- package/src/domain/models/app/table/field-types/user/updated-by-field.ts +51 -0
- package/src/domain/models/app/table/field-types/user/user-field.ts +52 -0
- package/src/domain/models/app/table/field-types/validation-utils.ts +166 -0
- package/src/domain/models/app/table/fields.ts +216 -0
- package/src/domain/models/app/table/foreign-keys.ts +111 -0
- package/src/domain/models/app/table/formula-keywords.ts +326 -0
- package/src/domain/models/app/table/id.ts +31 -0
- package/src/domain/models/app/table/index.ts +290 -0
- package/src/domain/models/app/table/indexes.ts +80 -0
- package/src/domain/models/app/table/name.ts +37 -0
- package/src/domain/models/app/table/permissions/field-permission.ts +83 -0
- package/src/domain/models/app/table/permissions/index.ts +167 -0
- package/src/domain/models/app/table/permissions/permission-evaluator.ts +372 -0
- package/src/domain/models/app/table/permissions/permission.ts +49 -0
- package/src/domain/models/app/table/primary-key.ts +62 -0
- package/src/domain/models/app/table/table-formula-validation.ts +168 -0
- package/src/domain/models/app/table/table-indexes-validation.ts +38 -0
- package/src/domain/models/app/table/table-permissions-validation.ts +77 -0
- package/src/domain/models/app/table/table-primary-key-validation.ts +49 -0
- package/src/domain/models/app/table/table-views-validation.ts +408 -0
- package/src/domain/models/app/table/unique-constraints.ts +79 -0
- package/src/domain/models/app/table/views/fields.ts +28 -0
- package/src/domain/models/app/table/views/filters.ts +162 -0
- package/src/domain/models/app/table/views/group-by.ts +32 -0
- package/src/domain/models/app/table/views/id.ts +50 -0
- package/src/domain/models/app/table/views/index.ts +177 -0
- package/src/domain/models/app/table/views/name.ts +32 -0
- package/src/domain/models/app/table/views/permissions.ts +98 -0
- package/src/domain/models/app/table/views/sorts.ts +31 -0
- package/src/domain/models/app/tables.ts +695 -0
- package/src/domain/models/app/theme/animations.ts +208 -0
- package/src/domain/models/app/theme/border-radius.ts +58 -0
- package/src/domain/models/app/theme/breakpoints.ts +62 -0
- package/src/domain/models/app/theme/colors.ts +110 -0
- package/src/domain/models/app/theme/fonts.ts +164 -0
- package/src/domain/models/app/theme/shadows.ts +61 -0
- package/src/domain/models/app/theme/spacing.ts +115 -0
- package/src/domain/models/app/theme.ts +66 -0
- package/src/domain/models/app/version.ts +87 -0
- package/src/domain/models/record-comment.ts +91 -0
- package/src/domain/utils/content-parsing.ts +49 -0
- package/src/domain/utils/format-detection.ts +69 -0
- package/src/domain/utils/index.ts +9 -0
- package/src/domain/utils/route-matcher.ts +184 -0
- package/src/domain/utils/translation-resolver.ts +170 -0
- package/src/index.ts +208 -0
- package/src/infrastructure/analytics/tracking-script.ts +48 -0
- package/src/infrastructure/auth/better-auth/auth.ts +216 -0
- package/src/infrastructure/auth/better-auth/email-handlers.ts +162 -0
- package/src/infrastructure/auth/better-auth/index.ts +16 -0
- package/src/infrastructure/auth/better-auth/layer.ts +97 -0
- package/src/infrastructure/auth/better-auth/plugins/admin.ts +56 -0
- package/src/infrastructure/auth/better-auth/plugins/magic-link.ts +31 -0
- package/src/infrastructure/auth/better-auth/plugins/two-factor.ts +19 -0
- package/src/infrastructure/auth/better-auth/schema.ts +152 -0
- package/src/infrastructure/auth/index.ts +27 -0
- package/src/infrastructure/css/cache/css-cache-service.ts +130 -0
- package/src/infrastructure/css/compiler.ts +210 -0
- package/src/infrastructure/css/css-compiler-live.ts +20 -0
- package/src/infrastructure/css/index.ts +25 -0
- package/src/infrastructure/css/styles/animation-styles-generator.ts +177 -0
- package/src/infrastructure/css/styles/click-animations.ts +147 -0
- package/src/infrastructure/css/styles/component-layer-generators.ts +147 -0
- package/src/infrastructure/css/theme/theme-generators.ts +130 -0
- package/src/infrastructure/css/theme/theme-layer-generators.ts +219 -0
- package/src/infrastructure/css/theme/theme-token-resolver.ts +76 -0
- package/src/infrastructure/database/activity-queries.ts +111 -0
- package/src/infrastructure/database/auth/auth-validation.ts +101 -0
- package/src/infrastructure/database/drizzle/db-bun.ts +17 -0
- package/src/infrastructure/database/drizzle/db.ts +17 -0
- package/src/infrastructure/database/drizzle/index.ts +16 -0
- package/src/infrastructure/database/drizzle/layer.ts +34 -0
- package/src/infrastructure/database/drizzle/migrate.ts +77 -0
- package/src/infrastructure/database/drizzle/schema/activity-log.ts +111 -0
- package/src/infrastructure/database/drizzle/schema/analytics-page-views.ts +116 -0
- package/src/infrastructure/database/drizzle/schema/migration-audit.ts +68 -0
- package/src/infrastructure/database/drizzle/schema/record-comments.ts +79 -0
- package/src/infrastructure/database/drizzle/schema.ts +12 -0
- package/src/infrastructure/database/field-utils.ts +87 -0
- package/src/infrastructure/database/filter-operators.ts +136 -0
- package/src/infrastructure/database/formula/formula-trigger-generators.ts +114 -0
- package/src/infrastructure/database/formula/formula-utils.ts +440 -0
- package/src/infrastructure/database/generators/index-generators.ts +152 -0
- package/src/infrastructure/database/generators/trigger-generators.ts +154 -0
- package/src/infrastructure/database/index.ts +35 -0
- package/src/infrastructure/database/lookup/lookup-expression-generators.ts +356 -0
- package/src/infrastructure/database/lookup/lookup-expressions.ts +116 -0
- package/src/infrastructure/database/lookup/lookup-view-generators.ts +403 -0
- package/src/infrastructure/database/lookup/lookup-view-helpers.ts +65 -0
- package/src/infrastructure/database/lookup/lookup-view-triggers.ts +121 -0
- package/src/infrastructure/database/migration-audit-trail.ts +375 -0
- package/src/infrastructure/database/repositories/activity-log-repository-live.ts +99 -0
- package/src/infrastructure/database/repositories/activity-repository-live.ts +21 -0
- package/src/infrastructure/database/repositories/analytics-repository-live.ts +316 -0
- package/src/infrastructure/database/repositories/auth-repository-live.ts +42 -0
- package/src/infrastructure/database/repositories/batch-repository-live.ts +29 -0
- package/src/infrastructure/database/repositories/comment-repository-live.ts +39 -0
- package/src/infrastructure/database/repositories/table-repository-live.ts +38 -0
- package/src/infrastructure/database/schema/schema-dependency-sorting.ts +142 -0
- package/src/infrastructure/database/schema/schema-initializer.ts +598 -0
- package/src/infrastructure/database/schema-migration/column-detection.ts +286 -0
- package/src/infrastructure/database/schema-migration/constants.ts +31 -0
- package/src/infrastructure/database/schema-migration/constraint-sync.ts +288 -0
- package/src/infrastructure/database/schema-migration/index-sync.ts +108 -0
- package/src/infrastructure/database/schema-migration/index.ts +66 -0
- package/src/infrastructure/database/schema-migration/migration-statements.ts +106 -0
- package/src/infrastructure/database/schema-migration/rename-detection.ts +87 -0
- package/src/infrastructure/database/schema-migration/table-operations.ts +65 -0
- package/src/infrastructure/database/schema-migration/type-utils.ts +98 -0
- package/src/infrastructure/database/schema-migration/types.ts +14 -0
- package/src/infrastructure/database/schema-migration-helpers.ts +53 -0
- package/src/infrastructure/database/session-context.ts +20 -0
- package/src/infrastructure/database/sql/sql-check-constraints.ts +252 -0
- package/src/infrastructure/database/sql/sql-column-generators.ts +174 -0
- package/src/infrastructure/database/sql/sql-execution.ts +245 -0
- package/src/infrastructure/database/sql/sql-field-predicates.ts +81 -0
- package/src/infrastructure/database/sql/sql-generators.ts +91 -0
- package/src/infrastructure/database/sql/sql-junction-tables.ts +79 -0
- package/src/infrastructure/database/sql/sql-key-constraints.ts +210 -0
- package/src/infrastructure/database/sql/sql-type-mappings.ts +106 -0
- package/src/infrastructure/database/sql/sql-utils.ts +53 -0
- package/src/infrastructure/database/table-live-layers.ts +30 -0
- package/src/infrastructure/database/table-operations/column-generators.ts +82 -0
- package/src/infrastructure/database/table-operations/create-table-sql.ts +81 -0
- package/src/infrastructure/database/table-operations/index.ts +55 -0
- package/src/infrastructure/database/table-operations/migration-utils.ts +157 -0
- package/src/infrastructure/database/table-operations/table-effects.ts +234 -0
- package/src/infrastructure/database/table-operations/table-features.ts +96 -0
- package/src/infrastructure/database/table-operations/type-compatibility.ts +58 -0
- package/src/infrastructure/database/table-operations.ts +47 -0
- package/src/infrastructure/database/table-queries/batch/batch-create.ts +80 -0
- package/src/infrastructure/database/table-queries/batch/batch-delete.ts +212 -0
- package/src/infrastructure/database/table-queries/batch/batch-helpers.ts +124 -0
- package/src/infrastructure/database/table-queries/batch/batch-restore.ts +161 -0
- package/src/infrastructure/database/table-queries/batch/batch-update.ts +146 -0
- package/src/infrastructure/database/table-queries/batch/batch-upsert.ts +357 -0
- package/src/infrastructure/database/table-queries/batch/batch.ts +14 -0
- package/src/infrastructure/database/table-queries/crud/crud-read.ts +351 -0
- package/src/infrastructure/database/table-queries/crud/crud-write.ts +399 -0
- package/src/infrastructure/database/table-queries/crud/crud.ts +16 -0
- package/src/infrastructure/database/table-queries/index.ts +11 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/authorship-helpers.ts +152 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/create-record-helpers.ts +90 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/delete-helpers.ts +163 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/record-fetch-helpers.ts +79 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/update-helpers.ts +74 -0
- package/src/infrastructure/database/table-queries/query-helpers/activity-log-helpers.ts +53 -0
- package/src/infrastructure/database/table-queries/query-helpers/activity-queries.ts +106 -0
- package/src/infrastructure/database/table-queries/query-helpers/aggregation-helpers.ts +314 -0
- package/src/infrastructure/database/table-queries/query-helpers/comment-queries.ts +414 -0
- package/src/infrastructure/database/table-queries/query-helpers/record-validation-queries.ts +126 -0
- package/src/infrastructure/database/table-queries/query-helpers/trash-helpers.ts +58 -0
- package/src/infrastructure/database/table-queries/shared/error-handling.ts +47 -0
- package/src/infrastructure/database/table-queries/shared/typed-execute.ts +27 -0
- package/src/infrastructure/database/table-queries/shared/user-join-helpers.ts +38 -0
- package/src/infrastructure/database/table-queries/shared/validation.ts +39 -0
- package/src/infrastructure/database/views/view-generators.ts +258 -0
- package/src/infrastructure/devtools/devtools-layer.ts +43 -0
- package/src/infrastructure/devtools/index.ts +8 -0
- package/src/infrastructure/email/email-config.ts +103 -0
- package/src/infrastructure/email/email-service.ts +152 -0
- package/src/infrastructure/email/index.ts +107 -0
- package/src/infrastructure/email/nodemailer.ts +125 -0
- package/src/infrastructure/email/templates.ts +244 -0
- package/src/infrastructure/errors/auth-config-required-error.ts +21 -0
- package/src/infrastructure/errors/auth-error.ts +16 -0
- package/src/infrastructure/errors/css-compilation-error.ts +14 -0
- package/src/infrastructure/errors/index.ts +26 -0
- package/src/infrastructure/errors/schema-initialization-error.ts +19 -0
- package/src/infrastructure/errors/server-creation-error.ts +14 -0
- package/src/infrastructure/filesystem/copy-directory.ts +136 -0
- package/src/infrastructure/layers/app-layer.ts +61 -0
- package/src/infrastructure/layers/page-renderer-layer.ts +41 -0
- package/src/infrastructure/logging/index.ts +8 -0
- package/src/infrastructure/logging/logger.ts +204 -0
- package/src/infrastructure/schema/file-loader.ts +53 -0
- package/src/infrastructure/schema/index.ts +15 -0
- package/src/infrastructure/schema/remote-loader.ts +48 -0
- package/src/infrastructure/server/index.ts +26 -0
- package/src/infrastructure/server/language-detection.ts +87 -0
- package/src/infrastructure/server/lifecycle.ts +67 -0
- package/src/infrastructure/server/route-setup/api-routes.ts +310 -0
- package/src/infrastructure/server/route-setup/auth-route-utils.ts +399 -0
- package/src/infrastructure/server/route-setup/auth-routes.ts +245 -0
- package/src/infrastructure/server/route-setup/openapi-routes.ts +45 -0
- package/src/infrastructure/server/route-setup/openapi-schema.ts +120 -0
- package/src/infrastructure/server/route-setup/page-routes.ts +219 -0
- package/src/infrastructure/server/route-setup/static-assets.ts +191 -0
- package/src/infrastructure/server/server-factory-live.ts +45 -0
- package/src/infrastructure/server/server.ts +275 -0
- package/src/infrastructure/server/ssg-adapter.ts +196 -0
- package/src/infrastructure/server/static-site-generator-live.ts +20 -0
- package/src/infrastructure/utils/accept-language-parser.ts +106 -0
- package/src/infrastructure/utils/glob-matcher.ts +50 -0
- package/src/presentation/api/client.ts +114 -0
- package/src/presentation/api/middleware/auth.ts +233 -0
- package/src/presentation/api/middleware/table.ts +155 -0
- package/src/presentation/api/middleware/validation.ts +88 -0
- package/src/presentation/api/routes/activity/get-activity-by-id-handler.ts +77 -0
- package/src/presentation/api/routes/activity/index.ts +28 -0
- package/src/presentation/api/routes/activity.ts +339 -0
- package/src/presentation/api/routes/analytics.ts +328 -0
- package/src/presentation/api/routes/auth.ts +169 -0
- package/src/presentation/api/routes/index.ts +11 -0
- package/src/presentation/api/routes/tables/activity-handlers.ts +57 -0
- package/src/presentation/api/routes/tables/batch-permission-helpers.ts +163 -0
- package/src/presentation/api/routes/tables/batch-routes.ts +355 -0
- package/src/presentation/api/routes/tables/comment-handlers.ts +377 -0
- package/src/presentation/api/routes/tables/create-record-helpers.ts +179 -0
- package/src/presentation/api/routes/tables/effect-runner.ts +58 -0
- package/src/presentation/api/routes/tables/error-handlers.ts +53 -0
- package/src/presentation/api/routes/tables/field-permission-validation.ts +167 -0
- package/src/presentation/api/routes/tables/filter-parser.ts +75 -0
- package/src/presentation/api/routes/tables/formula-parser.ts +118 -0
- package/src/presentation/api/routes/tables/index.ts +113 -0
- package/src/presentation/api/routes/tables/list-records-filter.ts +54 -0
- package/src/presentation/api/routes/tables/param-parsers.ts +59 -0
- package/src/presentation/api/routes/tables/record-handlers.ts +484 -0
- package/src/presentation/api/routes/tables/record-routes.ts +53 -0
- package/src/presentation/api/routes/tables/record-update-handler.ts +200 -0
- package/src/presentation/api/routes/tables/sort-validation.ts +85 -0
- package/src/presentation/api/routes/tables/table-routes.ts +76 -0
- package/src/presentation/api/routes/tables/timezone-validation.ts +41 -0
- package/src/presentation/api/routes/tables/upsert-helpers.ts +471 -0
- package/src/presentation/api/routes/tables/utils.ts +159 -0
- package/src/presentation/api/routes/tables/view-routes.ts +51 -0
- package/src/presentation/api/routes/tables.ts +9 -0
- package/src/presentation/api/utils/context-helpers.ts +43 -0
- package/src/presentation/api/utils/error-sanitizer.ts +235 -0
- package/src/presentation/api/utils/field-permission-validator.ts +53 -0
- package/src/presentation/api/utils/filter-field-validator.ts +90 -0
- package/src/presentation/api/utils/index.ts +13 -0
- package/src/presentation/api/utils/run-effect.ts +94 -0
- package/src/presentation/api/utils/validate-request.ts +89 -0
- package/src/presentation/api/validation/index.ts +29 -0
- package/src/presentation/api/validation/rules/field-rules.ts +158 -0
- package/src/presentation/api/validation/rules/record-rules.ts +73 -0
- package/src/presentation/cli/index.ts +19 -0
- package/src/presentation/cli/schema-loader.ts +172 -0
- package/src/presentation/hooks/use-breakpoint.ts +155 -0
- package/src/presentation/rendering/render-error-pages.tsx +60 -0
- package/src/presentation/rendering/render-homepage.tsx +137 -0
- package/src/presentation/scripts/script-renderers.ts +112 -0
- package/src/presentation/styling/animation-composer.ts +117 -0
- package/src/presentation/styling/index.ts +13 -0
- package/src/presentation/styling/parse-style.ts +243 -0
- package/src/presentation/styling/style-utils.ts +50 -0
- package/src/presentation/styling/theme-colors.ts +53 -0
- package/src/presentation/translations/component-utils.ts +54 -0
- package/src/presentation/translations/index.ts +16 -0
- package/src/presentation/translations/translation-resolver.ts +22 -0
- package/src/presentation/ui/languages/language-switcher.tsx +119 -0
- package/src/presentation/ui/metadata/analytics-builders.tsx +174 -0
- package/src/presentation/ui/metadata/analytics-head.tsx +39 -0
- package/src/presentation/ui/metadata/custom-elements-builders.tsx +157 -0
- package/src/presentation/ui/metadata/extract-component-meta.ts +108 -0
- package/src/presentation/ui/metadata/head-elements.tsx +164 -0
- package/src/presentation/ui/metadata/index.tsx +35 -0
- package/src/presentation/ui/metadata/meta-utils.tsx +42 -0
- package/src/presentation/ui/metadata/open-graph-meta.tsx +57 -0
- package/src/presentation/ui/metadata/structured-data-from-component.tsx +134 -0
- package/src/presentation/ui/metadata/structured-data.tsx +88 -0
- package/src/presentation/ui/metadata/twitter-card-meta.tsx +80 -0
- package/src/presentation/ui/pages/DefaultHomePage.tsx +43 -0
- package/src/presentation/ui/pages/DefaultPageConfigs.ts +220 -0
- package/src/presentation/ui/pages/DynamicPage.tsx +307 -0
- package/src/presentation/ui/pages/ErrorPage.tsx +25 -0
- package/src/presentation/ui/pages/NotFoundPage.tsx +25 -0
- package/src/presentation/ui/pages/PageBodyScripts.tsx +242 -0
- package/src/presentation/ui/pages/PageBodyStyles.ts +52 -0
- package/src/presentation/ui/pages/PageHead.tsx +380 -0
- package/src/presentation/ui/pages/PageLangResolver.ts +58 -0
- package/src/presentation/ui/pages/PageMain.tsx +58 -0
- package/src/presentation/ui/pages/PageMetadata.ts +168 -0
- package/src/presentation/ui/pages/PageMetadataI18n.ts +169 -0
- package/src/presentation/ui/pages/PageScripts.ts +78 -0
- package/src/presentation/ui/pages/SectionRenderer.tsx +67 -0
- package/src/presentation/ui/pages/SectionSpacing.tsx +131 -0
- package/src/presentation/ui/sections/component-renderer.tsx +426 -0
- package/src/presentation/ui/sections/component-renderer.types.ts +33 -0
- package/src/presentation/ui/sections/components/component-reference-handler.tsx +74 -0
- package/src/presentation/ui/sections/components/component-resolution.ts +65 -0
- package/src/presentation/ui/sections/components/index.ts +9 -0
- package/src/presentation/ui/sections/hero.tsx +394 -0
- package/src/presentation/ui/sections/props/component-builder.ts +183 -0
- package/src/presentation/ui/sections/props/element-props.ts +179 -0
- package/src/presentation/ui/sections/props/index.ts +9 -0
- package/src/presentation/ui/sections/props/prop-conversion.ts +171 -0
- package/src/presentation/ui/sections/props/props-builder-config.ts +42 -0
- package/src/presentation/ui/sections/props/props-builder.ts +296 -0
- package/src/presentation/ui/sections/renderers/element-renderers/html-element-renderer.tsx +124 -0
- package/src/presentation/ui/sections/renderers/element-renderers/index.ts +59 -0
- package/src/presentation/ui/sections/renderers/element-renderers/interactive-renderers.tsx +231 -0
- package/src/presentation/ui/sections/renderers/element-renderers/media-renderers.tsx +102 -0
- package/src/presentation/ui/sections/renderers/element-renderers/text-content-renderers.tsx +42 -0
- package/src/presentation/ui/sections/renderers/element-renderers.ts +53 -0
- package/src/presentation/ui/sections/renderers/html-element-helpers.ts +100 -0
- package/src/presentation/ui/sections/renderers/specialized-renderers.tsx +212 -0
- package/src/presentation/ui/sections/rendering/component-dispatch-config.ts +31 -0
- package/src/presentation/ui/sections/rendering/component-registry/index.ts +39 -0
- package/src/presentation/ui/sections/rendering/component-registry/interactive-components.ts +54 -0
- package/src/presentation/ui/sections/rendering/component-registry/media-components.ts +36 -0
- package/src/presentation/ui/sections/rendering/component-registry/special-components.tsx +153 -0
- package/src/presentation/ui/sections/rendering/component-registry/structural-components.ts +215 -0
- package/src/presentation/ui/sections/rendering/component-registry/text-components.ts +57 -0
- package/src/presentation/ui/sections/rendering/component-registry-helpers.tsx +29 -0
- package/src/presentation/ui/sections/rendering/component-registry.tsx +21 -0
- package/src/presentation/ui/sections/rendering/component-type-dispatcher.tsx +33 -0
- package/src/presentation/ui/sections/rendering/index.ts +9 -0
- package/src/presentation/ui/sections/responsive/responsive-children-builder.tsx +96 -0
- package/src/presentation/ui/sections/responsive/responsive-content-builder.tsx +95 -0
- package/src/presentation/ui/sections/responsive/responsive-props-merger.ts +195 -0
- package/src/presentation/ui/sections/responsive/responsive-resolver.ts +213 -0
- package/src/presentation/ui/sections/styling/animation-composer-wrapper.ts +65 -0
- package/src/presentation/ui/sections/styling/class-builders.ts +45 -0
- package/src/presentation/ui/sections/styling/color-resolver.ts +43 -0
- package/src/presentation/ui/sections/styling/hover-interaction-handler.ts +107 -0
- package/src/presentation/ui/sections/styling/index.ts +9 -0
- package/src/presentation/ui/sections/styling/interaction-props-builder.ts +55 -0
- package/src/presentation/ui/sections/styling/shadow-resolver.ts +83 -0
- package/src/presentation/ui/sections/styling/spacing-resolver.ts +104 -0
- package/src/presentation/ui/sections/styling/style-processor.ts +170 -0
- package/src/presentation/ui/sections/styling/theme-tokens.ts +184 -0
- package/src/presentation/ui/sections/translations/i18n-content-resolver.ts +198 -0
- package/src/presentation/ui/sections/translations/index.ts +9 -0
- package/src/presentation/ui/sections/translations/translation-handler.ts +143 -0
- package/src/presentation/ui/sections/translations/variable-substitution.ts +225 -0
- package/src/presentation/ui/sections/utils/time-parser.ts +82 -0
- package/src/presentation/utils/link-attributes.ts +50 -0
- package/src/presentation/utils/string-utils.ts +58 -0
- package/src/presentation/utils/styles.ts +50 -0
- package/tsconfig.json +46 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Migration Audit Trail
|
|
10
|
+
*
|
|
11
|
+
* Provides functions to track schema migrations, record checksums, and log rollback operations.
|
|
12
|
+
*
|
|
13
|
+
* ## Why Raw SQL Instead of Drizzle Query Builder?
|
|
14
|
+
*
|
|
15
|
+
* These functions use raw SQL via `executeSQL()` instead of Drizzle's query builder because:
|
|
16
|
+
*
|
|
17
|
+
* 1. **Transaction Type Incompatibility**: The schema-initializer uses `SQL.begin()` from bun:sql,
|
|
18
|
+
* which provides a transaction object with only an `unsafe()` method for raw SQL execution.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Drizzle Requirement**: Drizzle's query builder (`insert()`, `select()`, `where()`) requires
|
|
21
|
+
* a full `drizzle()` database instance wrapping an `SQL` client, not the raw transaction object.
|
|
22
|
+
*
|
|
23
|
+
* 3. **Architectural Constraint**: Refactoring to use `db.transaction()` instead of `SQL.begin()`
|
|
24
|
+
* would require significant changes to schema-initializer.ts and related files.
|
|
25
|
+
*
|
|
26
|
+
* The Drizzle schema definitions in `./drizzle/schema/migration-audit.ts` are used for:
|
|
27
|
+
* - Drizzle migrations (creating the tables)
|
|
28
|
+
* - Type exports for consumers
|
|
29
|
+
* - Table name constants (below)
|
|
30
|
+
*
|
|
31
|
+
* @see src/infrastructure/database/drizzle/schema/migration-audit.ts - Drizzle schema definitions
|
|
32
|
+
* @see src/infrastructure/database/schema-initializer.ts - Uses SQL.begin() transactions
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { createHash } from 'node:crypto'
|
|
36
|
+
import { getTableName } from 'drizzle-orm'
|
|
37
|
+
import { Effect } from 'effect'
|
|
38
|
+
import { logInfo } from '@/infrastructure/logging/logger'
|
|
39
|
+
import {
|
|
40
|
+
sovriumMigrationHistory,
|
|
41
|
+
sovriumMigrationLog,
|
|
42
|
+
sovriumSchemaChecksum,
|
|
43
|
+
} from './drizzle/schema'
|
|
44
|
+
import { executeSQL, SQLExecutionError, type TransactionLike } from './sql/sql-execution'
|
|
45
|
+
import { escapeSqlString } from './sql/sql-utils'
|
|
46
|
+
import type { App } from '@/domain/models/app'
|
|
47
|
+
|
|
48
|
+
// Re-export types from Drizzle schema for consumers
|
|
49
|
+
export type {
|
|
50
|
+
SovriumMigrationHistory,
|
|
51
|
+
SovriumMigrationLog,
|
|
52
|
+
SovriumSchemaChecksum,
|
|
53
|
+
} from './drizzle/schema'
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Table name constants derived from Drizzle schema
|
|
57
|
+
* Using getTableName() ensures consistency with schema definitions
|
|
58
|
+
* Schema-qualified names are used for system schema tables
|
|
59
|
+
*/
|
|
60
|
+
const MIGRATION_HISTORY_TABLE = `system.${getTableName(sovriumMigrationHistory)}`
|
|
61
|
+
const MIGRATION_LOG_TABLE = `system.${getTableName(sovriumMigrationLog)}`
|
|
62
|
+
const SCHEMA_CHECKSUM_TABLE = `system.${getTableName(sovriumSchemaChecksum)}`
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create schema snapshot object from app configuration
|
|
66
|
+
* Extracts tables array (including views) for consistent serialization
|
|
67
|
+
*
|
|
68
|
+
* IMPORTANT: Views are part of table definitions (table.views property).
|
|
69
|
+
* Including tables in the snapshot automatically includes views, since views
|
|
70
|
+
* are nested within table objects. This ensures that view changes trigger
|
|
71
|
+
* schema migrations correctly.
|
|
72
|
+
*/
|
|
73
|
+
const createSchemaSnapshot = (app: App): { readonly tables: readonly object[] } => ({
|
|
74
|
+
tables: app.tables ?? [],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Recursively sort object keys to ensure consistent serialization
|
|
79
|
+
* PostgreSQL JSONB reorders object properties, so we must normalize before hashing
|
|
80
|
+
*/
|
|
81
|
+
const sortObjectKeys = (obj: unknown): unknown => {
|
|
82
|
+
if (obj === null || obj === undefined) return obj
|
|
83
|
+
if (typeof obj !== 'object') return obj
|
|
84
|
+
if (Array.isArray(obj)) return obj.map(sortObjectKeys)
|
|
85
|
+
|
|
86
|
+
// Sort object keys alphabetically and recursively sort nested objects
|
|
87
|
+
const objRecord = obj as Record<string, unknown>
|
|
88
|
+
const keys = Object.keys(objRecord).toSorted()
|
|
89
|
+
|
|
90
|
+
return keys.reduce(
|
|
91
|
+
(sorted, key) => ({
|
|
92
|
+
...sorted,
|
|
93
|
+
[key]: sortObjectKeys(objRecord[key]),
|
|
94
|
+
}),
|
|
95
|
+
{} as Record<string, unknown>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Calculate SHA-256 checksum from schema tables
|
|
101
|
+
* Used by both generation and validation
|
|
102
|
+
*
|
|
103
|
+
* CRITICAL: Properties are sorted before hashing to ensure consistent checksums
|
|
104
|
+
* regardless of property insertion order (JavaScript objects) or JSONB normalization.
|
|
105
|
+
*/
|
|
106
|
+
const calculateChecksum = (tables: readonly object[], context: string = ''): string => {
|
|
107
|
+
// Sort object keys recursively to normalize property order
|
|
108
|
+
const normalizedTables = sortObjectKeys(tables)
|
|
109
|
+
const schemaJson = JSON.stringify(normalizedTables, undefined, 2)
|
|
110
|
+
|
|
111
|
+
if (context) {
|
|
112
|
+
logInfo(`[calculateChecksum] DEBUG ${context} - JSON string: ${schemaJson.substring(0, 300)}`)
|
|
113
|
+
logInfo(`[calculateChecksum] DEBUG ${context} - JSON length: ${schemaJson.length}`)
|
|
114
|
+
}
|
|
115
|
+
return createHash('sha256').update(schemaJson).digest('hex')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generate checksum for the current schema state
|
|
120
|
+
* Uses SHA-256 hash of the JSON-serialized schema
|
|
121
|
+
*/
|
|
122
|
+
export const generateSchemaChecksum = (app: App): string => {
|
|
123
|
+
const schemaSnapshot = createSchemaSnapshot(app)
|
|
124
|
+
return calculateChecksum(schemaSnapshot.tables, 'GENERATION')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Record a migration in the history table
|
|
129
|
+
* Stores the checksum, schema snapshot, and timestamp
|
|
130
|
+
*/
|
|
131
|
+
export const recordMigration = (
|
|
132
|
+
tx: TransactionLike,
|
|
133
|
+
app: App
|
|
134
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
135
|
+
Effect.gen(function* () {
|
|
136
|
+
logInfo('[recordMigration] Recording migration in history table...')
|
|
137
|
+
const checksum = generateSchemaChecksum(app)
|
|
138
|
+
const schemaSnapshot = createSchemaSnapshot(app)
|
|
139
|
+
|
|
140
|
+
// Get the next version number
|
|
141
|
+
const versionQuery = `
|
|
142
|
+
SELECT COALESCE(MAX(version), 0) + 1 as next_version
|
|
143
|
+
FROM ${MIGRATION_HISTORY_TABLE}
|
|
144
|
+
`
|
|
145
|
+
const versionResult = yield* executeSQL(tx, versionQuery)
|
|
146
|
+
// executeSQL returns an array directly, not {rows, rowCount}
|
|
147
|
+
const nextVersion =
|
|
148
|
+
(versionResult[0] as { next_version: number } | undefined)?.next_version ?? 1
|
|
149
|
+
logInfo(`[recordMigration] Next version: ${nextVersion}`)
|
|
150
|
+
|
|
151
|
+
// Insert migration record
|
|
152
|
+
// SECURITY NOTE: Using string interpolation for version (number) and proper escaping for JSON
|
|
153
|
+
// - nextVersion is a number from database query, not user input
|
|
154
|
+
// - checksum is a hex string from SHA-256 hash, safe
|
|
155
|
+
// - schemaSnapshot is JSON-escaped to prevent SQL injection
|
|
156
|
+
// NOTE: JSON.stringify is appropriate here - serializing trusted data for storage, not validation
|
|
157
|
+
// @effect-diagnostics effect/preferSchemaOverJson:off
|
|
158
|
+
const escapedSchema = escapeSqlString(JSON.stringify(schemaSnapshot))
|
|
159
|
+
const insertSQL = `
|
|
160
|
+
INSERT INTO ${MIGRATION_HISTORY_TABLE} (version, checksum, schema)
|
|
161
|
+
VALUES (${nextVersion}, '${checksum}', '${escapedSchema}')
|
|
162
|
+
`
|
|
163
|
+
yield* executeSQL(tx, insertSQL)
|
|
164
|
+
logInfo('[recordMigration] Migration recorded successfully')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Log a rollback operation in the migration log table
|
|
169
|
+
* Records the error reason and marks status as COMPLETED
|
|
170
|
+
*/
|
|
171
|
+
export const logRollbackOperation = (
|
|
172
|
+
tx: TransactionLike,
|
|
173
|
+
reason: string
|
|
174
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
175
|
+
Effect.gen(function* () {
|
|
176
|
+
logInfo('[logRollbackOperation] Logging rollback operation...')
|
|
177
|
+
// Escape single quotes in reason string to prevent SQL injection
|
|
178
|
+
const escapedReason = escapeSqlString(reason)
|
|
179
|
+
const insertSQL = `
|
|
180
|
+
INSERT INTO ${MIGRATION_LOG_TABLE} (operation, reason, status)
|
|
181
|
+
VALUES ('ROLLBACK', '${escapedReason}', 'COMPLETED')
|
|
182
|
+
`
|
|
183
|
+
yield* executeSQL(tx, insertSQL)
|
|
184
|
+
logInfo('[logRollbackOperation] Rollback operation logged')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Store the schema checksum in the system.schema_checksum table
|
|
189
|
+
* Uses a singleton row with id='singleton' to store the current checksum
|
|
190
|
+
*/
|
|
191
|
+
export const storeSchemaChecksum = (
|
|
192
|
+
tx: TransactionLike,
|
|
193
|
+
app: App
|
|
194
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
195
|
+
Effect.gen(function* () {
|
|
196
|
+
logInfo('[storeSchemaChecksum] Storing schema checksum...')
|
|
197
|
+
const checksum = generateSchemaChecksum(app)
|
|
198
|
+
const schemaSnapshot = createSchemaSnapshot(app)
|
|
199
|
+
|
|
200
|
+
// DEBUG: Log what we're hashing AND storing
|
|
201
|
+
// NOTE: JSON.stringify for pretty-printing debug output, not validation (Effect Schema not needed)
|
|
202
|
+
// @effect-diagnostics effect/preferSchemaOverJson:off
|
|
203
|
+
const tablesJson = JSON.stringify(schemaSnapshot.tables, undefined, 2)
|
|
204
|
+
// @effect-diagnostics effect/preferSchemaOverJson:off
|
|
205
|
+
const fullSchemaJson = JSON.stringify(schemaSnapshot, undefined, 2)
|
|
206
|
+
logInfo(
|
|
207
|
+
`[storeSchemaChecksum] DEBUG - Tables JSON being hashed: ${tablesJson.substring(0, 500)}`
|
|
208
|
+
)
|
|
209
|
+
logInfo(
|
|
210
|
+
`[storeSchemaChecksum] DEBUG - Full schema JSON being stored: ${fullSchemaJson.substring(0, 500)}`
|
|
211
|
+
)
|
|
212
|
+
logInfo(`[storeSchemaChecksum] DEBUG - Generated checksum: ${checksum}`)
|
|
213
|
+
|
|
214
|
+
// Use INSERT ... ON CONFLICT to update existing singleton row or create new one
|
|
215
|
+
// IMPORTANT: Use same JSON formatting (2-space indent) as calculateChecksum for consistency
|
|
216
|
+
const escapedSchema = escapeSqlString(fullSchemaJson)
|
|
217
|
+
const upsertSQL = `
|
|
218
|
+
INSERT INTO ${SCHEMA_CHECKSUM_TABLE} (id, checksum, schema, updated_at)
|
|
219
|
+
VALUES ('singleton', '${checksum}', '${escapedSchema}', NOW())
|
|
220
|
+
ON CONFLICT (id)
|
|
221
|
+
DO UPDATE SET checksum = EXCLUDED.checksum, schema = EXCLUDED.schema, updated_at = NOW()
|
|
222
|
+
`
|
|
223
|
+
yield* executeSQL(tx, upsertSQL)
|
|
224
|
+
logInfo('[storeSchemaChecksum] Schema checksum stored successfully')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Retrieve the previous schema from the system.schema_checksum table
|
|
229
|
+
* Returns undefined if no previous schema exists (first migration)
|
|
230
|
+
*/
|
|
231
|
+
export const getPreviousSchema = (
|
|
232
|
+
tx: TransactionLike
|
|
233
|
+
): Effect.Effect<{ readonly tables: readonly object[] } | undefined, SQLExecutionError> =>
|
|
234
|
+
Effect.gen(function* () {
|
|
235
|
+
logInfo('[getPreviousSchema] Retrieving previous schema...')
|
|
236
|
+
|
|
237
|
+
// Retrieve previous schema from singleton row
|
|
238
|
+
// Table is guaranteed to exist after Drizzle migrations
|
|
239
|
+
const selectSQL = `SELECT schema FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
|
|
240
|
+
const result = yield* executeSQL(tx, selectSQL)
|
|
241
|
+
|
|
242
|
+
if (!result || result.length === 0) {
|
|
243
|
+
logInfo('[getPreviousSchema] No previous schema found')
|
|
244
|
+
return undefined
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const schemaData = (result[0] as { schema: { tables: readonly object[] } } | undefined)?.schema
|
|
248
|
+
logInfo('[getPreviousSchema] Previous schema retrieved successfully')
|
|
249
|
+
return schemaData
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Retrieve the stored checksum from the system.schema_checksum table
|
|
254
|
+
* Returns undefined if no previous checksum exists (first migration)
|
|
255
|
+
*/
|
|
256
|
+
export const getStoredChecksum = (
|
|
257
|
+
tx: TransactionLike
|
|
258
|
+
): Effect.Effect<string | undefined, SQLExecutionError> =>
|
|
259
|
+
Effect.gen(function* () {
|
|
260
|
+
logInfo('[getStoredChecksum] Retrieving stored checksum...')
|
|
261
|
+
|
|
262
|
+
// Check if the checksum table exists first
|
|
263
|
+
const tableExistsSQL = `
|
|
264
|
+
SELECT EXISTS (
|
|
265
|
+
SELECT FROM information_schema.tables
|
|
266
|
+
WHERE table_schema = 'system' AND table_name = 'schema_checksum'
|
|
267
|
+
) as exists
|
|
268
|
+
`
|
|
269
|
+
const tableExistsResult = yield* executeSQL(tx, tableExistsSQL)
|
|
270
|
+
const tableExists = (tableExistsResult[0] as { exists: boolean } | undefined)?.exists
|
|
271
|
+
|
|
272
|
+
if (!tableExists) {
|
|
273
|
+
logInfo('[getStoredChecksum] Checksum table does not exist')
|
|
274
|
+
return undefined
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Retrieve checksum from singleton row
|
|
278
|
+
const selectSQL = `SELECT checksum FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
|
|
279
|
+
const result = yield* executeSQL(tx, selectSQL)
|
|
280
|
+
|
|
281
|
+
if (!result || result.length === 0) {
|
|
282
|
+
logInfo('[getStoredChecksum] No stored checksum found')
|
|
283
|
+
return undefined
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const storedChecksum = (result[0] as { checksum: string } | undefined)?.checksum
|
|
287
|
+
logInfo(`[getStoredChecksum] Retrieved checksum: ${storedChecksum}`)
|
|
288
|
+
return storedChecksum
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Check if checksum table exists in database
|
|
293
|
+
*/
|
|
294
|
+
const checksumTableExists = (tx: TransactionLike): Effect.Effect<boolean, SQLExecutionError> =>
|
|
295
|
+
Effect.gen(function* () {
|
|
296
|
+
const tableExistsSQL = `
|
|
297
|
+
SELECT EXISTS (
|
|
298
|
+
SELECT FROM information_schema.tables
|
|
299
|
+
WHERE table_schema = 'system' AND table_name = 'schema_checksum'
|
|
300
|
+
) as exists
|
|
301
|
+
`
|
|
302
|
+
const tableExistsResult = yield* executeSQL(tx, tableExistsSQL)
|
|
303
|
+
return (tableExistsResult[0] as { exists: boolean } | undefined)?.exists ?? false
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Retrieve stored checksum and schema from database
|
|
308
|
+
*/
|
|
309
|
+
const getStoredChecksumData = (
|
|
310
|
+
tx: TransactionLike
|
|
311
|
+
): Effect.Effect<
|
|
312
|
+
{ checksum: string; schema: { tables: readonly object[] } } | undefined,
|
|
313
|
+
SQLExecutionError
|
|
314
|
+
> =>
|
|
315
|
+
Effect.gen(function* () {
|
|
316
|
+
const selectSQL = `SELECT checksum, schema FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
|
|
317
|
+
const result = yield* executeSQL(tx, selectSQL)
|
|
318
|
+
|
|
319
|
+
if (!result || result.length === 0) {
|
|
320
|
+
return undefined
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return result[0] as { checksum: string; schema: { tables: readonly object[] } } | undefined
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Validate stored checksum against recalculated checksum from stored schema
|
|
328
|
+
* Detects schema drift or checksum tampering
|
|
329
|
+
* Throws error if mismatch detected
|
|
330
|
+
*/
|
|
331
|
+
export const validateStoredChecksum = (
|
|
332
|
+
tx: TransactionLike
|
|
333
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
334
|
+
Effect.gen(function* () {
|
|
335
|
+
logInfo('[validateStoredChecksum] Validating stored checksum...')
|
|
336
|
+
|
|
337
|
+
const tableExists = yield* checksumTableExists(tx)
|
|
338
|
+
if (!tableExists) {
|
|
339
|
+
logInfo('[validateStoredChecksum] Checksum table does not exist - skipping validation')
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const row = yield* getStoredChecksumData(tx)
|
|
344
|
+
if (!row) {
|
|
345
|
+
logInfo('[validateStoredChecksum] No stored checksum found - skipping validation')
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const storedChecksum = row.checksum
|
|
350
|
+
const storedSchema = row.schema
|
|
351
|
+
const recalculatedChecksum = calculateChecksum(storedSchema.tables, 'VALIDATION')
|
|
352
|
+
|
|
353
|
+
logInfo(`[validateStoredChecksum] Stored checksum: ${storedChecksum}`)
|
|
354
|
+
logInfo(`[validateStoredChecksum] Recalculated checksum: ${recalculatedChecksum}`)
|
|
355
|
+
|
|
356
|
+
// DEBUG: Log the actual JSON being hashed (JSON.stringify for debug output, not validation)
|
|
357
|
+
// NOTE: JSON.stringify appropriate for debug output (Effect Schema not needed)
|
|
358
|
+
// @effect-diagnostics effect/preferSchemaOverJson:off
|
|
359
|
+
logInfo(
|
|
360
|
+
`[validateStoredChecksum] DEBUG - Stored tables JSON: ${JSON.stringify(storedSchema.tables, undefined, 2).substring(0, 500)}`
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
if (storedChecksum !== recalculatedChecksum) {
|
|
364
|
+
const errorMsg =
|
|
365
|
+
'Schema drift detected: checksum mismatch. The stored checksum does not match the recalculated checksum from the stored schema. This indicates database tampering or corruption.'
|
|
366
|
+
logInfo(`[validateStoredChecksum] ${errorMsg}`)
|
|
367
|
+
return yield* new SQLExecutionError({
|
|
368
|
+
message: errorMsg,
|
|
369
|
+
sql: 'validateStoredChecksum',
|
|
370
|
+
cause: new Error(errorMsg),
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
logInfo('[validateStoredChecksum] Checksum validation passed')
|
|
375
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { desc, eq, gte, sql } from 'drizzle-orm'
|
|
9
|
+
import { Effect, Layer } from 'effect'
|
|
10
|
+
import {
|
|
11
|
+
ActivityLogRepository,
|
|
12
|
+
ActivityLogDatabaseError,
|
|
13
|
+
} from '@/application/ports/repositories/activity-log-repository'
|
|
14
|
+
import { users } from '@/infrastructure/auth/better-auth/schema'
|
|
15
|
+
import { db } from '@/infrastructure/database'
|
|
16
|
+
import { activityLogs } from '@/infrastructure/database/drizzle/schema/activity-log'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Activity Log Repository Implementation
|
|
20
|
+
*
|
|
21
|
+
* Uses Drizzle ORM query builder for type-safe, SQL-injection-proof queries.
|
|
22
|
+
*/
|
|
23
|
+
export const ActivityLogRepositoryLive = Layer.succeed(ActivityLogRepository, {
|
|
24
|
+
/**
|
|
25
|
+
* List all activity logs with user metadata
|
|
26
|
+
*/
|
|
27
|
+
listAll: () =>
|
|
28
|
+
Effect.tryPromise({
|
|
29
|
+
try: async () => {
|
|
30
|
+
const rows = await db
|
|
31
|
+
.select({
|
|
32
|
+
id: activityLogs.id,
|
|
33
|
+
createdAt: activityLogs.createdAt,
|
|
34
|
+
userId: activityLogs.userId,
|
|
35
|
+
sessionId: activityLogs.sessionId,
|
|
36
|
+
action: activityLogs.action,
|
|
37
|
+
tableName: activityLogs.tableName,
|
|
38
|
+
tableId: activityLogs.tableId,
|
|
39
|
+
recordId: activityLogs.recordId,
|
|
40
|
+
changes: activityLogs.changes,
|
|
41
|
+
ipAddress: activityLogs.ipAddress,
|
|
42
|
+
userAgent: activityLogs.userAgent,
|
|
43
|
+
userName: users.name,
|
|
44
|
+
userEmail: users.email,
|
|
45
|
+
})
|
|
46
|
+
.from(activityLogs)
|
|
47
|
+
.leftJoin(users, eq(activityLogs.userId, users.id))
|
|
48
|
+
.where(gte(activityLogs.createdAt, sql`NOW() - INTERVAL '1 year'`))
|
|
49
|
+
.orderBy(sql`(${activityLogs.userId} IS NULL) DESC`, desc(activityLogs.createdAt))
|
|
50
|
+
|
|
51
|
+
return rows.map((row) => ({
|
|
52
|
+
id: row.id,
|
|
53
|
+
createdAt: row.createdAt,
|
|
54
|
+
userId: row.userId,
|
|
55
|
+
sessionId: row.sessionId,
|
|
56
|
+
action: row.action,
|
|
57
|
+
tableName: row.tableName,
|
|
58
|
+
tableId: row.tableId,
|
|
59
|
+
recordId: row.recordId,
|
|
60
|
+
changes: row.changes,
|
|
61
|
+
ipAddress: row.ipAddress,
|
|
62
|
+
userAgent: row.userAgent,
|
|
63
|
+
user:
|
|
64
|
+
row.userId && row.userName && row.userEmail
|
|
65
|
+
? { id: row.userId, name: row.userName, email: row.userEmail }
|
|
66
|
+
: // eslint-disable-next-line unicorn/no-null -- Null is intentional for system-logged activities (no user_id)
|
|
67
|
+
null,
|
|
68
|
+
}))
|
|
69
|
+
},
|
|
70
|
+
catch: (error) => new ActivityLogDatabaseError({ cause: error }),
|
|
71
|
+
}),
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create activity log entry
|
|
75
|
+
*/
|
|
76
|
+
create: (log) =>
|
|
77
|
+
Effect.tryPromise({
|
|
78
|
+
try: async () => {
|
|
79
|
+
const result = await db
|
|
80
|
+
.insert(activityLogs)
|
|
81
|
+
.values({
|
|
82
|
+
id: crypto.randomUUID(),
|
|
83
|
+
userId: log.userId,
|
|
84
|
+
action: log.action,
|
|
85
|
+
tableName: log.tableName,
|
|
86
|
+
tableId: log.tableId,
|
|
87
|
+
recordId: log.recordId,
|
|
88
|
+
changes: log.changes,
|
|
89
|
+
sessionId: log.sessionId,
|
|
90
|
+
ipAddress: log.ipAddress,
|
|
91
|
+
userAgent: log.userAgent,
|
|
92
|
+
})
|
|
93
|
+
.returning()
|
|
94
|
+
|
|
95
|
+
return result[0]!
|
|
96
|
+
},
|
|
97
|
+
catch: (error) => new ActivityLogDatabaseError({ cause: error }),
|
|
98
|
+
}),
|
|
99
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Layer } from 'effect'
|
|
9
|
+
import { ActivityRepository } from '@/application/ports/repositories/activity-repository'
|
|
10
|
+
import { getRecordHistory } from '@/infrastructure/database/table-queries/query-helpers/activity-queries'
|
|
11
|
+
import { checkRecordExists } from '@/infrastructure/database/table-queries/query-helpers/record-validation-queries'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Live implementation of ActivityRepository using activity-queries infrastructure
|
|
15
|
+
*
|
|
16
|
+
* Maps port method names to infrastructure function names.
|
|
17
|
+
*/
|
|
18
|
+
export const ActivityRepositoryLive = Layer.succeed(ActivityRepository, {
|
|
19
|
+
getRecordHistory,
|
|
20
|
+
checkRecordExists,
|
|
21
|
+
})
|