@beechcms/api 0.5.0 → 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.
Files changed (225) hide show
  1. package/assets/dashboard/BeechLogo.svg +18 -18
  2. package/assets/dashboard/BeechLogoLIght.svg +48 -48
  3. package/assets/dashboard/Searching.svg +1 -0
  4. package/assets/dashboard/assets/chart-BrJFdsGZ.js +39 -0
  5. package/assets/dashboard/assets/index-CIaoMxuB.css +1 -0
  6. package/assets/dashboard/assets/index-wTKgygsg.js +636 -0
  7. package/assets/dashboard/assets/pie-chart-recharts-BEaE1FOE.js +1 -0
  8. package/assets/dashboard/assets/timeseries-chart-recharts-DmWM6imF.js +1 -0
  9. package/assets/dashboard/beechLogoDark.svg +48 -48
  10. package/assets/dashboard/index.html +18 -18
  11. package/assets/dashboard/noResult.svg +43 -0
  12. package/assets/dashboard/sol.svg +3 -3
  13. package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
  14. package/assets/dashboard/working.svg +1 -0
  15. package/migrations/0000_v040_base.sql +265 -238
  16. package/migrations/0029_automations.sql +14 -14
  17. package/migrations/0030_test_seeds.sql +125 -125
  18. package/package.json +13 -9
  19. package/src/auth/{in-memory-hash-provider.ts → __fixtures__/in-memory-hash-provider.ts} +17 -13
  20. package/src/auth/{static-token-service.ts → __fixtures__/static-token-service.ts} +22 -18
  21. package/src/auth/bcrypt-hash-provider.ts +24 -20
  22. package/src/auth/constants.ts +14 -10
  23. package/src/auth/generate-refresh-token.test.ts +23 -19
  24. package/src/auth/hash-provider.test.ts +55 -46
  25. package/src/auth/jose-token-service.ts +62 -55
  26. package/src/auth/jwt-claims-passthrough.test.ts +87 -0
  27. package/src/auth/login.test.ts +100 -92
  28. package/src/auth/login.ts +78 -74
  29. package/src/auth/refresh.ts +11 -5
  30. package/src/auth/token-service.test.ts +90 -82
  31. package/src/factory.ts +370 -347
  32. package/src/features/automations/__tests__/action-executors.test.ts +255 -268
  33. package/src/features/automations/__tests__/automation-runner.test.ts +196 -192
  34. package/src/features/automations/__tests__/automation-runner.utils.test.ts +60 -56
  35. package/src/features/automations/__tests__/automations.handler.test.ts +264 -260
  36. package/src/features/automations/__tests__/automations.repository.test.ts +163 -159
  37. package/src/features/automations/__tests__/automations.schema.test.ts +224 -134
  38. package/src/features/automations/__tests__/context-resolver.test.ts +126 -122
  39. package/src/features/automations/__tests__/cron-runner.test.ts +267 -263
  40. package/src/features/automations/__tests__/cron-runner.utils.test.ts +144 -140
  41. package/src/features/automations/__tests__/set-variable.executor.test.ts +383 -306
  42. package/src/features/automations/__tests__/template-grammar.test.ts +308 -304
  43. package/src/features/automations/__tests__/webhook.executor.test.ts +142 -0
  44. package/src/features/automations/__tests__/when-evaluator.test.ts +274 -270
  45. package/src/features/automations/__tests__/when-pushdown.test.ts +281 -277
  46. package/src/features/automations/action-executors/create-entry.executor.ts +27 -23
  47. package/src/features/automations/action-executors/edit-field.executor.ts +26 -22
  48. package/src/features/automations/action-executors/index.ts +37 -33
  49. package/src/features/automations/action-executors/send-mail.executor.ts +53 -42
  50. package/src/features/automations/action-executors/set-variable.executor.ts +159 -146
  51. package/src/features/automations/action-executors/webhook.executor.ts +67 -25
  52. package/src/features/automations/automation-runner.ts +85 -81
  53. package/src/features/automations/automation-runner.utils.ts +47 -43
  54. package/src/features/automations/automations.handler.ts +197 -193
  55. package/src/features/automations/automations.schema.ts +177 -160
  56. package/src/features/automations/context-resolver.ts +152 -148
  57. package/src/features/automations/cron-runner.ts +140 -136
  58. package/src/features/automations/cron-runner.utils.ts +44 -40
  59. package/src/features/automations/filter-translation.ts +46 -42
  60. package/src/features/automations/index.ts +16 -12
  61. package/src/features/automations/template-grammar.ts +245 -241
  62. package/src/features/automations/var-access-resolver.ts +140 -136
  63. package/src/features/automations/when-evaluator.ts +87 -83
  64. package/src/features/automations/when-pushdown.ts +57 -53
  65. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +359 -0
  66. package/src/features/backrefs/backrefs.handler.ts +168 -0
  67. package/src/features/backrefs/d1-backref.repository.ts +84 -0
  68. package/src/features/backrefs/index.ts +5 -0
  69. package/src/features/content/constants.ts +16 -10
  70. package/src/features/content/handlers/bulk.handler.ts +185 -0
  71. package/src/features/content/handlers/create.ts +129 -165
  72. package/src/features/content/handlers/delete.ts +62 -87
  73. package/src/features/content/handlers/facets.ts +49 -45
  74. package/src/features/content/handlers/get.ts +120 -116
  75. package/src/features/content/handlers/helpers.test.ts +180 -0
  76. package/src/features/content/handlers/helpers.ts +93 -0
  77. package/src/features/content/handlers/list.ts +174 -88
  78. package/src/features/content/handlers/update.ts +174 -219
  79. package/src/features/content/index.ts +26 -20
  80. package/src/features/dashboard-layout/__tests__/dashboard-layout.handler.test.ts +388 -0
  81. package/src/features/dashboard-layout/dashboard-layout.handler.ts +206 -0
  82. package/src/features/dashboard-layout/index.ts +5 -0
  83. package/src/features/draft/draft.handler.ts +195 -170
  84. package/src/features/draft/draft.middleware.ts +66 -62
  85. package/src/features/draft/index.ts +5 -1
  86. package/src/features/email/email.provider.ts +42 -38
  87. package/src/features/email/email.service.ts +98 -93
  88. package/src/features/email/email.types.ts +119 -108
  89. package/src/features/email/index.ts +33 -29
  90. package/src/features/email/providers/resend.ts +67 -63
  91. package/src/features/email/providers/smtp.ts +55 -0
  92. package/src/features/email/templates/automation-mail.ts +19 -15
  93. package/src/features/email/templates/password-changed.ts +63 -59
  94. package/src/features/email/templates/password-reset.ts +68 -64
  95. package/src/features/email/templates/shell.ts +96 -92
  96. package/src/features/notifications/index.ts +5 -1
  97. package/src/features/notifications/notifications.handler.ts +105 -101
  98. package/src/features/password-reset/index.ts +19 -15
  99. package/src/features/password-reset/request.ts +92 -82
  100. package/src/features/password-reset/reset.ts +102 -92
  101. package/src/features/rotate-field/index.ts +5 -1
  102. package/src/features/rotate-field/rotate-field.handler.ts +132 -128
  103. package/src/features/rotate-field/rotate-field.schema.ts +17 -13
  104. package/src/features/schema/schema.handler.ts +131 -16
  105. package/src/features/seeds/index.ts +5 -0
  106. package/src/features/seeds/seeds.handler.test.ts +1199 -0
  107. package/src/features/seeds/seeds.handler.ts +558 -0
  108. package/src/features/settings/__tests__/settings.handler.test.ts +555 -0
  109. package/src/features/settings/settings.handler.ts +392 -302
  110. package/src/features/setup/index.ts +288 -91
  111. package/src/features/stats/index.ts +5 -1
  112. package/src/features/stats/stats.handler.ts +420 -430
  113. package/src/features/webhooks/index.ts +63 -0
  114. package/src/index.ts +65 -56
  115. package/src/media-utils.ts +82 -78
  116. package/src/middleware/auth-providers.middleware.test.ts +85 -0
  117. package/src/middleware/auth-providers.middleware.ts +45 -32
  118. package/src/middleware/observability.middleware.test.ts +93 -0
  119. package/src/middleware/observability.middleware.ts +70 -52
  120. package/src/middleware/rate-limit.middleware.ts +45 -41
  121. package/src/middleware/repository.middleware.ts +117 -91
  122. package/src/middleware/seed-registry.middleware.test.ts +97 -0
  123. package/src/middleware/seed-registry.middleware.ts +21 -0
  124. package/src/middleware/storage.middleware.ts +25 -21
  125. package/src/middleware.ts +45 -47
  126. package/src/public/access-policy.ts +27 -23
  127. package/src/public/api-key-middleware.ts +57 -53
  128. package/src/public/cache-utils.ts +38 -34
  129. package/src/public/entry-projection.ts +46 -42
  130. package/src/public/idempotency.ts +23 -19
  131. package/src/public/index.ts +16 -12
  132. package/src/public/problem-details.ts +124 -53
  133. package/src/public/public-add.ts +114 -110
  134. package/src/public/public-edit.ts +163 -159
  135. package/src/public/public-errors.ts +19 -15
  136. package/src/public/public-read.ts +63 -59
  137. package/src/public/public-routes.ts +88 -84
  138. package/src/public/query-builder.test.ts +224 -220
  139. package/src/public/query-builder.ts +156 -152
  140. package/src/public/rate-limit-middleware.ts +34 -30
  141. package/src/public/read-list.ts +54 -50
  142. package/src/public/read-single.ts +48 -44
  143. package/src/public/response-builder.ts +30 -26
  144. package/src/public/sanitize.ts +69 -65
  145. package/src/public/slug-utils.ts +18 -14
  146. package/src/rate-limit/cloudflare-rate-limiter.test.ts +30 -26
  147. package/src/rate-limit/cloudflare-rate-limiter.ts +15 -11
  148. package/src/rate-limit/in-memory-rate-limiter.test.ts +37 -33
  149. package/src/rate-limit/in-memory-rate-limiter.ts +17 -13
  150. package/src/rate-limit/no-op-rate-limiter.test.ts +22 -18
  151. package/src/rate-limit/no-op-rate-limiter.ts +11 -7
  152. package/src/search-utils.test.ts +211 -207
  153. package/src/search-utils.ts +213 -209
  154. package/src/search.ts +65 -61
  155. package/src/shared/apply-policies.test.ts +81 -77
  156. package/src/shared/apply-policies.ts +67 -63
  157. package/src/shared/automations.repository.d1.ts +150 -146
  158. package/src/shared/background-notification-service.test.ts +62 -58
  159. package/src/shared/background-notification-service.ts +52 -48
  160. package/src/shared/base.repository.d1.ts +32 -28
  161. package/src/shared/content-utils.test.ts +165 -161
  162. package/src/shared/content-utils.ts +86 -82
  163. package/src/shared/content.repository.d1.test.ts +507 -312
  164. package/src/shared/content.repository.d1.ts +851 -382
  165. package/src/shared/d1-activity-log.repository.test.ts +140 -136
  166. package/src/shared/d1-activity-log.repository.ts +105 -101
  167. package/src/shared/d1-activity-logger.test.ts +86 -82
  168. package/src/shared/d1-activity-logger.ts +67 -63
  169. package/src/shared/d1-analytics.repository.test.ts +78 -74
  170. package/src/shared/d1-analytics.repository.ts +85 -81
  171. package/src/shared/d1-content-scan.repository.test.ts +80 -76
  172. package/src/shared/d1-content-scan.repository.ts +37 -29
  173. package/src/shared/d1-notification.repository.test.ts +128 -124
  174. package/src/shared/d1-notification.repository.ts +118 -114
  175. package/src/shared/d1-password-reset-token.repository.test.ts +81 -77
  176. package/src/shared/d1-password-reset-token.repository.ts +56 -52
  177. package/src/shared/d1-search.repository.test.ts +87 -83
  178. package/src/shared/d1-search.repository.ts +88 -84
  179. package/src/shared/d1-session.repository.test.ts +125 -121
  180. package/src/shared/d1-session.repository.ts +102 -98
  181. package/src/shared/d1-setup-checklist.repository.ts +36 -0
  182. package/src/shared/d1-user.repository.test.ts +151 -147
  183. package/src/shared/d1-user.repository.ts +119 -109
  184. package/src/shared/d1-widget.repository.test.ts +267 -217
  185. package/src/shared/d1-widget.repository.ts +385 -337
  186. package/src/shared/dashboard-layout.repository.d1.test.ts +135 -0
  187. package/src/shared/dashboard-layout.repository.d1.ts +58 -0
  188. package/src/shared/demo-data-sql.ts +216 -0
  189. package/src/shared/demo-data.repository.d1.ts +20 -0
  190. package/src/shared/execution-context-scheduler.ts +13 -9
  191. package/src/shared/fixed-clock.ts +25 -21
  192. package/src/shared/fts-sync.ts +8 -4
  193. package/src/shared/idempotency.repository.d1.test.ts +83 -79
  194. package/src/shared/idempotency.repository.d1.ts +60 -56
  195. package/src/shared/in-memory-activity-logger.ts +19 -15
  196. package/src/shared/in-memory-notification-service.ts +19 -15
  197. package/src/shared/in-memory-seed.repository.ts +58 -0
  198. package/src/shared/media.repository.d1.test.ts +107 -103
  199. package/src/shared/media.repository.d1.ts +68 -64
  200. package/src/shared/qstash-notification-service.test.ts +67 -0
  201. package/src/shared/qstash-notification-service.ts +55 -0
  202. package/src/shared/query-utils.ts +141 -137
  203. package/src/shared/request-utils.ts +26 -22
  204. package/src/shared/schema-mutator.d1.test.ts +142 -0
  205. package/src/shared/schema-mutator.d1.ts +64 -0
  206. package/src/shared/seed-layout.repository.d1.test.ts +114 -0
  207. package/src/shared/seed-layout.repository.d1.ts +62 -0
  208. package/src/shared/seed-registry-cache.test.ts +68 -0
  209. package/src/shared/seed-registry-cache.ts +49 -0
  210. package/src/shared/seed.repository.d1.test.ts +143 -0
  211. package/src/shared/seed.repository.d1.ts +98 -0
  212. package/src/shared/sequential-id-generator.ts +33 -22
  213. package/src/shared/site-settings.repository.d1.ts +53 -0
  214. package/src/shared/storage/factory.ts +41 -40
  215. package/src/shared/storage/s3-bucket.ts +195 -163
  216. package/src/shared/storage-utils.ts +40 -36
  217. package/src/shared/system-stats.repository.d1.test.ts +58 -54
  218. package/src/shared/system-stats.repository.d1.ts +48 -44
  219. package/src/types.ts +90 -67
  220. package/src/upload.ts +198 -187
  221. package/src/widget.test.ts +85 -0
  222. package/src/widget.ts +251 -208
  223. package/assets/dashboard/assets/index-B1mUfgiK.css +0 -1
  224. package/assets/dashboard/assets/index-CHxxc_id.js +0 -629
  225. package/src/shared/storage/r2-binding-bucket.ts +0 -81
@@ -1,36 +1,40 @@
1
- import { S3Client, ListObjectsV2Command, ListObjectsV2CommandOutput } from '@aws-sdk/client-s3'
2
-
3
- /**
4
- * Calcola la dimensione totale occupata in un bucket R2 (in byte).
5
- * Nota: esegue una scansione completa del bucket. Per bucket molto grandi
6
- * andrebbe implementata una cache o usato Cloudflare Logpush/Analytics.
7
- */
8
- export async function getBucketSize(client: S3Client, bucketName: string): Promise<number> {
9
- let totalSize = 0
10
- let isTruncatedFlag = true
11
- let continuationToken: string | undefined = undefined
12
-
13
- try {
14
- while (isTruncatedFlag) {
15
- const command: ListObjectsV2Command = new ListObjectsV2Command({
16
- Bucket: bucketName,
17
- ContinuationToken: continuationToken,
18
- })
19
-
20
- const response: ListObjectsV2CommandOutput = await client.send(command)
21
-
22
- if (response.Contents) {
23
- for (const obj of response.Contents) {
24
- totalSize += obj.Size ?? 0
25
- }
26
- }
27
-
28
- isTruncatedFlag = response.IsTruncated ?? false
29
- continuationToken = response.NextContinuationToken
30
- }
31
- return totalSize
32
- } catch (err) {
33
- console.error('Error calculating bucket size:', err)
34
- throw new Error('Failed to calculate bucket size. Check your R2/S3 credentials and endpoint.')
35
- }
36
- }
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
+ // See LICENSE in the repository root for license terms.
4
+
5
+ import { S3Client, ListObjectsV2Command, ListObjectsV2CommandOutput } from '@aws-sdk/client-s3'
6
+
7
+ /**
8
+ * Calcola la dimensione totale occupata in un bucket R2 (in byte).
9
+ * Nota: esegue una scansione completa del bucket. Per bucket molto grandi
10
+ * andrebbe implementata una cache o usato Cloudflare Logpush/Analytics.
11
+ */
12
+ export async function getBucketSize(client: S3Client, bucketName: string): Promise<number> {
13
+ let totalSize = 0
14
+ let isTruncatedFlag = true
15
+ let continuationToken: string | undefined = undefined
16
+
17
+ try {
18
+ while (isTruncatedFlag) {
19
+ const command: ListObjectsV2Command = new ListObjectsV2Command({
20
+ Bucket: bucketName,
21
+ ContinuationToken: continuationToken,
22
+ })
23
+
24
+ const response: ListObjectsV2CommandOutput = await client.send(command)
25
+
26
+ if (response.Contents) {
27
+ for (const obj of response.Contents) {
28
+ totalSize += obj.Size ?? 0
29
+ }
30
+ }
31
+
32
+ isTruncatedFlag = response.IsTruncated ?? false
33
+ continuationToken = response.NextContinuationToken
34
+ }
35
+ return totalSize
36
+ } catch (err) {
37
+ console.error('Error calculating bucket size:', err)
38
+ throw new Error('Failed to calculate bucket size. Check your R2/S3 credentials and endpoint.')
39
+ }
40
+ }
@@ -1,54 +1,58 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { D1SystemStatsRepository } from './system-stats.repository.d1'
3
-
4
- function makeMockDb(firstResult: unknown = null) {
5
- const runMock = vi.fn().mockResolvedValue({ success: true })
6
- const firstMock = vi.fn().mockResolvedValue(firstResult)
7
- // bind() returns the same statement interface for chained calls.
8
- // The statement also exposes first() directly for queries without bind (e.g. getStorageUsage).
9
- const bindMock = vi.fn(() => ({ run: runMock, first: firstMock }))
10
- const stmt = { bind: bindMock, run: runMock, first: firstMock }
11
- const prepareMock = vi.fn(() => stmt)
12
- return { db: { prepare: prepareMock } as any, prepareMock, bindMock, runMock, firstMock }
13
- }
14
-
15
- describe('D1SystemStatsRepository', () => {
16
- describe('incrementStorage', () => {
17
- it('calls UPDATE system_stats with the byte count', async () => {
18
- const { db, prepareMock, bindMock } = makeMockDb()
19
- await new D1SystemStatsRepository(db).incrementStorage(512)
20
- expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('UPDATE system_stats'))
21
- expect(bindMock).toHaveBeenCalledWith(512)
22
- })
23
- })
24
-
25
- describe('decrementStorage', () => {
26
- it('calls UPDATE system_stats with MAX(0, ...) and the byte count', async () => {
27
- const { db, prepareMock, bindMock } = makeMockDb()
28
- await new D1SystemStatsRepository(db).decrementStorage(256)
29
- expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('MAX(0'))
30
- expect(bindMock).toHaveBeenCalledWith(256)
31
- })
32
- })
33
-
34
- describe('setStorage', () => {
35
- it('calls UPDATE system_stats with the stringified byte count', async () => {
36
- const { db, prepareMock, bindMock } = makeMockDb()
37
- await new D1SystemStatsRepository(db).setStorage(1024)
38
- expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('UPDATE system_stats'))
39
- expect(bindMock).toHaveBeenCalledWith('1024')
40
- })
41
- })
42
-
43
- describe('getStorageUsage', () => {
44
- it('parses and returns the integer value from D1', async () => {
45
- const { db } = makeMockDb({ value: '2048' })
46
- expect(await new D1SystemStatsRepository(db).getStorageUsage()).toBe(2048)
47
- })
48
-
49
- it('returns 0 when D1 returns null (stat not initialised)', async () => {
50
- const { db } = makeMockDb(null)
51
- expect(await new D1SystemStatsRepository(db).getStorageUsage()).toBe(0)
52
- })
53
- })
54
- })
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
+ // See LICENSE in the repository root for license terms.
4
+
5
+ import { describe, it, expect, vi } from 'vitest'
6
+ import { D1SystemStatsRepository } from './system-stats.repository.d1'
7
+
8
+ function makeMockDb(firstResult: unknown = null) {
9
+ const runMock = vi.fn().mockResolvedValue({ success: true })
10
+ const firstMock = vi.fn().mockResolvedValue(firstResult)
11
+ // bind() returns the same statement interface for chained calls.
12
+ // The statement also exposes first() directly for queries without bind (e.g. getStorageUsage).
13
+ const bindMock = vi.fn(() => ({ run: runMock, first: firstMock }))
14
+ const stmt = { bind: bindMock, run: runMock, first: firstMock }
15
+ const prepareMock = vi.fn(() => stmt)
16
+ return { db: { prepare: prepareMock } as any, prepareMock, bindMock, runMock, firstMock }
17
+ }
18
+
19
+ describe('D1SystemStatsRepository', () => {
20
+ describe('incrementStorage', () => {
21
+ it('calls UPDATE system_stats with the byte count', async () => {
22
+ const { db, prepareMock, bindMock } = makeMockDb()
23
+ await new D1SystemStatsRepository(db).incrementStorage(512)
24
+ expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('UPDATE system_stats'))
25
+ expect(bindMock).toHaveBeenCalledWith(512)
26
+ })
27
+ })
28
+
29
+ describe('decrementStorage', () => {
30
+ it('calls UPDATE system_stats with MAX(0, ...) and the byte count', async () => {
31
+ const { db, prepareMock, bindMock } = makeMockDb()
32
+ await new D1SystemStatsRepository(db).decrementStorage(256)
33
+ expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('MAX(0'))
34
+ expect(bindMock).toHaveBeenCalledWith(256)
35
+ })
36
+ })
37
+
38
+ describe('setStorage', () => {
39
+ it('calls UPDATE system_stats with the stringified byte count', async () => {
40
+ const { db, prepareMock, bindMock } = makeMockDb()
41
+ await new D1SystemStatsRepository(db).setStorage(1024)
42
+ expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('UPDATE system_stats'))
43
+ expect(bindMock).toHaveBeenCalledWith('1024')
44
+ })
45
+ })
46
+
47
+ describe('getStorageUsage', () => {
48
+ it('parses and returns the integer value from D1', async () => {
49
+ const { db } = makeMockDb({ value: '2048' })
50
+ expect(await new D1SystemStatsRepository(db).getStorageUsage()).toBe(2048)
51
+ })
52
+
53
+ it('returns 0 when D1 returns null (stat not initialised)', async () => {
54
+ const { db } = makeMockDb(null)
55
+ expect(await new D1SystemStatsRepository(db).getStorageUsage()).toBe(0)
56
+ })
57
+ })
58
+ })
@@ -1,44 +1,48 @@
1
- import { SystemStatsRepository } from '@beechcms/core'
2
-
3
- /**
4
- * Implementation of SystemStatsRepository using Cloudflare D1.
5
- * Manages global system statistics such as total storage usage.
6
- */
7
- export class D1SystemStatsRepository implements SystemStatsRepository {
8
- constructor(private database: D1Database) {}
9
-
10
- /**
11
- * Increments the total storage usage count.
12
- */
13
- async incrementStorage(byteCount: number): Promise<void> {
14
- await this.database.prepare(
15
- "UPDATE system_stats SET value = CAST(value AS INTEGER) + ? WHERE id = 'total_storage_bytes'"
16
- ).bind(byteCount).run()
17
- }
18
-
19
- /**
20
- * Decrements the total storage usage count, ensuring it doesn't fall below zero.
21
- */
22
- async decrementStorage(byteCount: number): Promise<void> {
23
- await this.database.prepare(
24
- "UPDATE system_stats SET value = MAX(0, CAST(value AS INTEGER) - ?) WHERE id = 'total_storage_bytes'"
25
- ).bind(byteCount).run()
26
- }
27
-
28
- /**
29
- * Sets the total storage usage count to a specific value.
30
- */
31
- async setStorage(byteCount: number): Promise<void> {
32
- await this.database.prepare(
33
- "UPDATE system_stats SET value = ? WHERE id = 'total_storage_bytes'"
34
- ).bind(String(byteCount)).run()
35
- }
36
-
37
- /**
38
- * Retrieves the current total storage usage in bytes.
39
- */
40
- async getStorageUsage(): Promise<number> {
41
- const statsRecordResult = await this.database.prepare("SELECT value FROM system_stats WHERE id = 'total_storage_bytes'").first<{ value: string }>()
42
- return statsRecordResult ? parseInt(statsRecordResult.value, 10) : 0
43
- }
44
- }
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
+ // See LICENSE in the repository root for license terms.
4
+
5
+ import { SystemStatsRepository } from '@beechcms/core'
6
+
7
+ /**
8
+ * Implementation of SystemStatsRepository using Cloudflare D1.
9
+ * Manages global system statistics such as total storage usage.
10
+ */
11
+ export class D1SystemStatsRepository implements SystemStatsRepository {
12
+ constructor(private database: D1Database) {}
13
+
14
+ /**
15
+ * Increments the total storage usage count.
16
+ */
17
+ async incrementStorage(byteCount: number): Promise<void> {
18
+ await this.database.prepare(
19
+ "UPDATE system_stats SET value = CAST(value AS INTEGER) + ? WHERE id = 'total_storage_bytes'"
20
+ ).bind(byteCount).run()
21
+ }
22
+
23
+ /**
24
+ * Decrements the total storage usage count, ensuring it doesn't fall below zero.
25
+ */
26
+ async decrementStorage(byteCount: number): Promise<void> {
27
+ await this.database.prepare(
28
+ "UPDATE system_stats SET value = MAX(0, CAST(value AS INTEGER) - ?) WHERE id = 'total_storage_bytes'"
29
+ ).bind(byteCount).run()
30
+ }
31
+
32
+ /**
33
+ * Sets the total storage usage count to a specific value.
34
+ */
35
+ async setStorage(byteCount: number): Promise<void> {
36
+ await this.database.prepare(
37
+ "UPDATE system_stats SET value = ? WHERE id = 'total_storage_bytes'"
38
+ ).bind(String(byteCount)).run()
39
+ }
40
+
41
+ /**
42
+ * Retrieves the current total storage usage in bytes.
43
+ */
44
+ async getStorageUsage(): Promise<number> {
45
+ const statsRecordResult = await this.database.prepare("SELECT value FROM system_stats WHERE id = 'total_storage_bytes'").first<{ value: string }>()
46
+ return statsRecordResult ? Number.parseInt(statsRecordResult.value, 10) : 0
47
+ }
48
+ }
package/src/types.ts CHANGED
@@ -1,67 +1,90 @@
1
- /// <reference types="@cloudflare/workers-types" />
2
- import type { Seed, ContentRepository, IdempotencyRepository, BeechBucket, MediaRepository, SystemStatsRepository, IHashProvider, ITokenService, IUserRepository, ISessionRepository, IPasswordResetTokenRepository, IActivityLogger, IActivityLogRepository, INotificationRepository, INotificationService, IWidgetRepository, ISearchRepository, IAnalyticsRepository, IContentScanRepository, ISeedRegistry, IClock, IIdGenerator, IAutomationRunner, IAutomationRepository, IScheduler } from '@beechcms/core'
3
- import type { IRateLimiterRegistry } from './middleware/rate-limit.middleware'
4
-
5
- export interface Env {
6
- DB: D1Database
7
- JWT_SECRET: string
8
- JWT_ISSUER?: string
9
- JWT_AUDIENCE?: string
10
- R2_ACCESS_KEY_ID?: string
11
- R2_SECRET_ACCESS_KEY?: string
12
- R2_ENDPOINT?: string
13
- R2_BUCKET_NAME?: string
14
- LOGIN_RATE_LIMITER?: RateLimit
15
- REFRESH_RATE_LIMITER?: RateLimit
16
- PUBLIC_READ_RATE_LIMITER?: RateLimit
17
- PUBLIC_WRITE_RATE_LIMITER?: RateLimit
18
- CORS_ORIGINS?: string
19
- PUBLIC_READ_API_KEY?: string
20
- PUBLIC_WRITE_API_KEY?: string
21
- PUBLIC_PUBLISHED_ONLY?: string
22
- PUBLIC_IDEMPOTENCY_TTL_SECONDS?: string
23
- MEDIA_BASE_URL?: string
24
- MEDIA_CDN_URL?: string
25
- RESEND_API_KEY?: string
26
- EMAIL_API_KEY?: string
27
- APP_URL?: string
28
- EMAIL_FROM?: string
29
- FORGOT_PASSWORD_RATE_LIMITER?: RateLimit
30
- RESET_PASSWORD_RATE_LIMITER?: RateLimit
31
- ENV?: string
32
- DATE_FORMAT?: string
33
- ASSETS?: Fetcher
34
- MEDIA_BUCKET?: R2Bucket
35
- }
36
-
37
- export interface Variables {
38
- jwtPayload: { sub: string; email?: string; name?: string | null }
39
- getSeed: (slug: string) => Seed | null
40
- seedRegistry: ISeedRegistry
41
- repository: ContentRepository
42
- idempotencyRepository: IdempotencyRepository
43
- bucket: BeechBucket
44
- mediaRepository: MediaRepository
45
- systemStatsRepository: SystemStatsRepository
46
- hashProvider: IHashProvider
47
- tokenService: ITokenService
48
- userRepository: IUserRepository
49
- sessionRepository: ISessionRepository
50
- passwordResetTokenRepository: IPasswordResetTokenRepository
51
- rateLimiters: IRateLimiterRegistry
52
- activityLogger: IActivityLogger
53
- activityLogRepository: IActivityLogRepository
54
- notificationRepository: INotificationRepository
55
- notificationService: INotificationService
56
- widgetRepository: IWidgetRepository
57
- searchRepository: ISearchRepository
58
- analyticsRepository: IAnalyticsRepository
59
- contentScanRepository: IContentScanRepository
60
- clock: IClock
61
- idGenerator: IIdGenerator
62
- automationRepository: IAutomationRepository
63
- automationRunner: IAutomationRunner
64
- scheduler: IScheduler
65
- }
66
-
67
- export type AppEnv = { Bindings: Env; Variables: Variables }
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
+ // See LICENSE in the repository root for license terms.
4
+
5
+ /// <reference types="@cloudflare/workers-types" />
6
+ import type { Seed, ContentRepository, IdempotencyRepository, BeechBucket, MediaRepository, SystemStatsRepository, IHashProvider, ITokenService, IUserRepository, ISessionRepository, IPasswordResetTokenRepository, IActivityLogger, IActivityLogRepository, INotificationRepository, INotificationService, IWidgetRepository, ISearchRepository, IAnalyticsRepository, IContentScanRepository, ISeedRegistry, IClock, IIdGenerator, IAutomationRunner, IAutomationRepository, IScheduler, BackrefMap, ISiteSettingsRepository, IDemoDataRepository, JwtClaims, ISeedLayoutRepository, ISeedRepository, ISchemaMutator, IDashboardLayoutRepository } from '@beechcms/core'
7
+ import type { IRateLimiterRegistry } from './middleware/rate-limit.middleware'
8
+ import type { ISetupChecklistRepository } from './shared/d1-setup-checklist.repository'
9
+
10
+ export interface Env {
11
+ DB: D1Database
12
+ JWT_SECRET: string
13
+ JWT_ISSUER?: string
14
+ JWT_AUDIENCE?: string
15
+ R2_ACCESS_KEY_ID?: string
16
+ R2_SECRET_ACCESS_KEY?: string
17
+ R2_ENDPOINT?: string
18
+ R2_BUCKET_NAME?: string
19
+ LOGIN_RATE_LIMITER?: RateLimit
20
+ REFRESH_RATE_LIMITER?: RateLimit
21
+ PUBLIC_READ_RATE_LIMITER?: RateLimit
22
+ PUBLIC_WRITE_RATE_LIMITER?: RateLimit
23
+ CORS_ORIGINS?: string
24
+ PUBLIC_READ_API_KEY?: string
25
+ PUBLIC_WRITE_API_KEY?: string
26
+ PUBLIC_PUBLISHED_ONLY?: string
27
+ PUBLIC_IDEMPOTENCY_TTL_SECONDS?: string
28
+ MEDIA_BASE_URL?: string
29
+ MEDIA_CDN_URL?: string
30
+ MAX_UPLOAD_BYTES?: string
31
+ RESEND_API_KEY?: string
32
+ EMAIL_API_KEY?: string
33
+ APP_URL?: string
34
+ EMAIL_FROM?: string
35
+ EMAIL_PROVIDER?: string
36
+ SMTP_HOST?: string
37
+ SMTP_PORT?: string
38
+ WEBHOOK_TESTER_URL?: string
39
+ FORGOT_PASSWORD_RATE_LIMITER?: RateLimit
40
+ RESET_PASSWORD_RATE_LIMITER?: RateLimit
41
+ ENV?: string
42
+ DATE_FORMAT?: string
43
+ ASSETS?: Fetcher
44
+ QSTASH_TOKEN?: string
45
+ QSTASH_URL?: string
46
+ /** Public base URL the worker is reachable at, used by QStash to call back the webhook (e.g. an ngrok tunnel in dev). Falls back to APP_URL. */
47
+ QSTASH_CALLBACK_URL?: string
48
+ QSTASH_CURRENT_SIGNING_KEY?: string
49
+ QSTASH_NEXT_SIGNING_KEY?: string
50
+ }
51
+
52
+ export interface Variables {
53
+ jwtPayload: JwtClaims
54
+ getSeed: (slug: string) => Seed | null
55
+ seedRegistry: ISeedRegistry
56
+ repository: ContentRepository
57
+ idempotencyRepository: IdempotencyRepository
58
+ bucket: BeechBucket
59
+ mediaRepository: MediaRepository
60
+ systemStatsRepository: SystemStatsRepository
61
+ hashProvider: IHashProvider
62
+ tokenService: ITokenService
63
+ userRepository: IUserRepository
64
+ sessionRepository: ISessionRepository
65
+ passwordResetTokenRepository: IPasswordResetTokenRepository
66
+ rateLimiters: IRateLimiterRegistry
67
+ activityLogger: IActivityLogger
68
+ activityLogRepository: IActivityLogRepository
69
+ notificationRepository: INotificationRepository
70
+ notificationService: INotificationService
71
+ widgetRepository: IWidgetRepository
72
+ searchRepository: ISearchRepository
73
+ analyticsRepository: IAnalyticsRepository
74
+ contentScanRepository: IContentScanRepository
75
+ clock: IClock
76
+ idGenerator: IIdGenerator
77
+ automationRepository: IAutomationRepository
78
+ automationRunner: IAutomationRunner
79
+ scheduler: IScheduler
80
+ backrefMap: BackrefMap
81
+ siteSettingsRepository: ISiteSettingsRepository
82
+ demoDataRepository: IDemoDataRepository
83
+ setupChecklistRepository: ISetupChecklistRepository
84
+ seedLayoutRepository: ISeedLayoutRepository
85
+ seedRepository: ISeedRepository
86
+ schemaMutator: ISchemaMutator
87
+ dashboardLayoutRepository: IDashboardLayoutRepository
88
+ }
89
+
90
+ export type AppEnv = { Bindings: Env; Variables: Variables }