Files
Siteforge/server/r2.js
T
nmemmertandClaude Sonnet 5.5 e74bd1df5d
Publish Container / build (push) Successful in 16m39s
Upgrade Asset Manager and Inbox; load Resend templates in Broadcast; v2.5.0
- Asset Manager: search/filter/sort, list+grid views, rename, move between R2 folders, tag chips, bulk actions, used-in counts; fix R2 delete lookup
- Inbox: readable wrapped rows with subject/preview, real read state, type filters, bulk actions, mojibake repair
- Broadcast: pick a Resend template to fill subject and body

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 07:45:50 -04:00

54 lines
1.7 KiB
JavaScript

import { S3Client, PutObjectCommand, DeleteObjectCommand, CopyObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3'
import { R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET, R2_PUBLIC_URL } from './config.js'
function makeClient() {
return new S3Client({
region: 'auto',
endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: { accessKeyId: R2_ACCESS_KEY_ID, secretAccessKey: R2_SECRET_ACCESS_KEY },
})
}
export function isR2Configured() {
return Boolean(R2_ACCOUNT_ID && R2_ACCESS_KEY_ID && R2_SECRET_ACCESS_KEY && R2_BUCKET && R2_PUBLIC_URL)
}
export async function uploadToR2(buffer, key, contentType) {
await makeClient().send(new PutObjectCommand({
Bucket: R2_BUCKET,
Key: key,
Body: buffer,
ContentType: contentType,
}))
return `${R2_PUBLIC_URL}/${key}`
}
export async function deleteFromR2(key) {
await makeClient().send(new DeleteObjectCommand({ Bucket: R2_BUCKET, Key: key }))
}
export async function copyInR2(fromKey, toKey) {
await makeClient().send(new CopyObjectCommand({
Bucket: R2_BUCKET,
CopySource: `${R2_BUCKET}/${fromKey.split('/').map(encodeURIComponent).join('/')}`,
Key: toKey,
}))
return `${R2_PUBLIC_URL}/${toKey}`
}
export async function listR2Objects(prefix) {
const objects = []
let continuationToken
const client = makeClient()
do {
const resp = await client.send(new ListObjectsV2Command({
Bucket: R2_BUCKET,
Prefix: prefix,
ContinuationToken: continuationToken,
}))
for (const obj of resp.Contents ?? []) objects.push(obj)
continuationToken = resp.IsTruncated ? resp.NextContinuationToken : undefined
} while (continuationToken)
return objects
}