@beechcms/api 0.4.0-preview.11 → 0.4.0-preview.13
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/index-CewtCjom.css +1 -0
- package/assets/dashboard/assets/index-FQ6JhvRH.js +554 -0
- package/assets/dashboard/beechLogoDark.svg +48 -48
- package/assets/dashboard/index.html +18 -18
- package/assets/dashboard/sol.svg +3 -3
- package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
- package/migrations/0000_v040_base.sql +213 -213
- package/package.json +2 -2
- package/src/auth/constants.ts +10 -10
- package/src/auth/login.ts +91 -91
- package/src/auth/refresh.ts +127 -127
- package/src/factory.ts +347 -325
- package/src/features/content/constants.ts +10 -10
- package/src/features/content/handlers/create.ts +153 -163
- package/src/features/content/handlers/delete.ts +76 -85
- package/src/features/content/handlers/facets.ts +45 -45
- package/src/features/content/handlers/get.ts +116 -116
- package/src/features/content/handlers/list.ts +88 -88
- package/src/features/content/handlers/update.ts +207 -216
- package/src/features/content/index.ts +20 -20
- package/src/features/draft/draft.handler.ts +272 -272
- package/src/features/draft/index.ts +1 -1
- package/src/features/email/email.provider.ts +38 -38
- package/src/features/email/email.service.ts +80 -80
- package/src/features/email/email.types.ts +98 -98
- package/src/features/email/index.ts +28 -28
- package/src/features/email/providers/resend.ts +63 -63
- package/src/features/email/templates/password-changed.ts +59 -59
- package/src/features/email/templates/password-reset.ts +64 -64
- package/src/features/email/templates/shell.ts +92 -93
- package/src/features/notifications/index.ts +1 -1
- package/src/features/notifications/notifications.handler.ts +130 -88
- package/src/features/password-reset/index.ts +15 -15
- package/src/features/password-reset/request.ts +106 -88
- package/src/features/password-reset/reset.ts +128 -110
- package/src/features/rotate-field/index.ts +1 -1
- package/src/features/rotate-field/rotate-field.handler.ts +132 -82
- package/src/features/rotate-field/rotate-field.schema.ts +13 -9
- package/src/features/schema/schema.handler.ts +16 -16
- package/src/features/settings/settings.handler.ts +414 -267
- package/src/features/setup/index.ts +96 -59
- package/src/features/stats/index.ts +1 -1
- package/src/features/stats/stats.handler.ts +458 -395
- package/src/index.ts +24 -24
- package/src/media-utils.ts +78 -78
- package/src/middleware/repository.middleware.ts +24 -18
- package/src/middleware/storage.middleware.ts +21 -0
- package/src/middleware.ts +67 -67
- package/src/public/access-policy.ts +23 -23
- package/src/public/api-key-middleware.ts +53 -53
- package/src/public/index.ts +12 -12
- package/src/public/problem-details.ts +48 -42
- package/src/public/public-add.ts +166 -166
- package/src/public/public-edit.ts +158 -158
- package/src/public/public-errors.ts +15 -15
- package/src/public/public-read.ts +216 -216
- package/src/public/public-routes.ts +84 -31
- package/src/public/query-builder.ts +152 -152
- package/src/public/rate-limit-middleware.ts +42 -42
- package/src/public/response-builder.ts +26 -26
- package/src/public/sanitize.ts +65 -65
- package/src/public/slug-utils.ts +14 -14
- package/src/search-utils.ts +192 -192
- package/src/search.ts +72 -72
- package/src/shared/activity-logger.ts +79 -79
- package/src/shared/apply-policies.ts +63 -63
- package/src/shared/base.repository.d1.ts +28 -28
- package/src/shared/content-utils.ts +82 -108
- package/src/shared/content.repository.d1.ts +382 -382
- package/src/shared/fts-sync.ts +4 -4
- package/src/shared/idempotency.repository.d1.ts +56 -45
- package/src/shared/media.repository.d1.ts +64 -0
- package/src/shared/notification-service.ts +56 -56
- package/src/shared/query-utils.ts +137 -137
- package/src/shared/storage/factory.ts +40 -0
- package/src/shared/storage/r2-binding-bucket.ts +81 -0
- package/src/shared/storage/s3-bucket.ts +163 -0
- package/src/shared/storage-utils.ts +36 -36
- package/src/shared/system-stats.repository.d1.ts +44 -0
- package/src/types.ts +46 -41
- package/src/upload.ts +179 -331
- package/src/widget.ts +349 -349
- package/assets/dashboard/assets/index-9Ch2xWJr.js +0 -554
- package/assets/dashboard/assets/index-ye3325L9.css +0 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BeechBucket, PutBucketOptions, GetBucketResult } from '@beechcms/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of BeechBucket using Cloudflare R2 native binding.
|
|
5
|
+
* Used for local development (Miniflare) or native Cloudflare Workers deployment.
|
|
6
|
+
*/
|
|
7
|
+
export class R2BindingBucket implements BeechBucket {
|
|
8
|
+
constructor(
|
|
9
|
+
private bucket: R2Bucket,
|
|
10
|
+
private baseUrl: string
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
async put(key: string, body: ArrayBuffer | Uint8Array | ReadableStream, options?: PutBucketOptions): Promise<void> {
|
|
14
|
+
await this.bucket.put(key, body, {
|
|
15
|
+
httpMetadata: { contentType: options?.contentType },
|
|
16
|
+
customMetadata: options?.metadata
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async get(key: string): Promise<GetBucketResult | null> {
|
|
21
|
+
const object = await this.bucket.get(key)
|
|
22
|
+
if (!object) return null
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
body: object.body,
|
|
26
|
+
contentType: object.httpMetadata?.contentType,
|
|
27
|
+
size: object.size,
|
|
28
|
+
metadata: object.customMetadata
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async delete(key: string): Promise<void> {
|
|
33
|
+
await this.bucket.delete(key)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async head(key: string): Promise<{ size: number; contentType?: string; metadata?: Record<string, string> } | null> {
|
|
37
|
+
const object = await this.bucket.head(key)
|
|
38
|
+
if (!object) return null
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
size: object.size,
|
|
42
|
+
contentType: object.httpMetadata?.contentType,
|
|
43
|
+
metadata: object.customMetadata
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getUrl(key: string): string {
|
|
48
|
+
// If baseUrl is just the origin, we use the proxied route.
|
|
49
|
+
// If it's a custom CDN/R2 domain, it might not need /api/media/
|
|
50
|
+
// but for now, we maintain the contract: the provider decides the full URL.
|
|
51
|
+
if (this.baseUrl.includes('/api/media')) {
|
|
52
|
+
return `${this.baseUrl}/${encodeURIComponent(key)}`
|
|
53
|
+
}
|
|
54
|
+
return `${this.baseUrl}/api/media/${encodeURIComponent(key)}`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getTotalSize(): Promise<number> {
|
|
58
|
+
let total = 0
|
|
59
|
+
let cursor: string | undefined
|
|
60
|
+
do {
|
|
61
|
+
const result: R2Objects = await this.bucket.list({ cursor })
|
|
62
|
+
for (const obj of result.objects) {
|
|
63
|
+
total += obj.size
|
|
64
|
+
}
|
|
65
|
+
cursor = result.truncated ? result.cursor : undefined
|
|
66
|
+
} while (cursor)
|
|
67
|
+
return total
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ objects: Array<{ key: string; size: number }>; cursor?: string }> {
|
|
71
|
+
const result = await this.bucket.list({
|
|
72
|
+
prefix: options?.prefix,
|
|
73
|
+
limit: options?.limit,
|
|
74
|
+
cursor: options?.cursor
|
|
75
|
+
})
|
|
76
|
+
return {
|
|
77
|
+
objects: result.objects.map(obj => ({ key: obj.key, size: obj.size })),
|
|
78
|
+
cursor: result.truncated ? result.cursor : undefined
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, HeadObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3'
|
|
2
|
+
import { BeechBucket, PutBucketOptions, GetBucketResult } from '@beechcms/core'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implementation of BeechBucket using S3-compatible API (AWS SDK).
|
|
6
|
+
* Used for production when connecting to R2 or any other S3 provider via HTTP.
|
|
7
|
+
*/
|
|
8
|
+
export class S3Bucket implements BeechBucket {
|
|
9
|
+
private client: S3Client
|
|
10
|
+
private bucketName: string
|
|
11
|
+
private baseUrl: string
|
|
12
|
+
|
|
13
|
+
private cdnUrl: string | null
|
|
14
|
+
|
|
15
|
+
constructor(config: {
|
|
16
|
+
endpoint: string,
|
|
17
|
+
accessKeyId: string,
|
|
18
|
+
secretAccessKey: string,
|
|
19
|
+
bucketName: string,
|
|
20
|
+
baseUrl: string,
|
|
21
|
+
cdnUrl?: string,
|
|
22
|
+
}) {
|
|
23
|
+
this.client = new S3Client({
|
|
24
|
+
region: 'auto',
|
|
25
|
+
endpoint: config.endpoint,
|
|
26
|
+
credentials: {
|
|
27
|
+
accessKeyId: config.accessKeyId,
|
|
28
|
+
secretAccessKey: config.secretAccessKey,
|
|
29
|
+
},
|
|
30
|
+
forcePathStyle: true,
|
|
31
|
+
})
|
|
32
|
+
this.bucketName = config.bucketName
|
|
33
|
+
this.baseUrl = config.baseUrl
|
|
34
|
+
this.cdnUrl = config.cdnUrl ?? null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async put(key: string, body: ArrayBuffer | Uint8Array | ReadableStream, options?: PutBucketOptions): Promise<void> {
|
|
38
|
+
const command = new PutObjectCommand({
|
|
39
|
+
Bucket: this.bucketName,
|
|
40
|
+
Key: key,
|
|
41
|
+
Body: body instanceof ReadableStream ? await this.streamToUint8Array(body) : new Uint8Array(body as ArrayBuffer),
|
|
42
|
+
ContentType: options?.contentType,
|
|
43
|
+
Metadata: options?.metadata
|
|
44
|
+
})
|
|
45
|
+
await this.client.send(command)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async streamToUint8Array(stream: ReadableStream): Promise<Uint8Array> {
|
|
49
|
+
const reader = stream.getReader()
|
|
50
|
+
const chunks: Uint8Array[] = []
|
|
51
|
+
let totalLength = 0
|
|
52
|
+
while (true) {
|
|
53
|
+
const { done, value } = await reader.read()
|
|
54
|
+
if (done) break
|
|
55
|
+
chunks.push(value)
|
|
56
|
+
totalLength += value.length
|
|
57
|
+
}
|
|
58
|
+
const result = new Uint8Array(totalLength)
|
|
59
|
+
let offset = 0
|
|
60
|
+
for (const chunk of chunks) {
|
|
61
|
+
result.set(chunk, offset)
|
|
62
|
+
offset += chunk.length
|
|
63
|
+
}
|
|
64
|
+
return result
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async get(key: string): Promise<GetBucketResult | null> {
|
|
68
|
+
try {
|
|
69
|
+
const command = new GetObjectCommand({
|
|
70
|
+
Bucket: this.bucketName,
|
|
71
|
+
Key: key
|
|
72
|
+
})
|
|
73
|
+
const response = await this.client.send(command)
|
|
74
|
+
|
|
75
|
+
if (!response.Body) return null
|
|
76
|
+
|
|
77
|
+
const rawBody = response.Body as any
|
|
78
|
+
const body: ReadableStream = typeof rawBody.transformToWebStream === 'function'
|
|
79
|
+
? rawBody.transformToWebStream()
|
|
80
|
+
: rawBody as ReadableStream
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
body,
|
|
84
|
+
contentType: response.ContentType,
|
|
85
|
+
size: response.ContentLength ?? 0,
|
|
86
|
+
metadata: response.Metadata
|
|
87
|
+
}
|
|
88
|
+
} catch (err: any) {
|
|
89
|
+
if (err.name === 'NoSuchKey' || err.name === 'NotFound' || err.message === 'NoSuchKey') {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
// Rethrow other errors (AccessDenied, Timeout, etc.)
|
|
93
|
+
throw err
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async delete(key: string): Promise<void> {
|
|
98
|
+
const command = new DeleteObjectCommand({
|
|
99
|
+
Bucket: this.bucketName,
|
|
100
|
+
Key: key
|
|
101
|
+
})
|
|
102
|
+
await this.client.send(command)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async head(key: string): Promise<{ size: number; contentType?: string; metadata?: Record<string, string> } | null> {
|
|
106
|
+
try {
|
|
107
|
+
const command = new HeadObjectCommand({
|
|
108
|
+
Bucket: this.bucketName,
|
|
109
|
+
Key: key
|
|
110
|
+
})
|
|
111
|
+
const response = await this.client.send(command)
|
|
112
|
+
return {
|
|
113
|
+
size: response.ContentLength ?? 0,
|
|
114
|
+
contentType: response.ContentType,
|
|
115
|
+
metadata: response.Metadata
|
|
116
|
+
}
|
|
117
|
+
} catch (err: any) {
|
|
118
|
+
if (err.name === 'NoSuchKey' || err.name === 'NotFound') return null
|
|
119
|
+
throw err
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getUrl(key: string): string {
|
|
124
|
+
if (this.cdnUrl) {
|
|
125
|
+
return `${this.cdnUrl}/${encodeURIComponent(key)}`
|
|
126
|
+
}
|
|
127
|
+
return `${this.baseUrl}/api/media/${encodeURIComponent(key)}`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async getTotalSize(): Promise<number> {
|
|
131
|
+
let total = 0
|
|
132
|
+
let token: string | undefined
|
|
133
|
+
do {
|
|
134
|
+
const command = new ListObjectsV2Command({
|
|
135
|
+
Bucket: this.bucketName,
|
|
136
|
+
ContinuationToken: token
|
|
137
|
+
})
|
|
138
|
+
const response = await this.client.send(command)
|
|
139
|
+
for (const obj of response.Contents ?? []) {
|
|
140
|
+
total += obj.Size ?? 0
|
|
141
|
+
}
|
|
142
|
+
token = response.NextContinuationToken
|
|
143
|
+
} while (token)
|
|
144
|
+
return total
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ objects: Array<{ key: string; size: number }>; cursor?: string }> {
|
|
148
|
+
const command = new ListObjectsV2Command({
|
|
149
|
+
Bucket: this.bucketName,
|
|
150
|
+
Prefix: options?.prefix,
|
|
151
|
+
MaxKeys: options?.limit,
|
|
152
|
+
ContinuationToken: options?.cursor
|
|
153
|
+
})
|
|
154
|
+
const response = await this.client.send(command)
|
|
155
|
+
return {
|
|
156
|
+
objects: (response.Contents ?? []).map(obj => ({
|
|
157
|
+
key: obj.Key ?? '',
|
|
158
|
+
size: obj.Size ?? 0
|
|
159
|
+
})),
|
|
160
|
+
cursor: response.NextContinuationToken
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
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 = new ListObjectsV2Command({
|
|
16
|
-
Bucket: bucketName,
|
|
17
|
-
ContinuationToken: continuationToken,
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const response = (await client.send(command)) as ListObjectsV2CommandOutput
|
|
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
|
-
|
|
35
|
-
}
|
|
36
|
-
}
|
|
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 = new ListObjectsV2Command({
|
|
16
|
+
Bucket: bucketName,
|
|
17
|
+
ContinuationToken: continuationToken,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const response = (await client.send(command)) as ListObjectsV2CommandOutput
|
|
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
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,41 +1,46 @@
|
|
|
1
|
-
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
import type { Seed, ContentRepository, IdempotencyRepository } from '@beechcms/core'
|
|
3
|
-
|
|
4
|
-
export interface Env {
|
|
5
|
-
DB: D1Database
|
|
6
|
-
JWT_SECRET: string
|
|
7
|
-
JWT_ISSUER?: string
|
|
8
|
-
JWT_AUDIENCE?: string
|
|
9
|
-
R2_ACCESS_KEY_ID?: string
|
|
10
|
-
R2_SECRET_ACCESS_KEY?: string
|
|
11
|
-
R2_ENDPOINT?: string
|
|
12
|
-
R2_BUCKET_NAME?: string
|
|
13
|
-
LOGIN_RATE_LIMITER?: RateLimit
|
|
14
|
-
REFRESH_RATE_LIMITER?: RateLimit
|
|
15
|
-
PUBLIC_READ_RATE_LIMITER?: RateLimit
|
|
16
|
-
PUBLIC_WRITE_RATE_LIMITER?: RateLimit
|
|
17
|
-
CORS_ORIGINS?: string
|
|
18
|
-
PUBLIC_READ_API_KEY?: string
|
|
19
|
-
PUBLIC_WRITE_API_KEY?: string
|
|
20
|
-
PUBLIC_PUBLISHED_ONLY?: string
|
|
21
|
-
PUBLIC_IDEMPOTENCY_TTL_SECONDS?: string
|
|
22
|
-
MEDIA_BASE_URL?: string
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import type { Seed, ContentRepository, IdempotencyRepository, BeechBucket, MediaRepository, SystemStatsRepository } from '@beechcms/core'
|
|
3
|
+
|
|
4
|
+
export interface Env {
|
|
5
|
+
DB: D1Database
|
|
6
|
+
JWT_SECRET: string
|
|
7
|
+
JWT_ISSUER?: string
|
|
8
|
+
JWT_AUDIENCE?: string
|
|
9
|
+
R2_ACCESS_KEY_ID?: string
|
|
10
|
+
R2_SECRET_ACCESS_KEY?: string
|
|
11
|
+
R2_ENDPOINT?: string
|
|
12
|
+
R2_BUCKET_NAME?: string
|
|
13
|
+
LOGIN_RATE_LIMITER?: RateLimit
|
|
14
|
+
REFRESH_RATE_LIMITER?: RateLimit
|
|
15
|
+
PUBLIC_READ_RATE_LIMITER?: RateLimit
|
|
16
|
+
PUBLIC_WRITE_RATE_LIMITER?: RateLimit
|
|
17
|
+
CORS_ORIGINS?: string
|
|
18
|
+
PUBLIC_READ_API_KEY?: string
|
|
19
|
+
PUBLIC_WRITE_API_KEY?: string
|
|
20
|
+
PUBLIC_PUBLISHED_ONLY?: string
|
|
21
|
+
PUBLIC_IDEMPOTENCY_TTL_SECONDS?: string
|
|
22
|
+
MEDIA_BASE_URL?: string
|
|
23
|
+
MEDIA_CDN_URL?: string
|
|
24
|
+
RESEND_API_KEY?: string
|
|
25
|
+
APP_URL?: string
|
|
26
|
+
EMAIL_FROM?: string
|
|
27
|
+
FORGOT_PASSWORD_RATE_LIMITER?: RateLimit
|
|
28
|
+
RESET_PASSWORD_RATE_LIMITER?: RateLimit
|
|
29
|
+
ENV?: string
|
|
30
|
+
DATE_FORMAT?: string
|
|
31
|
+
ASSETS?: Fetcher
|
|
32
|
+
MEDIA_BUCKET?: R2Bucket
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Variables {
|
|
36
|
+
jwtPayload: { sub: string; email?: string }
|
|
37
|
+
getSeed: (slug: string) => Seed | null
|
|
38
|
+
seedRegistry: Record<string, Seed>
|
|
39
|
+
repository: ContentRepository
|
|
40
|
+
idempotencyRepository: IdempotencyRepository
|
|
41
|
+
bucket: BeechBucket
|
|
42
|
+
mediaRepository: MediaRepository
|
|
43
|
+
systemStatsRepository: SystemStatsRepository
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type AppEnv = { Bindings: Env; Variables: Variables }
|