Skip to content

Commit

Permalink
feat: use scaleway s3 bucket
Browse files Browse the repository at this point in the history
feat: use scaleway s3 bucket
  • Loading branch information
ledouxm authored Jun 12, 2024
2 parents f180586 + d3f6aae commit 98603b4
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 15 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ name: Create and publish a Docker image

on:
push:
branches: ["development", "main"]
branches:
- main
- development

env:
REGISTRY: ghcr.io
Expand Down Expand Up @@ -32,7 +34,7 @@ jobs:
context: .
push: true
target: backend
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend:latest
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:backend

build-and-push-frontend:
runs-on: ubuntu-latest
Expand All @@ -56,4 +58,4 @@ jobs:
context: .
push: true
target: frontend
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend:latest
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:frontend
4 changes: 2 additions & 2 deletions ci/docker-compose.development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ volumes:

services:
frontend:
image: ghcr.io/betagouv/compte-rendu-vif-development-frontend:latest
image: ghcr.io/betagouv/compte-rendu-vif-development:frontend
restart: always
depends_on:
- electric

backend:
image: ghcr.io/betagouv/compte-rendu-vif-development-backend:latest
image: ghcr.io/betagouv/compte-rendu-vif-development:backend
restart: always
depends_on:
- electric
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/openapi.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"openapi":"3.1.0","info":{"title":"CR VIF API","description":"CR VIF API Documentation","version":"1.0"},"components":{"schemas":{}},"paths":{"/api/create-user":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"udap_id":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}},"required":["name","udap_id","email","password"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/login":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/refresh-token":{"get":{"parameters":[{"schema":{"type":"string"},"in":"query","name":"token","required":true},{"schema":{"type":"string"},"in":"query","name":"refreshToken","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/send-reset-password":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}}}}},"/api/reset-password":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"temporaryLink":{"type":"string"},"newPassword":{"type":"string"}},"required":["temporaryLink","newPassword"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}}}}},"/api/udaps":{"get":{"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}}}}}}}},"/api/pdf/report":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"htmlString":{"type":"string"},"reportId":{"type":"string"},"recipients":{"type":"string"}},"required":["htmlString","reportId","recipients"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"string"}}}}}}}}}
{"openapi":"3.1.0","info":{"title":"CR VIF API","description":"CR VIF API Documentation","version":"1.0"},"components":{"schemas":{}},"paths":{"/api/create-user":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"udap_id":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}},"required":["name","udap_id","email","password"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/login":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/refresh-token":{"get":{"parameters":[{"schema":{"type":"string"},"in":"query","name":"token","required":true},{"schema":{"type":"string"},"in":"query","name":"refreshToken","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"udap_id":{"type":"string"},"udap":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}},"required":["id","name","udap_id","udap"]},"token":{"type":"string"},"refreshToken":{"type":"string"}},"required":["token","refreshToken"]}}}}}}},"/api/send-reset-password":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}}}}},"/api/reset-password":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"temporaryLink":{"type":"string"},"newPassword":{"type":"string"}},"required":["temporaryLink","newPassword"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}}}}},"/api/udaps":{"get":{"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"department":{"type":"string"},"completeCoords":{"type":"string"},"visible":{"type":"boolean"},"name":{"type":"string"},"address":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"required":["id","department"]}}}}}}}},"/api/pdf/report":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"htmlString":{"type":"string"},"reportId":{"type":"string"},"recipients":{"type":"string"}},"required":["htmlString","reportId","recipients"]}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"string"}}}}}},"get":{"parameters":[{"schema":{"type":"string"},"in":"query","name":"reportId","required":true}],"responses":{"200":{"description":"Default Response"}}}}}}
1 change: 1 addition & 0 deletions packages/backend/src/envVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const envSchema = z.object({
PORT: stringOrNumberAsNumber.default(3001),
AWS_BUCKET_NAME: z.string(),
AWS_REGION: z.string(),
AWS_ENDPOINT: z.string(),
DEBUG: z.string().default("cr-vif:*"),
FRONTEND_URL: z.string(),
EMAIL_HOST: z.string(),
Expand Down
20 changes: 19 additions & 1 deletion packages/backend/src/routes/pdfRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Udap } from "@cr-vif/electric-client/frontend";
import { authenticate } from "./authMiddleware";
import { db } from "../db/db";
import { sendReportMail } from "../features/mail";
import { getPDFName } from "../services/uploadService";

export const pdfPlugin: FastifyPluginAsyncTypebox = async (fastify, _) => {
fastify.addHook("preHandler", authenticate);
Expand All @@ -15,7 +16,7 @@ export const pdfPlugin: FastifyPluginAsyncTypebox = async (fastify, _) => {

const pdf = await generatePdf({ htmlString, udap });

const name = `${reportId}/compte_rendu.pdf`;
const name = getPDFName(reportId);

const url = await request.services.upload.addPDFToReport({
reportId,
Expand All @@ -35,6 +36,23 @@ export const pdfPlugin: FastifyPluginAsyncTypebox = async (fastify, _) => {

return url;
});

fastify.get(
"/report",
{
schema: {
querystring: Type.Object({ reportId: Type.String() }),
response: { 200: Type.Any() },
},
},
async (request) => {
console.log("salut");
const { reportId } = request.query;
const buffer = await request.services.upload.getReportPDF({ reportId });

return buffer.toString("base64");
},
);
};

const generatePdf = async ({ htmlString, udap }: { htmlString: string; udap: Udap }) => {
Expand Down
28 changes: 22 additions & 6 deletions packages/backend/src/services/uploadService.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import { S3Client, ListBucketsCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { ENV } from "../envVars";
import { makeDebug } from "../features/debug";
import { AppError } from "../features/errors";

const client = new S3Client({ region: ENV.AWS_REGION });
const command = new ListBucketsCommand("");

const client = new S3Client({ endpoint: ENV.AWS_ENDPOINT, region: ENV.AWS_REGION });
const debug = makeDebug("upload");
export const upload = async () => {};

export class UploadService {
async addPDFToReport({ reportId, buffer, name }: { reportId: string; buffer: Buffer; name: string }) {
debug("Uploading PDF to S3", reportId);
const command = new PutObjectCommand({ Bucket: ENV.AWS_BUCKET_NAME, Body: buffer, Key: name });
await client.send(command);

const url = `https://${ENV.AWS_BUCKET_NAME}.s3.${ENV.AWS_REGION}.amazonaws.com/${name}`;
const url = `https://${ENV.AWS_BUCKET_NAME}.s3.${ENV.AWS_REGION}.scw.cloud/${name}`;

debug(url);
return url;
}

async getReportPDF({ reportId }: { reportId: string }) {
const name = getPDFName(reportId);

const command = new GetObjectCommand({ Bucket: ENV.AWS_BUCKET_NAME, Key: name });
const response = await client.send(command);

const buffer = await response.Body?.transformToByteArray();
if (!buffer) throw new AppError(404, "PDF not found");

return Buffer.from(buffer);
}
}

export const getPDFName = (reportId: string) => `${reportId}/compte_rendu.pdf`;
9 changes: 9 additions & 0 deletions packages/frontend/src/api.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ export namespace Endpoints {
};
response: string;
};
export type get_Apipdfreport = {
method: "GET";
path: "/api/pdf/report";
parameters: {
query: { reportId: string };
};
response: unknown;
};

// </Endpoints>
}
Expand All @@ -153,6 +161,7 @@ export type EndpointByMethod = {
get: {
"/api/refresh-token": Endpoints.get_ApirefreshToken;
"/api/udaps": Endpoints.get_Apiudaps;
"/api/pdf/report": Endpoints.get_Apipdfreport;
};
};

Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const AuthProvider = ({ children }: PropsWithChildren) => {
console.log("token refreshed");
setDataAndSaveInStorage(resp);
}
console.log(resp);
return resp;
} catch (e) {
console.error("refreshTokenQuery error", e);
Expand Down
8 changes: 7 additions & 1 deletion packages/frontend/src/features/ReportActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { v4 } from "uuid";
import { omit } from "pastable";
import { ReportWithUser } from "./ReportList";
import { useNavigate } from "@tanstack/react-router";
import { api } from "../api";

export const ReportActions = forwardRef<HTMLDivElement, { report: ReportWithUser }>(({ report }, ref) => {
const user = useUser()!;
Expand All @@ -18,6 +19,11 @@ export const ReportActions = forwardRef<HTMLDivElement, { report: ReportWithUser

const navigate = useNavigate();

const downloadPdfMutation = useMutation(async () => {
const buffer = await api.get("/api/pdf/report", { query: { reportId: report.id } });
return downloadFile(`data:application/pdf;base64,${buffer}`);
});

const deleteMutation = useDeleteMutation();
const duplicateMutation = useMutation(async () => {
const payload = omit(report, ["id", "createdAt", "pdf", "user", "title"]);
Expand Down Expand Up @@ -50,7 +56,7 @@ export const ReportActions = forwardRef<HTMLDivElement, { report: ReportWithUser
/>
) : null}
{report.pdf ? (
<ReportAction iconId="ri-download-line" label="Télécharger" onClick={() => downloadFile(report.pdf!)} />
<ReportAction iconId="ri-download-line" label="Télécharger" onClick={() => downloadPdfMutation.mutate()} />
) : null}
<ReportAction iconId="ri-file-add-line" label="Dupliquer" onClick={() => duplicateMutation.mutate()} />
</Stack>
Expand Down

0 comments on commit 98603b4

Please sign in to comment.