Working on the s3 feature.

This commit is contained in:
charlesgauthereau
2025-07-29 09:53:38 +02:00
parent d3f465b5d4
commit a6b960ffb2
5 changed files with 45 additions and 49 deletions
+9 -1
View File
@@ -2,9 +2,17 @@ import path from "path";
import fs from "fs/promises"; import fs from "fs/promises";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
export async function GET({ params }: { params: Promise<{ fileName: string }> }) { export async function GET(
request: Request,
{params}: { params: Promise<{ fileName: string }> }
) {
try { try {
const fileName = (await params).fileName; const fileName = (await params).fileName;
console.log("fileName", fileName);
const filePath = path.join(process.cwd(), "private/uploads/images", fileName); const filePath = path.join(process.cwd(), "private/uploads/images", fileName);
// Check if the file exists // Check if the file exists
+3
View File
@@ -53,16 +53,19 @@ services:
# - db # - db
s3: s3:
container_name: s3-portabase-dev
image: docker.io/bitnami/minio:latest image: docker.io/bitnami/minio:latest
ports: ports:
- "9000:9000" - "9000:9000"
- "9001:9001" - "9001:9001"
volumes: volumes:
- minio_data:/data - minio_data:/data
environment: environment:
- MINIO_ROOT_USER=${S3_ACCESS_KEY} - MINIO_ROOT_USER=${S3_ACCESS_KEY}
- MINIO_ROOT_PASSWORD=${S3_SECRET_KEY} - MINIO_ROOT_PASSWORD=${S3_SECRET_KEY}
- MINIO_DEFAULT_BUCKETS=${S3_BUCKET_NAME} - MINIO_DEFAULT_BUCKETS=${S3_BUCKET_NAME}
- MINIO_BROWSER=on
volumes: volumes:
postgres-data: postgres-data:
@@ -1,16 +1,19 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
import { Info, ShieldCheck } from "lucide-react"; import {Info, ShieldCheck} from "lucide-react";
import { Switch } from "@/components/ui/switch"; import {Switch} from "@/components/ui/switch";
import { Label } from "@/components/ui/label"; import {Label} from "@/components/ui/label";
import { StorageS3Form } from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/storage-s3-form"; import {StorageS3Form} from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/storage-s3-form";
import { useState } from "react"; import {useState} from "react";
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading"; import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import { useMutation } from "@tanstack/react-query"; import {useMutation} from "@tanstack/react-query";
import { checkConnexionToS3 } from "@/features/upload/public/upload.action"; import {checkConnexionToS3} from "@/features/upload/public/upload.action";
import { toast } from "sonner"; import {toast} from "sonner";
import { useRouter } from "next/navigation"; import {useRouter} from "next/navigation";
import { updateStorageSettingsAction } from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/s3-form.action"; import {
updateStorageSettingsAction
} from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/s3-form.action";
import {Setting} from "@/db/schema/00_setting"; import {Setting} from "@/db/schema/00_setting";
import {S3FormType} from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/s3-form.schema";
export type SettingsStorageTabProps = { export type SettingsStorageTabProps = {
settings: Setting; settings: Setting;
@@ -33,7 +36,7 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
const [isSwitched, setIsSwitched] = useState<boolean>(props.settings.storage !== "local"); const [isSwitched, setIsSwitched] = useState<boolean>(props.settings.storage !== "local");
const updateMutation = useMutation({ const updateMutation = useMutation({
mutationFn: () => updateStorageSettingsAction({ name: "system", data: { storage: isSwitched ? "s3" : "local" } }), mutationFn: () => updateStorageSettingsAction({name: "system", data: {storage: isSwitched ? "s3" : "local"}}),
onSuccess: () => { onSuccess: () => {
toast.success(`Settings updated successfully.`); toast.success(`Settings updated successfully.`);
router.refresh(); router.refresh();
@@ -48,14 +51,25 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
await updateMutation.mutateAsync(); await updateMutation.mutateAsync();
}; };
const extractS3FormValues = (settings: Setting): S3FormType | undefined => {
if (!settings.s3EndPointUrl) return undefined;
return {
s3EndPointUrl: settings.s3EndPointUrl,
s3AccessKeyId: settings.s3AccessKeyId!,
s3SecretAccessKey: settings.s3SecretAccessKey!,
S3BucketName: settings.S3BucketName!,
};
};
return ( return (
<div className="flex flex-col h-full py-4"> <div className="flex flex-col h-full py-4">
<h1>Settings for Portabase storage</h1> <h1>Settings for Portabase storage</h1>
<Alert className="mt-3"> <Alert className="mt-3">
<Info className="h-4 w-4" /> <Info className="h-4 w-4"/>
<AlertTitle>Informations</AlertTitle> <AlertTitle>Informations</AlertTitle>
<AlertDescription> <AlertDescription>
Actually you can only store you data in one place : s3 compatible or in local. For exemple you cannot choose to store images in one place Actually you can only store you data in one place : s3 compatible or in local. For exemple you
cannot choose to store images in one place
and .dump files in another. and .dump files in another.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@@ -79,14 +93,14 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
onClick={async () => { onClick={async () => {
await mutation.mutateAsync(); await mutation.mutateAsync();
}} }}
icon={<ShieldCheck />} icon={<ShieldCheck/>}
text="Test connexion" text="Test connexion"
/> />
</div> </div>
</div> </div>
{isSwitched && ( {isSwitched && (
<div className="mt-5"> <div className="mt-5">
<StorageS3Form defaultValues={props.settings.s3EndPointUrl ? props.settings : null} /> <StorageS3Form defaultValues={extractS3FormValues(props.settings)}/>
</div> </div>
)} )}
</div> </div>
@@ -53,6 +53,8 @@ export const AvatarWithUpload = (props: AvatarWithUploadProps) => {
submitImage.mutate(file); submitImage.mutate(file);
}; };
return ( return (
<div className="relative "> <div className="relative ">
<Avatar className="size-14 mr-3 "> <Avatar className="size-14 mr-3 ">
-31
View File
@@ -5,37 +5,6 @@ import {db} from "@/db";
import * as drizzleDb from "@/db"; import * as drizzleDb from "@/db";
import {eq} from "drizzle-orm"; import {eq} from "drizzle-orm";
// const settings = await prisma.settings.findUnique({
// where:{
// name: "system"
// }
// })
// Create a new Minio client with the S3 endpoint, access key, and secret key
// export const s3Client = env.NODE_ENV === "production" ?
// new Minio.Client({
// endPoint: env.S3_ENDPOINT ?? "",
// accessKey: env.S3_ACCESS_KEY ?? "",
// secretKey: env.S3_SECRET_KEY ?? "",
// }) : new Minio.Client({
// endPoint: env.S3_ENDPOINT ?? "",
// port: Number(env.S3_PORT ?? 0),
// accessKey: env.S3_ACCESS_KEY ?? "",
// secretKey: env.S3_SECRET_KEY ?? "",
// useSSL: env.S3_USE_SSL === 'true'
// })
// export const s3Client = env.NODE_ENV === "production" ?
// new Minio.Client({
// endPoint: settings.s3EndPointUrl ?? "",
// accessKey: settings.s3AccessKeyId ?? "",
// secretKey: settings.s3SecretAccessKey ?? "",
// }) : new Minio.Client({
// endPoint: settings.s3EndPointUrl ?? "",
// port: Number(env.S3_PORT ?? 0),
// accessKey: settings.s3AccessKeyId ?? "",
// secretKey: settings.s3SecretAccessKey ?? "",
// useSSL: env.S3_USE_SSL === 'true'
// })
async function getS3Client() { async function getS3Client() {
const settings = await db const settings = await db