CORS UPloads
Deploy / deploy (push) Successful in 3m5s

This commit is contained in:
twotalesanimation
2026-07-22 00:03:02 +02:00
parent 519fe2ad33
commit 15fda1ec44
4 changed files with 194 additions and 60 deletions
+49
View File
@@ -4,6 +4,7 @@ import { auth } from "@/auth";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { S3Client, PutBucketCorsCommand } from "@aws-sdk/client-s3";
const HETZNER_KEYS = [
"hetzner_endpoint",
@@ -74,3 +75,51 @@ export async function saveHetznerConfig(
revalidatePath("/settings");
return { success: true };
}
/**
* Applies a CORS policy to the Hetzner bucket that allows direct browser
* PUT uploads (used by batch-upload presigned URL flow).
*/
export async function configureHetznerCors(): Promise<{ success: true }> {
await requireAdmin();
const rows = await db.systemConfig.findMany({
where: { key: { in: [...HETZNER_KEYS] } },
});
const map = Object.fromEntries(rows.map((r) => [r.key, r.value])) as Partial<
Record<HetznerKey, string>
>;
const endpoint = map.hetzner_endpoint ?? process.env.HETZNER_ENDPOINT ?? "";
const accessKey = map.hetzner_access_key ?? process.env.HETZNER_ACCESS_KEY ?? "";
const secretKey = map.hetzner_secret_key ?? process.env.HETZNER_SECRET_KEY ?? "";
const bucket = map.hetzner_bucket_name ?? process.env.HETZNER_BUCKET_NAME ?? "";
if (!endpoint || !accessKey || !secretKey || !bucket) {
throw new Error("Hetzner storage is not fully configured. Save credentials first.");
}
const client = new S3Client({
region: "auto",
endpoint,
credentials: { accessKeyId: accessKey, secretAccessKey: secretKey },
});
await client.send(
new PutBucketCorsCommand({
Bucket: bucket,
CORSConfiguration: {
CORSRules: [
{
AllowedOrigins: ["*"],
AllowedMethods: ["PUT", "GET"],
AllowedHeaders: ["*"],
MaxAgeSeconds: 3600,
},
],
},
})
);
return { success: true };
}