diff --git a/.env.dist b/.env.dist index 95678427..f36179a9 100644 --- a/.env.dist +++ b/.env.dist @@ -5,6 +5,9 @@ POSTGRES_USER= POSTGRES_PASSWORD= POSTGRES_DB= +MINIO_USER= +MINIO_PASSWORD= + PG_PROXY_PORT= PG_PROXY_PASSWORD= @@ -22,6 +25,17 @@ EMAIL_PORT= EMAIL_USER= EMAIL_PASSWORD= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_BUCKET_NAME= +AWS_REGION= +AWS_ENDPOINT= + +MINIO_URL= +MINIO_BUCKET= +MINIO_ACCESS_KEY_ID= +MINIO_SECRET_KEY= + # FRONTEND VITE_BACKEND_URL= VITE_ELECTRIC_URL= diff --git a/docker-compose.yaml b/docker-compose.yaml index 8b854cd5..e4cdb781 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,6 +2,7 @@ version: "3.1" volumes: pg_data: + minio_data: services: # frontend: @@ -57,3 +58,15 @@ services: restart: always ports: - "${ADMINER_PORT:-8081}:8080" + + minio: + image: minio/minio + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio_data:/data + environment: + MINIO_ROOT_USER: ${MINIO_USER} + MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD} + command: server --console-address ":9001" /data diff --git a/packages/backend/src/envVars.ts b/packages/backend/src/envVars.ts index feca2f26..26c9a3b3 100644 --- a/packages/backend/src/envVars.ts +++ b/packages/backend/src/envVars.ts @@ -28,6 +28,10 @@ const envSchema = z.object({ EMAIL_USER: z.string(), EMAIL_PASSWORD: z.string(), EMAIL_EMITTER: z.string(), + MINIO_ACCESS_KEY_ID: z.string(), + MINIO_SECRET_KEY: z.string(), + MINIO_URL: z.string(), + MINIO_BUCKET: z.string(), }); export const ENV = envSchema.parse(process.env); diff --git a/packages/backend/src/routes/uploadRoutes.tsx b/packages/backend/src/routes/uploadRoutes.tsx index fab13ed4..51361562 100644 --- a/packages/backend/src/routes/uploadRoutes.tsx +++ b/packages/backend/src/routes/uploadRoutes.tsx @@ -22,11 +22,10 @@ export const uploadPlugin: FastifyPluginAsyncTypebox = async (fastify, _) => { if (!file) throw new AppError(400, "No file provided"); if (!reportId || !id) throw new AppError(400, "No reportId or id provided"); - const url = await request.services.upload.addPDFToReport({ + const url = await request.services.upload.addPictureToReport({ reportId: (request.query as any).reportId as string, buffer: await file.toBuffer(), name: getPictureName(reportId, id), - publicRead: true, }); // await db.pictures.create({ data: { id, url, reportId, createdAt: new Date() } }); diff --git a/packages/backend/src/services/uploadService.ts b/packages/backend/src/services/uploadService.ts index 42886981..d3d1ea4d 100644 --- a/packages/backend/src/services/uploadService.ts +++ b/packages/backend/src/services/uploadService.ts @@ -2,9 +2,20 @@ import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3 import { ENV } from "../envVars"; import { makeDebug } from "../features/debug"; import { AppError } from "../features/errors"; +import { S3 } from "@aws-sdk/client-s3"; const client = new S3Client({ endpoint: ENV.AWS_ENDPOINT, region: ENV.AWS_REGION }); const debug = makeDebug("upload"); + +const imageClient = new S3({ + credentials: { + accessKeyId: ENV.MINIO_ACCESS_KEY_ID, + secretAccessKey: ENV.MINIO_SECRET_KEY, + }, + endpoint: ENV.MINIO_URL, + bucketEndpoint: true, +}); + export const upload = async () => {}; export class UploadService { @@ -34,6 +45,25 @@ export class UploadService { return url; } + async addPictureToReport({ reportId, buffer, name }: { reportId: string; buffer: Buffer; name: string }) { + debug("Uploading picture to S3", reportId); + + const bucketUrl = `${ENV.MINIO_URL}/${ENV.MINIO_BUCKET}`; + + await imageClient.putObject({ + Bucket: bucketUrl, + Key: name, + Body: buffer, + ACL: "public-read", + ContentType: "image/png", + }); + + const url = `${bucketUrl}/${name}`; + + debug(url); + return url; + } + async getReportPDF({ reportId }: { reportId: string }) { const name = getPDFName(reportId); diff --git a/packages/frontend/src/envVars.ts b/packages/frontend/src/envVars.ts index d363114f..4f74390c 100644 --- a/packages/frontend/src/envVars.ts +++ b/packages/frontend/src/envVars.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -const isDev = !z.boolean().parse(import.meta.env.PROD); +export const isDev = !z.boolean().parse(import.meta.env.PROD); const envSchema = z.object({ VITE_BACKEND_URL: z.string(), diff --git a/packages/frontend/src/features/InfoForm.tsx b/packages/frontend/src/features/InfoForm.tsx index 4e1b0bcd..a1ab1175 100644 --- a/packages/frontend/src/features/InfoForm.tsx +++ b/packages/frontend/src/features/InfoForm.tsx @@ -299,6 +299,7 @@ const PictureThumbnail = ({ picture, index, status }: { picture: Pictures; index return URL.createObjectURL(blob); }, + refetchOnWindowFocus: false, }); const idbStatusQuery = useQuery({ diff --git a/packages/frontend/src/service-worker/sw.ts b/packages/frontend/src/service-worker/sw.ts index ef92b112..6587f9f5 100644 --- a/packages/frontend/src/service-worker/sw.ts +++ b/packages/frontend/src/service-worker/sw.ts @@ -3,19 +3,27 @@ import { apiStore, createApiClientWithUrl, getTokenFromIdb } from "../api"; import { getPicturesStore, getToUploadStore, getUploadStatusStore } from "../features/idb"; import { get, keys, del, set } from "idb-keyval"; import { NavigationRoute, registerRoute } from "workbox-routing"; +import { isDev } from "../envVars"; declare let self: ServiceWorkerGlobalScope; -skipWaiting(); -clients.claim(); +self.addEventListener("install", (event) => { + event.waitUntil(self.skipWaiting()); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil(self.clients.claim()); +}); const manif = self.__WB_MANIFEST; console.log(manif); cleanupOutdatedCaches(); precacheAndRoute(manif); -const handler = createHandlerBoundToURL("/index.html"); -registerRoute(new NavigationRoute(handler)); +if (!isDev) { + const handler = createHandlerBoundToURL("/index.html"); + registerRoute(new NavigationRoute(handler)); +} const broadcastChannel = new BroadcastChannel("sw-messages"); diff --git a/packages/pdf/src/report.tsx b/packages/pdf/src/report.tsx index f95e54fe..89faf7ae 100644 --- a/packages/pdf/src/report.tsx +++ b/packages/pdf/src/report.tsx @@ -29,93 +29,93 @@ export const ReportPDFDocument = ({ udap, htmlString, images, pictures }: Report >{` - +