diff --git a/Makefile b/Makefile index a8b18819..d07b36d4 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ PERSISTENT_DIR := $(HOME)/persistent/$(APP_NAME) BACKUP_FILENAME := $(HOME)/backups/$(shell date +"%Y-%m-%d_%H-%M-%S").sql.gz online-energieausweis: - bun run --bun dev --host + bun run dev --host dev: database online-energieausweis diff --git a/astro.config.mjs b/astro.config.mjs index 13e73576..dfe9f678 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -19,6 +19,11 @@ export default defineConfig({ optimizeDeps: { exclude: ["bun"] }, + build: { + rollupOptions: { + external: ["bun", "stream", "node:stream", "http", "node:http", "https", "http2", "path", "os", "crypto", "fs", "url", "util", "child_process"] + } + }, ssr: { external: ["@prisma/client", "bun", "stream"], resolve: { diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index 490baafd..ca46156e 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -1,45 +1,45 @@ import { createCallerFactory } from "astro-typesafe-api/server"; export const createCaller = createCallerFactory({ + "bild": await import("../src/pages/api/bild.ts"), "klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"), "postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"), - "bild": await import("../src/pages/api/bild.ts"), "unterlage": await import("../src/pages/api/unterlage.ts"), - "ausweise": await import("../src/pages/api/ausweise/index.ts"), - "ticket": await import("../src/pages/api/ticket/index.ts"), - "geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"), - "geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"), - "admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"), - "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), - "admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"), - "admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"), - "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), "admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"), + "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), + "admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"), + "admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"), "admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"), + "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), + "admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"), + "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), + "ausweise": await import("../src/pages/api/ausweise/index.ts"), + "auth/access-token": await import("../src/pages/api/auth/access-token.ts"), + "auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"), + "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), + "bedarfsausweis-gewerbe/[uid]": await import("../src/pages/api/bedarfsausweis-gewerbe/[uid].ts"), + "bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"), + "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), + "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), - "rechnung": await import("../src/pages/api/rechnung/index.ts"), + "geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"), + "geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"), + "geg-nachweis-wohnen/[uid]": await import("../src/pages/api/geg-nachweis-wohnen/[uid].ts"), + "geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"), + "objekt": await import("../src/pages/api/objekt/index.ts"), "rechnung/[uid]": await import("../src/pages/api/rechnung/[uid].ts"), "rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"), - "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), - "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), - "webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"), - "verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"), - "verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"), - "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), - "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), - "verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"), - "bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"), - "bedarfsausweis-gewerbe/[uid]": await import("../src/pages/api/bedarfsausweis-gewerbe/[uid].ts"), - "auth/access-token": await import("../src/pages/api/auth/access-token.ts"), - "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), - "auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"), - "user/self": await import("../src/pages/api/user/self.ts"), + "rechnung": await import("../src/pages/api/rechnung/index.ts"), + "ticket": await import("../src/pages/api/ticket/index.ts"), "user": await import("../src/pages/api/user/index.ts"), - "objekt": await import("../src/pages/api/objekt/index.ts"), - "geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"), - "geg-nachweis-wohnen/[uid]": await import("../src/pages/api/geg-nachweis-wohnen/[uid].ts"), - "aufnahme/[uid]/unterlagen": await import("../src/pages/api/aufnahme/[uid]/unterlagen.ts"), + "user/self": await import("../src/pages/api/user/self.ts"), + "verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"), + "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), + "verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"), + "verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"), + "webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"), "aufnahme/[uid]/bilder": await import("../src/pages/api/aufnahme/[uid]/bilder.ts"), "aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid]/index.ts"), + "aufnahme/[uid]/unterlagen": await import("../src/pages/api/aufnahme/[uid]/unterlagen.ts"), "objekt/[uid]": await import("../src/pages/api/objekt/[uid]/index.ts"), }) \ No newline at end of file diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 0923ea31..484328a2 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -94,14 +94,4 @@ export const SERVICES: Record< }; export const BASE_URI = process.env.NODE_ENV == "production" ? "https://online-energieausweis.org" : "http://localhost:3000"; -export const LEX_OFFICE_API_KEY = process.env.NODE_ENV == "production" ? "iwQLCU_ZAq6bVV7hmR8RO8MiC8Q" : "znjmkmbA3Hbx9dC7wdKp7TnOf1pcRl_tCUwEBZys7bj-QRPG" - -export const s3Client = new S3Client({ - region: "eu-central-1", - endpoint: "https://s3-eu-central-1.ionoscloud.com", - credentials: { - accessKeyId: "EEAAAAHYzkBh2aRNC4OEVfqCRCviXoIZ3wm9UieAVCeLbWnJrQAAAAECGy6_AAAAAAIbLr9zLHhcZE7kGJngOPTFoODh", - secretAccessKey: "zlRI0jK+A8CIxir0QPdXNFUV+L9XjFTGyBvdUT1dvgY4FNlsOJgNJ+5xW5oShzmy", - }, - forcePathStyle: true, - }); \ No newline at end of file +export const LEX_OFFICE_API_KEY = process.env.NODE_ENV == "production" ? "iwQLCU_ZAq6bVV7hmR8RO8MiC8Q" : "znjmkmbA3Hbx9dC7wdKp7TnOf1pcRl_tCUwEBZys7bj-QRPG" \ No newline at end of file diff --git a/src/lib/s3.ts b/src/lib/s3.ts new file mode 100644 index 00000000..31e1f36d --- /dev/null +++ b/src/lib/s3.ts @@ -0,0 +1,44 @@ +import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import * as fs from "fs"; + +export const s3Client = new S3Client({ + region: "eu-central-1", + endpoint: "https://s3-eu-central-1.ionoscloud.com", + credentials: { + accessKeyId: + "EEAAAAHYzkBh2aRNC4OEVfqCRCviXoIZ3wm9UieAVCeLbWnJrQAAAAECGy6_AAAAAAIbLr9zLHhcZE7kGJngOPTFoODh", + secretAccessKey: + "zlRI0jK+A8CIxir0QPdXNFUV+L9XjFTGyBvdUT1dvgY4FNlsOJgNJ+5xW5oShzmy", + }, + forcePathStyle: true, +}); + +export async function getS3File( + bucket: string, + key: string +): Promise { + try { + let command = new GetObjectCommand({ Bucket: bucket, Key: key }); + let response = await s3Client.send(command); + + const body = response.Body; + + if (!body) { + return null; + } + + let buffer = await streamToBuffer(body as unknown as fs.ReadStream); + return buffer; + } catch (e) { + return null; + } +} + +async function streamToBuffer(stream: fs.ReadStream): Promise { + return new Promise((resolve, reject) => { + const chunks: any[] = []; + stream.on("data", (chunk) => chunks.push(chunk)); + stream.on("error", reject); + stream.on("end", () => resolve(Buffer.concat(chunks))); + }); +} diff --git a/src/pages/api/bild.ts b/src/pages/api/bild.ts index ea881609..37fbb957 100644 --- a/src/pages/api/bild.ts +++ b/src/pages/api/bild.ts @@ -10,7 +10,7 @@ import { BildSchema } from "src/generated/zod/bild.js"; import sharp from "sharp" import { PERSISTENT_DIR } from "#lib/server/constants.js"; import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; -import { s3Client } from "#lib/constants.js"; +import { s3Client } from "#lib/s3.js"; export const PUT = defineApiRoute({ input: BildSchema.pick({ diff --git a/src/pages/bilder/[uid].jpg.ts b/src/pages/bilder/[uid].jpg.ts index cb508fac..0642fc5f 100644 --- a/src/pages/bilder/[uid].jpg.ts +++ b/src/pages/bilder/[uid].jpg.ts @@ -1,8 +1,6 @@ import { prisma } from "#lib/server/prisma.js"; import { APIRoute } from "astro"; -import * as fs from "fs"; -import { GetObjectCommand } from "@aws-sdk/client-s3"; -import { s3Client } from "#lib/constants.js"; +import { getS3File } from "#lib/s3.js"; export const GET: APIRoute = async (Astro) => { const { uid } = Astro.params; @@ -19,42 +17,18 @@ export const GET: APIRoute = async (Astro) => { }); } - try { - // Attempt to retrieve the image by UID - let command = new GetObjectCommand({ Bucket: "ibc-images", Key: `${image.uid}.jpg` }); - let response = await s3Client.send(command); - let buffer = await streamToBuffer(response.Body); - return new Response(buffer, { status: 200 }); + const file = await getS3File("ibc-images", `${image.uid}.jpg`); - } catch (error) { - if (error.name === "NoSuchKey") { - // If not found, attempt to retrieve by image name - try { - let command = new GetObjectCommand({ Bucket: "ibc-images", Key: `${image.name}.jpg` }); - let response = await s3Client.send(command); - let buffer = await streamToBuffer(response.Body); - return new Response(buffer, { status: 200 }); - } catch (fallbackError) { - if (fallbackError.name === "NoSuchKey") { - // Image not found - return new Response(null, { status: 404 }); - } else { - // Other errors - return new Response(null, { status: 500 }); - } - } - } else { - // Other errors - return new Response(null, { status: 500 }); - } - } + if (!file) { + const file = await getS3File("ibc-images", `${image.name}.jpg`) + + if (!file) { + return new Response(null, { status: 404 }) + } + + return new Response(file, { status: 200 }) + } + + return new Response(file, { status: 200 }) }; -async function streamToBuffer(stream: fs.ReadStream) { - return new Promise((resolve, reject) => { - const chunks: any[] = []; - stream.on("data", (chunk) => chunks.push(chunk)); - stream.on("error", reject); - stream.on("end", () => resolve(Buffer.concat(chunks))); - }); -}