@beechcms/api 0.6.0-preview.1 → 0.6.0-preview.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/assets/dashboard/BeechLogo.svg +18 -18
- package/assets/dashboard/BeechLogoLIght.svg +48 -48
- package/assets/dashboard/assets/chart-BrJFdsGZ.js +39 -0
- package/assets/dashboard/assets/index-CIaoMxuB.css +1 -0
- package/assets/dashboard/assets/index-wTKgygsg.js +636 -0
- package/assets/dashboard/assets/pie-chart-recharts-BEaE1FOE.js +1 -0
- package/assets/dashboard/assets/timeseries-chart-recharts-DmWM6imF.js +1 -0
- package/assets/dashboard/beechLogoDark.svg +48 -48
- package/assets/dashboard/index.html +18 -18
- package/assets/dashboard/noResult.svg +42 -42
- package/assets/dashboard/sol.svg +3 -3
- package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
- package/migrations/0000_v040_base.sql +265 -265
- package/migrations/0029_automations.sql +14 -14
- package/migrations/0030_test_seeds.sql +125 -125
- package/package.json +2 -2
- package/src/auth/__fixtures__/in-memory-hash-provider.ts +17 -17
- package/src/auth/__fixtures__/static-token-service.ts +22 -22
- package/src/auth/bcrypt-hash-provider.ts +24 -24
- package/src/auth/constants.ts +14 -14
- package/src/auth/generate-refresh-token.test.ts +23 -23
- package/src/auth/hash-provider.test.ts +55 -55
- package/src/auth/jose-token-service.ts +62 -62
- package/src/auth/jwt-claims-passthrough.test.ts +87 -87
- package/src/auth/login.test.ts +100 -100
- package/src/auth/login.ts +78 -78
- package/src/auth/refresh.ts +11 -11
- package/src/auth/token-service.test.ts +90 -90
- package/src/factory.ts +370 -368
- package/src/features/automations/__tests__/action-executors.test.ts +255 -255
- package/src/features/automations/__tests__/automation-runner.test.ts +196 -196
- package/src/features/automations/__tests__/automation-runner.utils.test.ts +60 -60
- package/src/features/automations/__tests__/automations.handler.test.ts +264 -264
- package/src/features/automations/__tests__/automations.repository.test.ts +163 -163
- package/src/features/automations/__tests__/automations.schema.test.ts +224 -224
- package/src/features/automations/__tests__/context-resolver.test.ts +126 -126
- package/src/features/automations/__tests__/cron-runner.test.ts +267 -267
- package/src/features/automations/__tests__/cron-runner.utils.test.ts +144 -144
- package/src/features/automations/__tests__/set-variable.executor.test.ts +383 -383
- package/src/features/automations/__tests__/template-grammar.test.ts +308 -308
- package/src/features/automations/__tests__/webhook.executor.test.ts +142 -142
- package/src/features/automations/__tests__/when-evaluator.test.ts +274 -274
- package/src/features/automations/__tests__/when-pushdown.test.ts +281 -281
- package/src/features/automations/action-executors/create-entry.executor.ts +27 -27
- package/src/features/automations/action-executors/edit-field.executor.ts +26 -26
- package/src/features/automations/action-executors/index.ts +37 -37
- package/src/features/automations/action-executors/send-mail.executor.ts +53 -53
- package/src/features/automations/action-executors/set-variable.executor.ts +159 -159
- package/src/features/automations/action-executors/webhook.executor.ts +67 -67
- package/src/features/automations/automation-runner.ts +85 -85
- package/src/features/automations/automation-runner.utils.ts +47 -47
- package/src/features/automations/automations.handler.ts +197 -197
- package/src/features/automations/automations.schema.ts +177 -177
- package/src/features/automations/context-resolver.ts +152 -152
- package/src/features/automations/cron-runner.ts +140 -140
- package/src/features/automations/cron-runner.utils.ts +44 -44
- package/src/features/automations/filter-translation.ts +46 -46
- package/src/features/automations/index.ts +16 -16
- package/src/features/automations/template-grammar.ts +245 -245
- package/src/features/automations/var-access-resolver.ts +140 -140
- package/src/features/automations/when-evaluator.ts +87 -87
- package/src/features/automations/when-pushdown.ts +57 -57
- package/src/features/backrefs/__tests__/backrefs.handler.test.ts +359 -359
- package/src/features/backrefs/backrefs.handler.ts +168 -168
- package/src/features/backrefs/d1-backref.repository.ts +84 -84
- package/src/features/backrefs/index.ts +5 -5
- package/src/features/content/constants.ts +16 -16
- package/src/features/content/handlers/bulk.handler.ts +185 -185
- package/src/features/content/handlers/create.ts +129 -129
- package/src/features/content/handlers/delete.ts +62 -62
- package/src/features/content/handlers/facets.ts +49 -49
- package/src/features/content/handlers/get.ts +120 -120
- package/src/features/content/handlers/helpers.test.ts +180 -180
- package/src/features/content/handlers/helpers.ts +93 -93
- package/src/features/content/handlers/list.ts +174 -174
- package/src/features/content/handlers/update.ts +174 -174
- package/src/features/content/index.ts +26 -26
- package/src/features/dashboard-layout/__tests__/dashboard-layout.handler.test.ts +388 -0
- package/src/features/dashboard-layout/dashboard-layout.handler.ts +206 -0
- package/src/features/dashboard-layout/index.ts +5 -0
- package/src/features/draft/draft.handler.ts +195 -195
- package/src/features/draft/draft.middleware.ts +66 -66
- package/src/features/draft/index.ts +5 -5
- package/src/features/email/email.provider.ts +42 -42
- package/src/features/email/email.service.ts +98 -98
- package/src/features/email/email.types.ts +119 -119
- package/src/features/email/index.ts +33 -33
- package/src/features/email/providers/resend.ts +67 -67
- package/src/features/email/providers/smtp.ts +55 -55
- package/src/features/email/templates/automation-mail.ts +19 -19
- package/src/features/email/templates/password-changed.ts +63 -63
- package/src/features/email/templates/password-reset.ts +68 -68
- package/src/features/email/templates/shell.ts +96 -96
- package/src/features/notifications/index.ts +5 -5
- package/src/features/notifications/notifications.handler.ts +105 -105
- package/src/features/password-reset/index.ts +19 -19
- package/src/features/password-reset/request.ts +92 -92
- package/src/features/password-reset/reset.ts +102 -102
- package/src/features/rotate-field/index.ts +5 -5
- package/src/features/rotate-field/rotate-field.handler.ts +132 -132
- package/src/features/rotate-field/rotate-field.schema.ts +17 -17
- package/src/features/schema/schema.handler.ts +131 -131
- package/src/features/seeds/index.ts +5 -5
- package/src/features/seeds/seeds.handler.test.ts +1199 -632
- package/src/features/seeds/seeds.handler.ts +558 -693
- package/src/features/settings/__tests__/settings.handler.test.ts +555 -555
- package/src/features/settings/settings.handler.ts +392 -392
- package/src/features/setup/index.ts +288 -204
- package/src/features/stats/index.ts +5 -5
- package/src/features/stats/stats.handler.ts +420 -420
- package/src/features/webhooks/index.ts +63 -63
- package/src/index.ts +65 -65
- package/src/media-utils.ts +82 -82
- package/src/middleware/auth-providers.middleware.test.ts +85 -0
- package/src/middleware/auth-providers.middleware.ts +45 -45
- package/src/middleware/observability.middleware.test.ts +93 -0
- package/src/middleware/observability.middleware.ts +70 -70
- package/src/middleware/rate-limit.middleware.ts +45 -45
- package/src/middleware/repository.middleware.ts +117 -114
- package/src/middleware/seed-registry.middleware.test.ts +97 -97
- package/src/middleware/seed-registry.middleware.ts +21 -21
- package/src/middleware/storage.middleware.ts +25 -25
- package/src/middleware.ts +45 -45
- package/src/public/access-policy.ts +27 -27
- package/src/public/api-key-middleware.ts +57 -57
- package/src/public/cache-utils.ts +38 -38
- package/src/public/entry-projection.ts +46 -46
- package/src/public/idempotency.ts +23 -23
- package/src/public/index.ts +16 -16
- package/src/public/problem-details.ts +124 -124
- package/src/public/public-add.ts +114 -114
- package/src/public/public-edit.ts +163 -163
- package/src/public/public-errors.ts +19 -19
- package/src/public/public-read.ts +63 -63
- package/src/public/public-routes.ts +88 -88
- package/src/public/query-builder.test.ts +224 -224
- package/src/public/query-builder.ts +156 -156
- package/src/public/rate-limit-middleware.ts +34 -34
- package/src/public/read-list.ts +54 -54
- package/src/public/read-single.ts +48 -48
- package/src/public/response-builder.ts +30 -30
- package/src/public/sanitize.ts +69 -69
- package/src/public/slug-utils.ts +18 -18
- package/src/rate-limit/cloudflare-rate-limiter.test.ts +30 -30
- package/src/rate-limit/cloudflare-rate-limiter.ts +15 -15
- package/src/rate-limit/in-memory-rate-limiter.test.ts +37 -37
- package/src/rate-limit/in-memory-rate-limiter.ts +17 -17
- package/src/rate-limit/no-op-rate-limiter.test.ts +22 -22
- package/src/rate-limit/no-op-rate-limiter.ts +11 -11
- package/src/search-utils.test.ts +211 -211
- package/src/search-utils.ts +213 -213
- package/src/search.ts +65 -65
- package/src/shared/apply-policies.test.ts +81 -81
- package/src/shared/apply-policies.ts +67 -67
- package/src/shared/automations.repository.d1.ts +150 -150
- package/src/shared/background-notification-service.test.ts +62 -62
- package/src/shared/background-notification-service.ts +52 -52
- package/src/shared/base.repository.d1.ts +32 -32
- package/src/shared/content-utils.test.ts +165 -165
- package/src/shared/content-utils.ts +86 -86
- package/src/shared/content.repository.d1.test.ts +507 -458
- package/src/shared/content.repository.d1.ts +851 -851
- package/src/shared/d1-activity-log.repository.test.ts +140 -140
- package/src/shared/d1-activity-log.repository.ts +105 -105
- package/src/shared/d1-activity-logger.test.ts +86 -86
- package/src/shared/d1-activity-logger.ts +67 -67
- package/src/shared/d1-analytics.repository.test.ts +78 -78
- package/src/shared/d1-analytics.repository.ts +85 -85
- package/src/shared/d1-content-scan.repository.test.ts +80 -80
- package/src/shared/d1-content-scan.repository.ts +37 -37
- package/src/shared/d1-notification.repository.test.ts +128 -128
- package/src/shared/d1-notification.repository.ts +118 -118
- package/src/shared/d1-password-reset-token.repository.test.ts +81 -81
- package/src/shared/d1-password-reset-token.repository.ts +56 -56
- package/src/shared/d1-search.repository.test.ts +87 -87
- package/src/shared/d1-search.repository.ts +88 -88
- package/src/shared/d1-session.repository.test.ts +125 -125
- package/src/shared/d1-session.repository.ts +102 -102
- package/src/shared/d1-setup-checklist.repository.ts +36 -36
- package/src/shared/d1-user.repository.test.ts +151 -151
- package/src/shared/d1-user.repository.ts +119 -119
- package/src/shared/d1-widget.repository.test.ts +267 -221
- package/src/shared/d1-widget.repository.ts +385 -341
- package/src/shared/dashboard-layout.repository.d1.test.ts +135 -0
- package/src/shared/dashboard-layout.repository.d1.ts +58 -0
- package/src/shared/demo-data-sql.ts +216 -54
- package/src/shared/demo-data.repository.d1.ts +20 -20
- package/src/shared/execution-context-scheduler.ts +13 -13
- package/src/shared/fixed-clock.ts +25 -25
- package/src/shared/fts-sync.ts +8 -8
- package/src/shared/idempotency.repository.d1.test.ts +83 -83
- package/src/shared/idempotency.repository.d1.ts +60 -60
- package/src/shared/in-memory-activity-logger.ts +19 -19
- package/src/shared/in-memory-notification-service.ts +19 -19
- package/src/shared/in-memory-seed.repository.ts +58 -51
- package/src/shared/media.repository.d1.test.ts +107 -107
- package/src/shared/media.repository.d1.ts +68 -68
- package/src/shared/qstash-notification-service.test.ts +67 -67
- package/src/shared/qstash-notification-service.ts +55 -55
- package/src/shared/query-utils.ts +141 -141
- package/src/shared/request-utils.ts +26 -26
- package/src/shared/schema-mutator.d1.test.ts +142 -0
- package/src/shared/schema-mutator.d1.ts +64 -64
- package/src/shared/seed-layout.repository.d1.test.ts +114 -114
- package/src/shared/seed-layout.repository.d1.ts +62 -62
- package/src/shared/seed-registry-cache.test.ts +68 -68
- package/src/shared/seed-registry-cache.ts +49 -49
- package/src/shared/seed.repository.d1.test.ts +143 -143
- package/src/shared/seed.repository.d1.ts +98 -98
- package/src/shared/sequential-id-generator.ts +33 -33
- package/src/shared/site-settings.repository.d1.ts +53 -53
- package/src/shared/storage/factory.ts +41 -41
- package/src/shared/storage/s3-bucket.ts +195 -195
- package/src/shared/storage-utils.ts +40 -40
- package/src/shared/system-stats.repository.d1.test.ts +58 -58
- package/src/shared/system-stats.repository.d1.ts +48 -48
- package/src/types.ts +90 -87
- package/src/upload.ts +198 -198
- package/src/widget.test.ts +85 -0
- package/src/widget.ts +251 -212
- package/assets/dashboard/assets/index-BSjk0Sx9.js +0 -634
- package/assets/dashboard/assets/index-kRCYVN2B.css +0 -1
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
<svg width="784" height="869" viewBox="0 0 784 869" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<g clip-path="url(#clip0_1_2)">
|
|
3
|
-
<path d="M533.24 538.196C960.679 824.193 821.76 930.762 266.62 833.7C121.57 808.34 0 701.399 0 538.196C0 374.994 119.37 242.692 266.62 242.692C413.87 242.692 397.6 447.44 533.24 538.196Z" fill="#3F3D56"/>
|
|
4
|
-
<path opacity="0.1" d="M571.456 816.814C664.715 816.814 740.315 810.05 740.315 801.706C740.315 793.362 664.715 786.597 571.456 786.597C478.197 786.597 402.597 793.362 402.597 801.706C402.597 810.05 478.197 816.814 571.456 816.814Z" fill="black"/>
|
|
5
|
-
<path opacity="0.1" d="M238.428 318.814C287.857 318.814 327.928 312.05 327.928 303.706C327.928 295.362 287.857 288.598 238.428 288.598C188.998 288.598 148.928 295.362 148.928 303.706C148.928 312.05 188.998 318.814 238.428 318.814Z" fill="black"/>
|
|
6
|
-
<path opacity="0.1" d="M233.737 340.899C195.335 340.891 157.715 351.748 125.221 372.214C92.7278 392.68 66.687 421.921 50.1069 456.559C69.1658 435.504 92.43 418.681 118.396 407.178C144.363 395.675 172.454 389.748 200.854 389.779C313.252 389.779 404.374 480.901 404.374 593.3C404.42 623.708 397.622 653.736 384.484 681.16C418.509 643.754 437.33 594.985 437.257 544.419C437.257 432.021 346.135 340.899 233.737 340.899Z" fill="black"/>
|
|
7
|
-
<path opacity="0.3" d="M404.374 592.411C404.42 622.819 397.622 652.848 384.484 680.271C365.425 701.326 342.161 718.149 316.195 729.652C290.228 741.155 262.137 747.082 233.737 747.051C121.339 747.051 30.217 655.929 30.217 543.53C30.1711 513.122 36.969 483.094 50.1069 455.67C69.1658 434.615 92.43 417.792 118.396 406.289C144.363 394.786 172.454 388.859 200.854 388.891C313.252 388.891 404.374 480.013 404.374 592.411Z" fill="#D5A889"/>
|
|
8
|
-
<path d="M233.737 748.05C120.964 748.05 29.2168 656.303 29.2168 543.531C29.1348 512.969 35.9682 482.784 49.2051 455.238C65.8668 420.43 92.0355 391.045 124.689 370.479C157.342 349.912 195.147 339.003 233.737 339.01C346.51 339.01 438.257 430.758 438.257 543.531C438.259 570.389 432.97 596.985 422.693 621.799C412.415 646.613 397.35 669.16 378.359 688.152C359.367 707.144 336.82 722.208 312.006 732.486C287.191 742.763 260.596 748.052 233.737 748.05ZM233.737 341.01C156.248 341.01 84.5224 386.187 51.0088 456.103C37.9014 483.38 31.1352 513.268 31.2168 543.531C31.2168 655.201 122.067 746.05 233.737 746.05C260.333 746.052 286.669 740.815 311.24 730.638C335.812 720.461 358.139 705.544 376.945 686.738C395.751 667.932 410.668 645.606 420.845 621.034C431.022 596.462 436.259 570.127 436.257 543.531C436.257 431.86 345.407 341.01 233.737 341.01Z" fill="white"/>
|
|
9
|
-
<path d="M699.356 750.635C684.574 769.966 686.264 796.093 686.264 796.093C686.264 796.093 711.92 790.876 726.702 771.544C741.484 752.213 739.793 726.086 739.793 726.086C739.793 726.086 714.137 731.303 699.356 750.635Z" fill="#D0CDE1"/>
|
|
10
|
-
<path d="M709.042 754.976C706.174 779.142 686.756 796.703 686.756 796.703C686.756 796.703 671.99 775.084 674.858 750.919C677.727 726.753 697.144 709.191 697.144 709.191C697.144 709.191 711.911 730.811 709.042 754.976Z" fill="#D5A889"/>
|
|
11
|
-
<path d="M354.901 190.533C354.942 274.417 305.088 303.727 243.578 303.756C242.149 303.757 240.727 303.742 239.312 303.711C236.461 303.651 233.64 303.522 230.849 303.325C175.332 299.421 132.182 268.653 132.144 190.641C132.105 109.908 235.238 7.97471 242.972 0.4377C242.979 0.4377 242.979 0.437699 242.986 0.430849C243.28 0.143599 243.43 0 243.43 0C243.43 0 354.861 106.655 354.901 190.533Z" fill="#D5A889"/>
|
|
12
|
-
<path d="M239.518 290.927L280.226 233.991L239.418 297.175L239.312 303.711C236.461 303.651 233.64 303.522 230.849 303.325L235.197 219.405L235.162 218.755L235.237 218.632L235.651 210.702L194.679 147.4L235.771 204.762L235.874 206.443L239.159 143.038L204.079 77.6211L239.584 131.895L242.972 0.437694L242.986 0.000213623L242.986 0.430842L242.462 104.099L277.326 62.9838L242.316 113.027L241.421 169.8L273.981 115.301L241.295 178.147L240.798 209.715L288.052 133.854L240.618 220.728L239.518 290.927Z" fill="#3F3D56"/>
|
|
13
|
-
<path d="M572.956 238.594C562.281 220.557 541.162 219.716 541.162 219.716C541.162 219.716 520.583 217.085 507.382 244.555C495.077 270.159 478.095 294.881 504.648 300.875L509.444 285.947L512.414 301.986C516.195 302.258 519.988 302.323 523.775 302.181C552.211 301.263 579.292 302.449 578.42 292.245C577.261 278.68 583.227 255.949 572.956 238.594Z" fill="#2F2E41"/>
|
|
14
|
-
<path d="M525.862 407.843L528.608 435.301L592.22 428.437L593.593 407.843H525.862Z" fill="#A0616A"/>
|
|
15
|
-
<path d="M518.998 422.945L517.167 438.962C517.167 438.962 496.116 468.251 497.946 518.592C499.777 568.933 469.115 792.72 491.081 794.55C513.048 796.381 552.405 799.127 554.236 792.262C556.067 785.398 559.728 524.999 559.728 524.999C559.728 524.999 569.338 792.587 585.813 793.502C602.289 794.418 657.663 796.248 657.663 789.384C657.663 782.519 609.153 476.031 609.153 476.031L601.373 450.403L593.593 418.826C593.593 418.826 545.083 427.064 531.812 423.86C518.54 420.657 518.998 422.945 518.998 422.945Z" fill="#2F2E41"/>
|
|
16
|
-
<path d="M540.964 296.178C557.14 296.178 570.254 283.065 570.254 266.889C570.254 250.713 557.14 237.6 540.964 237.6C524.789 237.6 511.675 250.713 511.675 266.889C511.675 283.065 524.789 296.178 540.964 296.178Z" fill="#A0616A"/>
|
|
17
|
-
<path d="M592.22 315.399L586.66 323.49L583.1 328.666L577.118 337.366L547.371 339.654C547.371 339.654 540.145 337.32 531.807 333.911C528.727 332.648 525.492 331.238 522.416 329.742C510.485 323.948 500.912 316.877 511.675 312.195C513.587 311.375 515.423 310.389 517.162 309.248C518.745 308.214 520.219 307.021 521.56 305.688C524.318 302.925 526.359 299.531 527.508 295.8C528.656 292.069 528.877 288.115 528.15 284.279L556.982 280.161C556.144 287.474 563.416 295.45 571.567 301.963C574.244 304.109 577.022 306.091 579.635 307.839C586.486 312.424 592.22 315.399 592.22 315.399Z" fill="#A0616A"/>
|
|
18
|
-
<path d="M552.863 324.094C552.863 324.094 524.718 319.289 518.311 309.221C518.311 309.221 490.624 312.653 492.912 327.298C495.2 341.942 518.082 422.03 518.082 422.03C518.082 422.03 575.288 411.046 596.339 419.284L609.153 317.23C609.153 317.23 604.577 307.161 595.882 309.907C587.186 312.653 583.754 310.136 583.754 310.136C583.754 310.136 573.915 324.094 552.863 324.094Z" fill="#D0CDE1"/>
|
|
19
|
-
<path d="M578.491 484.727C578.491 484.727 598.17 506.693 587.186 510.355C576.203 514.016 562.931 495.71 562.931 495.71L578.491 484.727Z" fill="#A0616A"/>
|
|
20
|
-
<path d="M584.898 486.557C584.898 486.557 568.881 497.541 574.83 504.863C580.779 512.185 601.373 498.456 601.373 495.71C601.373 492.964 584.898 486.557 584.898 486.557Z" fill="#A0616A"/>
|
|
21
|
-
<path d="M545.541 427.064L532.269 429.352L523.574 364.824C524.076 353.118 523.689 341.39 522.416 329.742C521.446 321.962 519.826 314.484 517.162 309.248C518.745 308.214 520.219 307.021 521.56 305.688C524.746 310.736 530.558 321.426 531.807 333.911C532.134 337.037 532.135 340.189 531.812 343.315C529.523 364.824 529.981 368.943 529.981 368.943L545.541 427.064Z" fill="#2F2E41"/>
|
|
22
|
-
<path d="M514.879 313.111C514.879 313.111 493.37 307.161 491.539 319.975C489.709 332.789 491.997 365.74 497.031 378.096C502.065 390.452 519.455 430.725 519.455 430.725C519.455 430.725 551.033 470.997 552.406 480.608C553.778 490.218 560.643 500.286 560.643 500.286L582.61 486.1L539.591 413.334L520.828 362.994C520.828 362.994 528.608 317.23 514.879 313.111Z" fill="#D0CDE1"/>
|
|
23
|
-
<path d="M599.085 314.941L609.153 317.23C609.153 317.23 618.306 356.587 614.645 377.638C610.984 398.69 610.526 406.012 610.526 406.012L607.323 465.963C607.323 465.963 608.238 504.405 601.831 498.456C595.424 492.507 582.61 489.761 582.61 489.761C582.61 489.761 583.983 472.828 589.017 468.709C589.017 468.709 594.966 437.132 591.305 423.403C587.644 409.673 599.085 314.941 599.085 314.941Z" fill="#D0CDE1"/>
|
|
24
|
-
<path d="M602.746 359.333L591.076 426.835L583.983 423.86L596.339 359.333L583.1 328.666L571.567 301.963C574.244 304.109 577.022 306.091 579.635 307.839L586.66 323.49L602.746 359.333Z" fill="#2F2E41"/>
|
|
25
|
-
<path d="M568.236 236.855L547.372 225.926L518.559 230.397L512.598 256.726L527.437 256.155L531.583 246.482V255.996L538.43 255.732L542.404 240.332L544.888 256.726L569.23 256.229L568.236 236.855Z" fill="#2F2E41"/>
|
|
26
|
-
<path d="M527.019 801.706C534.873 801.706 541.239 799.319 541.239 796.374C541.239 793.429 534.873 791.041 527.019 791.041C519.166 791.041 512.8 793.429 512.8 796.374C512.8 799.319 519.166 801.706 527.019 801.706Z" fill="#2F2E41"/>
|
|
27
|
-
<path d="M623.003 801.706C630.856 801.706 637.222 799.319 637.222 796.374C637.222 793.429 630.856 791.041 623.003 791.041C615.149 791.041 608.783 793.429 608.783 796.374C608.783 799.319 615.149 801.706 623.003 801.706Z" fill="#2F2E41"/>
|
|
28
|
-
<path d="M263.546 648.795C249 658.26 232.238 663.769 214.913 664.778C197.589 665.788 180.3 662.262 164.754 654.549L166.955 650.059C185.694 659.265 206.933 662.076 227.422 658.063C247.91 654.049 266.519 643.433 280.399 627.838C294.28 612.242 302.668 592.528 304.279 571.713C305.89 550.897 300.635 530.127 289.319 512.582C278.003 495.037 261.249 481.683 241.623 474.566C221.996 467.448 200.577 466.958 180.645 473.17C160.713 479.383 143.366 491.956 131.259 508.966C119.153 525.975 112.954 546.483 113.611 567.35L108.613 567.503C108.059 549.946 112.117 532.549 120.382 517.048C128.648 501.548 140.831 488.484 155.719 479.159C170.606 469.835 187.678 464.575 205.231 463.905C222.785 463.234 240.208 467.176 255.764 475.338C271.319 483.5 284.464 495.596 293.887 510.421C303.311 525.246 308.684 542.282 309.471 559.831C310.258 577.38 306.432 594.829 298.374 610.439C290.316 626.048 278.308 639.273 263.546 648.795Z" fill="white"/>
|
|
29
|
-
<path d="M218.318 558.375C203.532 561.648 183.855 563.642 169.517 561.56L187.516 575.266L137.128 607.765L139.838 611.967L190.226 579.468L195.292 601.522C199.308 587.602 209.241 570.499 218.318 558.375Z" fill="white"/>
|
|
30
|
-
</g>
|
|
31
|
-
<defs>
|
|
32
|
-
<clipPath id="clip0_1_2">
|
|
33
|
-
<rect width="783.928" height="868.734" fill="white"/>
|
|
34
|
-
</clipPath>
|
|
35
|
-
</defs>
|
|
36
|
-
</svg>
|
|
1
|
+
<svg width="784" height="869" viewBox="0 0 784 869" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_1_2)">
|
|
3
|
+
<path d="M533.24 538.196C960.679 824.193 821.76 930.762 266.62 833.7C121.57 808.34 0 701.399 0 538.196C0 374.994 119.37 242.692 266.62 242.692C413.87 242.692 397.6 447.44 533.24 538.196Z" fill="#3F3D56"/>
|
|
4
|
+
<path opacity="0.1" d="M571.456 816.814C664.715 816.814 740.315 810.05 740.315 801.706C740.315 793.362 664.715 786.597 571.456 786.597C478.197 786.597 402.597 793.362 402.597 801.706C402.597 810.05 478.197 816.814 571.456 816.814Z" fill="black"/>
|
|
5
|
+
<path opacity="0.1" d="M238.428 318.814C287.857 318.814 327.928 312.05 327.928 303.706C327.928 295.362 287.857 288.598 238.428 288.598C188.998 288.598 148.928 295.362 148.928 303.706C148.928 312.05 188.998 318.814 238.428 318.814Z" fill="black"/>
|
|
6
|
+
<path opacity="0.1" d="M233.737 340.899C195.335 340.891 157.715 351.748 125.221 372.214C92.7278 392.68 66.687 421.921 50.1069 456.559C69.1658 435.504 92.43 418.681 118.396 407.178C144.363 395.675 172.454 389.748 200.854 389.779C313.252 389.779 404.374 480.901 404.374 593.3C404.42 623.708 397.622 653.736 384.484 681.16C418.509 643.754 437.33 594.985 437.257 544.419C437.257 432.021 346.135 340.899 233.737 340.899Z" fill="black"/>
|
|
7
|
+
<path opacity="0.3" d="M404.374 592.411C404.42 622.819 397.622 652.848 384.484 680.271C365.425 701.326 342.161 718.149 316.195 729.652C290.228 741.155 262.137 747.082 233.737 747.051C121.339 747.051 30.217 655.929 30.217 543.53C30.1711 513.122 36.969 483.094 50.1069 455.67C69.1658 434.615 92.43 417.792 118.396 406.289C144.363 394.786 172.454 388.859 200.854 388.891C313.252 388.891 404.374 480.013 404.374 592.411Z" fill="#D5A889"/>
|
|
8
|
+
<path d="M233.737 748.05C120.964 748.05 29.2168 656.303 29.2168 543.531C29.1348 512.969 35.9682 482.784 49.2051 455.238C65.8668 420.43 92.0355 391.045 124.689 370.479C157.342 349.912 195.147 339.003 233.737 339.01C346.51 339.01 438.257 430.758 438.257 543.531C438.259 570.389 432.97 596.985 422.693 621.799C412.415 646.613 397.35 669.16 378.359 688.152C359.367 707.144 336.82 722.208 312.006 732.486C287.191 742.763 260.596 748.052 233.737 748.05ZM233.737 341.01C156.248 341.01 84.5224 386.187 51.0088 456.103C37.9014 483.38 31.1352 513.268 31.2168 543.531C31.2168 655.201 122.067 746.05 233.737 746.05C260.333 746.052 286.669 740.815 311.24 730.638C335.812 720.461 358.139 705.544 376.945 686.738C395.751 667.932 410.668 645.606 420.845 621.034C431.022 596.462 436.259 570.127 436.257 543.531C436.257 431.86 345.407 341.01 233.737 341.01Z" fill="white"/>
|
|
9
|
+
<path d="M699.356 750.635C684.574 769.966 686.264 796.093 686.264 796.093C686.264 796.093 711.92 790.876 726.702 771.544C741.484 752.213 739.793 726.086 739.793 726.086C739.793 726.086 714.137 731.303 699.356 750.635Z" fill="#D0CDE1"/>
|
|
10
|
+
<path d="M709.042 754.976C706.174 779.142 686.756 796.703 686.756 796.703C686.756 796.703 671.99 775.084 674.858 750.919C677.727 726.753 697.144 709.191 697.144 709.191C697.144 709.191 711.911 730.811 709.042 754.976Z" fill="#D5A889"/>
|
|
11
|
+
<path d="M354.901 190.533C354.942 274.417 305.088 303.727 243.578 303.756C242.149 303.757 240.727 303.742 239.312 303.711C236.461 303.651 233.64 303.522 230.849 303.325C175.332 299.421 132.182 268.653 132.144 190.641C132.105 109.908 235.238 7.97471 242.972 0.4377C242.979 0.4377 242.979 0.437699 242.986 0.430849C243.28 0.143599 243.43 0 243.43 0C243.43 0 354.861 106.655 354.901 190.533Z" fill="#D5A889"/>
|
|
12
|
+
<path d="M239.518 290.927L280.226 233.991L239.418 297.175L239.312 303.711C236.461 303.651 233.64 303.522 230.849 303.325L235.197 219.405L235.162 218.755L235.237 218.632L235.651 210.702L194.679 147.4L235.771 204.762L235.874 206.443L239.159 143.038L204.079 77.6211L239.584 131.895L242.972 0.437694L242.986 0.000213623L242.986 0.430842L242.462 104.099L277.326 62.9838L242.316 113.027L241.421 169.8L273.981 115.301L241.295 178.147L240.798 209.715L288.052 133.854L240.618 220.728L239.518 290.927Z" fill="#3F3D56"/>
|
|
13
|
+
<path d="M572.956 238.594C562.281 220.557 541.162 219.716 541.162 219.716C541.162 219.716 520.583 217.085 507.382 244.555C495.077 270.159 478.095 294.881 504.648 300.875L509.444 285.947L512.414 301.986C516.195 302.258 519.988 302.323 523.775 302.181C552.211 301.263 579.292 302.449 578.42 292.245C577.261 278.68 583.227 255.949 572.956 238.594Z" fill="#2F2E41"/>
|
|
14
|
+
<path d="M525.862 407.843L528.608 435.301L592.22 428.437L593.593 407.843H525.862Z" fill="#A0616A"/>
|
|
15
|
+
<path d="M518.998 422.945L517.167 438.962C517.167 438.962 496.116 468.251 497.946 518.592C499.777 568.933 469.115 792.72 491.081 794.55C513.048 796.381 552.405 799.127 554.236 792.262C556.067 785.398 559.728 524.999 559.728 524.999C559.728 524.999 569.338 792.587 585.813 793.502C602.289 794.418 657.663 796.248 657.663 789.384C657.663 782.519 609.153 476.031 609.153 476.031L601.373 450.403L593.593 418.826C593.593 418.826 545.083 427.064 531.812 423.86C518.54 420.657 518.998 422.945 518.998 422.945Z" fill="#2F2E41"/>
|
|
16
|
+
<path d="M540.964 296.178C557.14 296.178 570.254 283.065 570.254 266.889C570.254 250.713 557.14 237.6 540.964 237.6C524.789 237.6 511.675 250.713 511.675 266.889C511.675 283.065 524.789 296.178 540.964 296.178Z" fill="#A0616A"/>
|
|
17
|
+
<path d="M592.22 315.399L586.66 323.49L583.1 328.666L577.118 337.366L547.371 339.654C547.371 339.654 540.145 337.32 531.807 333.911C528.727 332.648 525.492 331.238 522.416 329.742C510.485 323.948 500.912 316.877 511.675 312.195C513.587 311.375 515.423 310.389 517.162 309.248C518.745 308.214 520.219 307.021 521.56 305.688C524.318 302.925 526.359 299.531 527.508 295.8C528.656 292.069 528.877 288.115 528.15 284.279L556.982 280.161C556.144 287.474 563.416 295.45 571.567 301.963C574.244 304.109 577.022 306.091 579.635 307.839C586.486 312.424 592.22 315.399 592.22 315.399Z" fill="#A0616A"/>
|
|
18
|
+
<path d="M552.863 324.094C552.863 324.094 524.718 319.289 518.311 309.221C518.311 309.221 490.624 312.653 492.912 327.298C495.2 341.942 518.082 422.03 518.082 422.03C518.082 422.03 575.288 411.046 596.339 419.284L609.153 317.23C609.153 317.23 604.577 307.161 595.882 309.907C587.186 312.653 583.754 310.136 583.754 310.136C583.754 310.136 573.915 324.094 552.863 324.094Z" fill="#D0CDE1"/>
|
|
19
|
+
<path d="M578.491 484.727C578.491 484.727 598.17 506.693 587.186 510.355C576.203 514.016 562.931 495.71 562.931 495.71L578.491 484.727Z" fill="#A0616A"/>
|
|
20
|
+
<path d="M584.898 486.557C584.898 486.557 568.881 497.541 574.83 504.863C580.779 512.185 601.373 498.456 601.373 495.71C601.373 492.964 584.898 486.557 584.898 486.557Z" fill="#A0616A"/>
|
|
21
|
+
<path d="M545.541 427.064L532.269 429.352L523.574 364.824C524.076 353.118 523.689 341.39 522.416 329.742C521.446 321.962 519.826 314.484 517.162 309.248C518.745 308.214 520.219 307.021 521.56 305.688C524.746 310.736 530.558 321.426 531.807 333.911C532.134 337.037 532.135 340.189 531.812 343.315C529.523 364.824 529.981 368.943 529.981 368.943L545.541 427.064Z" fill="#2F2E41"/>
|
|
22
|
+
<path d="M514.879 313.111C514.879 313.111 493.37 307.161 491.539 319.975C489.709 332.789 491.997 365.74 497.031 378.096C502.065 390.452 519.455 430.725 519.455 430.725C519.455 430.725 551.033 470.997 552.406 480.608C553.778 490.218 560.643 500.286 560.643 500.286L582.61 486.1L539.591 413.334L520.828 362.994C520.828 362.994 528.608 317.23 514.879 313.111Z" fill="#D0CDE1"/>
|
|
23
|
+
<path d="M599.085 314.941L609.153 317.23C609.153 317.23 618.306 356.587 614.645 377.638C610.984 398.69 610.526 406.012 610.526 406.012L607.323 465.963C607.323 465.963 608.238 504.405 601.831 498.456C595.424 492.507 582.61 489.761 582.61 489.761C582.61 489.761 583.983 472.828 589.017 468.709C589.017 468.709 594.966 437.132 591.305 423.403C587.644 409.673 599.085 314.941 599.085 314.941Z" fill="#D0CDE1"/>
|
|
24
|
+
<path d="M602.746 359.333L591.076 426.835L583.983 423.86L596.339 359.333L583.1 328.666L571.567 301.963C574.244 304.109 577.022 306.091 579.635 307.839L586.66 323.49L602.746 359.333Z" fill="#2F2E41"/>
|
|
25
|
+
<path d="M568.236 236.855L547.372 225.926L518.559 230.397L512.598 256.726L527.437 256.155L531.583 246.482V255.996L538.43 255.732L542.404 240.332L544.888 256.726L569.23 256.229L568.236 236.855Z" fill="#2F2E41"/>
|
|
26
|
+
<path d="M527.019 801.706C534.873 801.706 541.239 799.319 541.239 796.374C541.239 793.429 534.873 791.041 527.019 791.041C519.166 791.041 512.8 793.429 512.8 796.374C512.8 799.319 519.166 801.706 527.019 801.706Z" fill="#2F2E41"/>
|
|
27
|
+
<path d="M623.003 801.706C630.856 801.706 637.222 799.319 637.222 796.374C637.222 793.429 630.856 791.041 623.003 791.041C615.149 791.041 608.783 793.429 608.783 796.374C608.783 799.319 615.149 801.706 623.003 801.706Z" fill="#2F2E41"/>
|
|
28
|
+
<path d="M263.546 648.795C249 658.26 232.238 663.769 214.913 664.778C197.589 665.788 180.3 662.262 164.754 654.549L166.955 650.059C185.694 659.265 206.933 662.076 227.422 658.063C247.91 654.049 266.519 643.433 280.399 627.838C294.28 612.242 302.668 592.528 304.279 571.713C305.89 550.897 300.635 530.127 289.319 512.582C278.003 495.037 261.249 481.683 241.623 474.566C221.996 467.448 200.577 466.958 180.645 473.17C160.713 479.383 143.366 491.956 131.259 508.966C119.153 525.975 112.954 546.483 113.611 567.35L108.613 567.503C108.059 549.946 112.117 532.549 120.382 517.048C128.648 501.548 140.831 488.484 155.719 479.159C170.606 469.835 187.678 464.575 205.231 463.905C222.785 463.234 240.208 467.176 255.764 475.338C271.319 483.5 284.464 495.596 293.887 510.421C303.311 525.246 308.684 542.282 309.471 559.831C310.258 577.38 306.432 594.829 298.374 610.439C290.316 626.048 278.308 639.273 263.546 648.795Z" fill="white"/>
|
|
29
|
+
<path d="M218.318 558.375C203.532 561.648 183.855 563.642 169.517 561.56L187.516 575.266L137.128 607.765L139.838 611.967L190.226 579.468L195.292 601.522C199.308 587.602 209.241 570.499 218.318 558.375Z" fill="white"/>
|
|
30
|
+
</g>
|
|
31
|
+
<defs>
|
|
32
|
+
<clipPath id="clip0_1_2">
|
|
33
|
+
<rect width="783.928" height="868.734" fill="white"/>
|
|
34
|
+
</clipPath>
|
|
35
|
+
</defs>
|
|
36
|
+
</svg>
|
|
@@ -1,265 +1,265 @@
|
|
|
1
|
-
-- =============================================================================
|
|
2
|
-
-- Beech CMS v0.4.0 — Base Schema
|
|
3
|
-
--
|
|
4
|
-
-- Single migration for fresh installations. Creates all system tables.
|
|
5
|
-
--
|
|
6
|
-
-- Content tables (content_{slug}, fts_{slug}, content_{slug}_drafts) are
|
|
7
|
-
-- NOT defined here — they are generated at deploy time by: beech seed:load
|
|
8
|
-
-- FTS indexes text + richtext branches (policies.search !== false).
|
|
9
|
-
--
|
|
10
|
-
-- Usage:
|
|
11
|
-
-- wrangler d1 execute beech-db --file=migrations/0000_v040_base.sql (remote)
|
|
12
|
-
-- wrangler d1 execute beech-db --local --file=migrations/0000_v040_base.sql (local)
|
|
13
|
-
--
|
|
14
|
-
-- After this migration, run:
|
|
15
|
-
-- beech seed:load → creates content_{slug} tables + FTS + triggers
|
|
16
|
-
-- POST /auth/setup → creates first admin user
|
|
17
|
-
-- =============================================================================
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-- =============================================================================
|
|
21
|
-
-- 1. USERS
|
|
22
|
-
-- Dashboard users (admins + editors). Passwords stored as bcrypt hashes.
|
|
23
|
-
-- Profile fields (name, avatar_url, notification_prefs) merged in from
|
|
24
|
-
-- the start — no incremental ALTER TABLE needed.
|
|
25
|
-
-- =============================================================================
|
|
26
|
-
|
|
27
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
28
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
29
|
-
email TEXT NOT NULL UNIQUE,
|
|
30
|
-
password_hash TEXT NOT NULL,
|
|
31
|
-
role TEXT NOT NULL DEFAULT 'editor' CHECK (role IN ('admin', 'editor')),
|
|
32
|
-
name TEXT,
|
|
33
|
-
surname TEXT,
|
|
34
|
-
avatar_url TEXT,
|
|
35
|
-
notification_prefs TEXT NOT NULL DEFAULT '{}',
|
|
36
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-- =============================================================================
|
|
41
|
-
-- 2. REFRESH TOKENS
|
|
42
|
-
-- JWT refresh token rotation. Stored as SHA-256 hashes, never plaintext.
|
|
43
|
-
-- revoked_at IS NULL = active token.
|
|
44
|
-
-- =============================================================================
|
|
45
|
-
|
|
46
|
-
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
47
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
48
|
-
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
49
|
-
token_hash TEXT NOT NULL,
|
|
50
|
-
expires_at INTEGER NOT NULL,
|
|
51
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
52
|
-
revoked_at INTEGER DEFAULT NULL
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
CREATE INDEX IF NOT EXISTS idx_refresh_user ON refresh_tokens(user_id);
|
|
56
|
-
CREATE INDEX IF NOT EXISTS idx_refresh_hash ON refresh_tokens(token_hash);
|
|
57
|
-
CREATE INDEX IF NOT EXISTS idx_refresh_expires ON refresh_tokens(expires_at);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-- =============================================================================
|
|
61
|
-
-- 3. PASSWORD RESET TOKENS
|
|
62
|
-
-- Single-use, 30-minute TTL, stored as SHA-256 hashes.
|
|
63
|
-
-- used_at set on redemption to prevent replay attacks.
|
|
64
|
-
-- =============================================================================
|
|
65
|
-
|
|
66
|
-
CREATE TABLE IF NOT EXISTS password_reset_tokens (
|
|
67
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
68
|
-
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
69
|
-
token_hash TEXT NOT NULL,
|
|
70
|
-
expires_at INTEGER NOT NULL,
|
|
71
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
72
|
-
used_at INTEGER DEFAULT NULL
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
CREATE INDEX IF NOT EXISTS idx_prt_hash ON password_reset_tokens(token_hash);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_prt_user ON password_reset_tokens(user_id);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-- =============================================================================
|
|
80
|
-
-- 4. PUBLIC API — IDEMPOTENCY KEYS
|
|
81
|
-
-- Deduplicates POST requests from public consumers (e.g. contact forms).
|
|
82
|
-
-- expires_at-based pruning keeps the table small.
|
|
83
|
-
-- =============================================================================
|
|
84
|
-
|
|
85
|
-
CREATE TABLE IF NOT EXISTS public_idempotency_keys (
|
|
86
|
-
idempotency_key TEXT NOT NULL PRIMARY KEY,
|
|
87
|
-
request_fingerprint TEXT NOT NULL,
|
|
88
|
-
response_status INTEGER NOT NULL,
|
|
89
|
-
response_body TEXT NOT NULL,
|
|
90
|
-
created_at INTEGER NOT NULL,
|
|
91
|
-
expires_at INTEGER NOT NULL
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
CREATE INDEX IF NOT EXISTS idx_idempotency_expires ON public_idempotency_keys(expires_at);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
-- =============================================================================
|
|
98
|
-
-- 5. ANALYTICS
|
|
99
|
-
-- Daily metric counters per seed and global (seed = '' sentinel).
|
|
100
|
-
-- Metrics: 'requests' | 'visitors' | 'bandwidth_mb'
|
|
101
|
-
-- =============================================================================
|
|
102
|
-
|
|
103
|
-
CREATE TABLE IF NOT EXISTS analytics (
|
|
104
|
-
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
105
|
-
day_ts INTEGER NOT NULL,
|
|
106
|
-
metric TEXT NOT NULL,
|
|
107
|
-
seed TEXT NOT NULL DEFAULT '',
|
|
108
|
-
value INTEGER NOT NULL DEFAULT 0,
|
|
109
|
-
UNIQUE(day_ts, metric, seed)
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
CREATE INDEX IF NOT EXISTS idx_analytics_day ON analytics(day_ts);
|
|
113
|
-
CREATE INDEX IF NOT EXISTS idx_analytics_seed ON analytics(seed, day_ts);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-- =============================================================================
|
|
117
|
-
-- 6. SYSTEM STATS
|
|
118
|
-
-- Persistent key-value store for system-level counters.
|
|
119
|
-
-- 'total_storage_bytes' is kept for dashboard widgets;
|
|
120
|
-
-- SUM(size_bytes) FROM media_objects is the authoritative source.
|
|
121
|
-
-- =============================================================================
|
|
122
|
-
|
|
123
|
-
CREATE TABLE IF NOT EXISTS system_stats (
|
|
124
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
125
|
-
value TEXT NOT NULL
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
INSERT OR IGNORE INTO system_stats (id, value) VALUES ('total_storage_bytes', '0');
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-- =============================================================================
|
|
132
|
-
-- 7. ACTIVITY LOGS
|
|
133
|
-
-- Append-only audit log. No FK on user_id — deleted users are retained
|
|
134
|
-
-- for compliance. user_name denormalized to survive user deletions.
|
|
135
|
-
-- Actions: 'create' | 'update' | 'delete' | 'upload'
|
|
136
|
-
-- Entity types: 'content' | 'media'
|
|
137
|
-
-- =============================================================================
|
|
138
|
-
|
|
139
|
-
CREATE TABLE IF NOT EXISTS activity_logs (
|
|
140
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
141
|
-
user_id TEXT NOT NULL,
|
|
142
|
-
user_email TEXT NOT NULL,
|
|
143
|
-
user_name TEXT,
|
|
144
|
-
action TEXT NOT NULL,
|
|
145
|
-
entity_type TEXT NOT NULL,
|
|
146
|
-
entity_id TEXT NOT NULL,
|
|
147
|
-
entity_slug TEXT,
|
|
148
|
-
details TEXT,
|
|
149
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
CREATE INDEX IF NOT EXISTS idx_activity_user ON activity_logs(user_id);
|
|
153
|
-
CREATE INDEX IF NOT EXISTS idx_activity_created ON activity_logs(created_at);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
-- =============================================================================
|
|
157
|
-
-- 8. NOTIFICATIONS
|
|
158
|
-
-- In-app notification inbox for system events (e.g. new form submission).
|
|
159
|
-
-- is_read: 0 = unread, 1 = read.
|
|
160
|
-
-- =============================================================================
|
|
161
|
-
|
|
162
|
-
CREATE TABLE IF NOT EXISTS notifications (
|
|
163
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
164
|
-
title TEXT NOT NULL,
|
|
165
|
-
message TEXT NOT NULL,
|
|
166
|
-
type TEXT NOT NULL DEFAULT 'info' CHECK (type IN ('info', 'warning', 'error')),
|
|
167
|
-
is_read INTEGER NOT NULL DEFAULT 0 CHECK (is_read IN (0, 1)),
|
|
168
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
CREATE INDEX IF NOT EXISTS idx_notifications_created ON notifications(created_at);
|
|
172
|
-
CREATE INDEX IF NOT EXISTS idx_notifications_unread ON notifications(is_read);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
-- =============================================================================
|
|
176
|
-
-- 9. MEDIA OBJECTS
|
|
177
|
-
-- Tracks every file uploaded to R2. Enables media library UI, orphan
|
|
178
|
-
-- detection, and storage reporting. No FK on uploaded_by for D1 portability.
|
|
179
|
-
-- =============================================================================
|
|
180
|
-
|
|
181
|
-
CREATE TABLE IF NOT EXISTS media_objects (
|
|
182
|
-
key TEXT NOT NULL PRIMARY KEY,
|
|
183
|
-
filename TEXT NOT NULL,
|
|
184
|
-
mime_type TEXT NOT NULL,
|
|
185
|
-
size_bytes INTEGER NOT NULL,
|
|
186
|
-
uploaded_by TEXT NOT NULL DEFAULT '',
|
|
187
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
CREATE INDEX IF NOT EXISTS idx_media_user ON media_objects(uploaded_by);
|
|
191
|
-
CREATE INDEX IF NOT EXISTS idx_media_created ON media_objects(created_at DESC);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
-- =============================================================================
|
|
195
|
-
-- 10. CONTENT EVENT LOG
|
|
196
|
-
-- Lightweight event log for activity feed and dashboard stats.
|
|
197
|
-
-- Does NOT store field data — only event metadata (who did what, when).
|
|
198
|
-
-- Replaces the legacy content_entries table as cross-seed event source.
|
|
199
|
-
-- The actual content lives in content_{slug} tables (created by seed:load).
|
|
200
|
-
-- =============================================================================
|
|
201
|
-
|
|
202
|
-
CREATE TABLE IF NOT EXISTS content_event_log (
|
|
203
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
204
|
-
schema_slug TEXT NOT NULL,
|
|
205
|
-
entry_id TEXT NOT NULL,
|
|
206
|
-
action TEXT NOT NULL CHECK (action IN ('create', 'update', 'delete')),
|
|
207
|
-
user_id TEXT,
|
|
208
|
-
details TEXT,
|
|
209
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
CREATE INDEX IF NOT EXISTS idx_event_log_schema_slug ON content_event_log(schema_slug);
|
|
213
|
-
CREATE INDEX IF NOT EXISTS idx_event_log_created_at ON content_event_log(created_at DESC);
|
|
214
|
-
CREATE INDEX IF NOT EXISTS idx_event_log_entry_id ON content_event_log(entry_id);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-- =============================================================================
|
|
218
|
-
-- 11. AUTOMATIONS
|
|
219
|
-
-- Event-driven automation rules. Each automation defines one or more
|
|
220
|
-
-- triggers (JSON array of {event, cron?}), optional WhenNode conditions,
|
|
221
|
-
-- and a sequence of actions to execute.
|
|
222
|
-
-- triggers: [{ event: 'create'|'update'|'delete'|'cron', cron?: string }]
|
|
223
|
-
-- Max one cron trigger per automation, enforced at the application layer.
|
|
224
|
-
-- =============================================================================
|
|
225
|
-
|
|
226
|
-
CREATE TABLE IF NOT EXISTS automations (
|
|
227
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
228
|
-
seed_slug TEXT NOT NULL,
|
|
229
|
-
name TEXT NOT NULL,
|
|
230
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
231
|
-
triggers TEXT NOT NULL DEFAULT '[]',
|
|
232
|
-
trigger_conditions TEXT,
|
|
233
|
-
actions TEXT NOT NULL,
|
|
234
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
235
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
CREATE INDEX IF NOT EXISTS idx_automations_seed_slug ON automations(seed_slug);
|
|
239
|
-
CREATE INDEX IF NOT EXISTS idx_automations_enabled ON automations(enabled);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
-- =============================================================================
|
|
243
|
-
-- 12. SITE SETTINGS
|
|
244
|
-
-- Key-value store for site-level configuration (title, locale, timezone…).
|
|
245
|
-
-- =============================================================================
|
|
246
|
-
|
|
247
|
-
CREATE TABLE IF NOT EXISTS site_settings (
|
|
248
|
-
key TEXT NOT NULL PRIMARY KEY,
|
|
249
|
-
value TEXT NOT NULL
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
-- =============================================================================
|
|
254
|
-
-- 13. SEED LAYOUTS
|
|
255
|
-
-- Persists per-Seed custom editor form layouts (sprint 04a).
|
|
256
|
-
-- layout column stores a FormLayout JSON blob.
|
|
257
|
-
-- Reset = DELETE the row; client recomputes the generated default.
|
|
258
|
-
-- =============================================================================
|
|
259
|
-
|
|
260
|
-
CREATE TABLE IF NOT EXISTS seed_layouts (
|
|
261
|
-
slug TEXT NOT NULL PRIMARY KEY,
|
|
262
|
-
layout TEXT NOT NULL, -- JSON of FormLayout
|
|
263
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
264
|
-
updated_by TEXT NOT NULL -- users.id of the writer
|
|
265
|
-
);
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- Beech CMS v0.4.0 — Base Schema
|
|
3
|
+
--
|
|
4
|
+
-- Single migration for fresh installations. Creates all system tables.
|
|
5
|
+
--
|
|
6
|
+
-- Content tables (content_{slug}, fts_{slug}, content_{slug}_drafts) are
|
|
7
|
+
-- NOT defined here — they are generated at deploy time by: beech seed:load
|
|
8
|
+
-- FTS indexes text + richtext branches (policies.search !== false).
|
|
9
|
+
--
|
|
10
|
+
-- Usage:
|
|
11
|
+
-- wrangler d1 execute beech-db --file=migrations/0000_v040_base.sql (remote)
|
|
12
|
+
-- wrangler d1 execute beech-db --local --file=migrations/0000_v040_base.sql (local)
|
|
13
|
+
--
|
|
14
|
+
-- After this migration, run:
|
|
15
|
+
-- beech seed:load → creates content_{slug} tables + FTS + triggers
|
|
16
|
+
-- POST /auth/setup → creates first admin user
|
|
17
|
+
-- =============================================================================
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
-- =============================================================================
|
|
21
|
+
-- 1. USERS
|
|
22
|
+
-- Dashboard users (admins + editors). Passwords stored as bcrypt hashes.
|
|
23
|
+
-- Profile fields (name, avatar_url, notification_prefs) merged in from
|
|
24
|
+
-- the start — no incremental ALTER TABLE needed.
|
|
25
|
+
-- =============================================================================
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
28
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
29
|
+
email TEXT NOT NULL UNIQUE,
|
|
30
|
+
password_hash TEXT NOT NULL,
|
|
31
|
+
role TEXT NOT NULL DEFAULT 'editor' CHECK (role IN ('admin', 'editor')),
|
|
32
|
+
name TEXT,
|
|
33
|
+
surname TEXT,
|
|
34
|
+
avatar_url TEXT,
|
|
35
|
+
notification_prefs TEXT NOT NULL DEFAULT '{}',
|
|
36
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
-- =============================================================================
|
|
41
|
+
-- 2. REFRESH TOKENS
|
|
42
|
+
-- JWT refresh token rotation. Stored as SHA-256 hashes, never plaintext.
|
|
43
|
+
-- revoked_at IS NULL = active token.
|
|
44
|
+
-- =============================================================================
|
|
45
|
+
|
|
46
|
+
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
47
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
48
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
49
|
+
token_hash TEXT NOT NULL,
|
|
50
|
+
expires_at INTEGER NOT NULL,
|
|
51
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
52
|
+
revoked_at INTEGER DEFAULT NULL
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_refresh_user ON refresh_tokens(user_id);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_refresh_hash ON refresh_tokens(token_hash);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_refresh_expires ON refresh_tokens(expires_at);
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
-- =============================================================================
|
|
61
|
+
-- 3. PASSWORD RESET TOKENS
|
|
62
|
+
-- Single-use, 30-minute TTL, stored as SHA-256 hashes.
|
|
63
|
+
-- used_at set on redemption to prevent replay attacks.
|
|
64
|
+
-- =============================================================================
|
|
65
|
+
|
|
66
|
+
CREATE TABLE IF NOT EXISTS password_reset_tokens (
|
|
67
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
68
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
69
|
+
token_hash TEXT NOT NULL,
|
|
70
|
+
expires_at INTEGER NOT NULL,
|
|
71
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
72
|
+
used_at INTEGER DEFAULT NULL
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_prt_hash ON password_reset_tokens(token_hash);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_prt_user ON password_reset_tokens(user_id);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
-- =============================================================================
|
|
80
|
+
-- 4. PUBLIC API — IDEMPOTENCY KEYS
|
|
81
|
+
-- Deduplicates POST requests from public consumers (e.g. contact forms).
|
|
82
|
+
-- expires_at-based pruning keeps the table small.
|
|
83
|
+
-- =============================================================================
|
|
84
|
+
|
|
85
|
+
CREATE TABLE IF NOT EXISTS public_idempotency_keys (
|
|
86
|
+
idempotency_key TEXT NOT NULL PRIMARY KEY,
|
|
87
|
+
request_fingerprint TEXT NOT NULL,
|
|
88
|
+
response_status INTEGER NOT NULL,
|
|
89
|
+
response_body TEXT NOT NULL,
|
|
90
|
+
created_at INTEGER NOT NULL,
|
|
91
|
+
expires_at INTEGER NOT NULL
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_idempotency_expires ON public_idempotency_keys(expires_at);
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
-- =============================================================================
|
|
98
|
+
-- 5. ANALYTICS
|
|
99
|
+
-- Daily metric counters per seed and global (seed = '' sentinel).
|
|
100
|
+
-- Metrics: 'requests' | 'visitors' | 'bandwidth_mb'
|
|
101
|
+
-- =============================================================================
|
|
102
|
+
|
|
103
|
+
CREATE TABLE IF NOT EXISTS analytics (
|
|
104
|
+
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
105
|
+
day_ts INTEGER NOT NULL,
|
|
106
|
+
metric TEXT NOT NULL,
|
|
107
|
+
seed TEXT NOT NULL DEFAULT '',
|
|
108
|
+
value INTEGER NOT NULL DEFAULT 0,
|
|
109
|
+
UNIQUE(day_ts, metric, seed)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_analytics_day ON analytics(day_ts);
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_analytics_seed ON analytics(seed, day_ts);
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
-- =============================================================================
|
|
117
|
+
-- 6. SYSTEM STATS
|
|
118
|
+
-- Persistent key-value store for system-level counters.
|
|
119
|
+
-- 'total_storage_bytes' is kept for dashboard widgets;
|
|
120
|
+
-- SUM(size_bytes) FROM media_objects is the authoritative source.
|
|
121
|
+
-- =============================================================================
|
|
122
|
+
|
|
123
|
+
CREATE TABLE IF NOT EXISTS system_stats (
|
|
124
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
125
|
+
value TEXT NOT NULL
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
INSERT OR IGNORE INTO system_stats (id, value) VALUES ('total_storage_bytes', '0');
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
-- =============================================================================
|
|
132
|
+
-- 7. ACTIVITY LOGS
|
|
133
|
+
-- Append-only audit log. No FK on user_id — deleted users are retained
|
|
134
|
+
-- for compliance. user_name denormalized to survive user deletions.
|
|
135
|
+
-- Actions: 'create' | 'update' | 'delete' | 'upload'
|
|
136
|
+
-- Entity types: 'content' | 'media'
|
|
137
|
+
-- =============================================================================
|
|
138
|
+
|
|
139
|
+
CREATE TABLE IF NOT EXISTS activity_logs (
|
|
140
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
141
|
+
user_id TEXT NOT NULL,
|
|
142
|
+
user_email TEXT NOT NULL,
|
|
143
|
+
user_name TEXT,
|
|
144
|
+
action TEXT NOT NULL,
|
|
145
|
+
entity_type TEXT NOT NULL,
|
|
146
|
+
entity_id TEXT NOT NULL,
|
|
147
|
+
entity_slug TEXT,
|
|
148
|
+
details TEXT,
|
|
149
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
CREATE INDEX IF NOT EXISTS idx_activity_user ON activity_logs(user_id);
|
|
153
|
+
CREATE INDEX IF NOT EXISTS idx_activity_created ON activity_logs(created_at);
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
-- =============================================================================
|
|
157
|
+
-- 8. NOTIFICATIONS
|
|
158
|
+
-- In-app notification inbox for system events (e.g. new form submission).
|
|
159
|
+
-- is_read: 0 = unread, 1 = read.
|
|
160
|
+
-- =============================================================================
|
|
161
|
+
|
|
162
|
+
CREATE TABLE IF NOT EXISTS notifications (
|
|
163
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
164
|
+
title TEXT NOT NULL,
|
|
165
|
+
message TEXT NOT NULL,
|
|
166
|
+
type TEXT NOT NULL DEFAULT 'info' CHECK (type IN ('info', 'warning', 'error')),
|
|
167
|
+
is_read INTEGER NOT NULL DEFAULT 0 CHECK (is_read IN (0, 1)),
|
|
168
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_created ON notifications(created_at);
|
|
172
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_unread ON notifications(is_read);
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
-- =============================================================================
|
|
176
|
+
-- 9. MEDIA OBJECTS
|
|
177
|
+
-- Tracks every file uploaded to R2. Enables media library UI, orphan
|
|
178
|
+
-- detection, and storage reporting. No FK on uploaded_by for D1 portability.
|
|
179
|
+
-- =============================================================================
|
|
180
|
+
|
|
181
|
+
CREATE TABLE IF NOT EXISTS media_objects (
|
|
182
|
+
key TEXT NOT NULL PRIMARY KEY,
|
|
183
|
+
filename TEXT NOT NULL,
|
|
184
|
+
mime_type TEXT NOT NULL,
|
|
185
|
+
size_bytes INTEGER NOT NULL,
|
|
186
|
+
uploaded_by TEXT NOT NULL DEFAULT '',
|
|
187
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
CREATE INDEX IF NOT EXISTS idx_media_user ON media_objects(uploaded_by);
|
|
191
|
+
CREATE INDEX IF NOT EXISTS idx_media_created ON media_objects(created_at DESC);
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
-- =============================================================================
|
|
195
|
+
-- 10. CONTENT EVENT LOG
|
|
196
|
+
-- Lightweight event log for activity feed and dashboard stats.
|
|
197
|
+
-- Does NOT store field data — only event metadata (who did what, when).
|
|
198
|
+
-- Replaces the legacy content_entries table as cross-seed event source.
|
|
199
|
+
-- The actual content lives in content_{slug} tables (created by seed:load).
|
|
200
|
+
-- =============================================================================
|
|
201
|
+
|
|
202
|
+
CREATE TABLE IF NOT EXISTS content_event_log (
|
|
203
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
204
|
+
schema_slug TEXT NOT NULL,
|
|
205
|
+
entry_id TEXT NOT NULL,
|
|
206
|
+
action TEXT NOT NULL CHECK (action IN ('create', 'update', 'delete')),
|
|
207
|
+
user_id TEXT,
|
|
208
|
+
details TEXT,
|
|
209
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
CREATE INDEX IF NOT EXISTS idx_event_log_schema_slug ON content_event_log(schema_slug);
|
|
213
|
+
CREATE INDEX IF NOT EXISTS idx_event_log_created_at ON content_event_log(created_at DESC);
|
|
214
|
+
CREATE INDEX IF NOT EXISTS idx_event_log_entry_id ON content_event_log(entry_id);
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
-- =============================================================================
|
|
218
|
+
-- 11. AUTOMATIONS
|
|
219
|
+
-- Event-driven automation rules. Each automation defines one or more
|
|
220
|
+
-- triggers (JSON array of {event, cron?}), optional WhenNode conditions,
|
|
221
|
+
-- and a sequence of actions to execute.
|
|
222
|
+
-- triggers: [{ event: 'create'|'update'|'delete'|'cron', cron?: string }]
|
|
223
|
+
-- Max one cron trigger per automation, enforced at the application layer.
|
|
224
|
+
-- =============================================================================
|
|
225
|
+
|
|
226
|
+
CREATE TABLE IF NOT EXISTS automations (
|
|
227
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
228
|
+
seed_slug TEXT NOT NULL,
|
|
229
|
+
name TEXT NOT NULL,
|
|
230
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
231
|
+
triggers TEXT NOT NULL DEFAULT '[]',
|
|
232
|
+
trigger_conditions TEXT,
|
|
233
|
+
actions TEXT NOT NULL,
|
|
234
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
235
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
CREATE INDEX IF NOT EXISTS idx_automations_seed_slug ON automations(seed_slug);
|
|
239
|
+
CREATE INDEX IF NOT EXISTS idx_automations_enabled ON automations(enabled);
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
-- =============================================================================
|
|
243
|
+
-- 12. SITE SETTINGS
|
|
244
|
+
-- Key-value store for site-level configuration (title, locale, timezone…).
|
|
245
|
+
-- =============================================================================
|
|
246
|
+
|
|
247
|
+
CREATE TABLE IF NOT EXISTS site_settings (
|
|
248
|
+
key TEXT NOT NULL PRIMARY KEY,
|
|
249
|
+
value TEXT NOT NULL
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
-- =============================================================================
|
|
254
|
+
-- 13. SEED LAYOUTS
|
|
255
|
+
-- Persists per-Seed custom editor form layouts (sprint 04a).
|
|
256
|
+
-- layout column stores a FormLayout JSON blob.
|
|
257
|
+
-- Reset = DELETE the row; client recomputes the generated default.
|
|
258
|
+
-- =============================================================================
|
|
259
|
+
|
|
260
|
+
CREATE TABLE IF NOT EXISTS seed_layouts (
|
|
261
|
+
slug TEXT NOT NULL PRIMARY KEY,
|
|
262
|
+
layout TEXT NOT NULL, -- JSON of FormLayout
|
|
263
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
264
|
+
updated_by TEXT NOT NULL -- users.id of the writer
|
|
265
|
+
);
|