fix: refactoring of storage method

This commit is contained in:
charlesgauthereau
2026-01-24 13:14:38 +01:00
parent a64a80910d
commit b2c19e7ed0
6 changed files with 77 additions and 31 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ services:
# context: .
# dockerfile: docker/dockerfile/Dockerfile
# target: prod
image: solucetechnologies/portabase:1.2.2-rc.2
image: solucetechnologies/portabase:latest
ports:
- '8887:80'
environment:
+1
View File
@@ -87,6 +87,7 @@
"react-twc": "^1.4.2",
"react-use-measure": "^2.1.7",
"recharts": "^2.15.3",
"sharp": "^0.34.5",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"sonner": "^2.0.3",
+20 -18
View File
@@ -124,7 +124,7 @@ importers:
version: 6.0.0
better-auth:
specifier: 1.4.5
version: 1.4.5(next@16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
version: 1.4.5(next@16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -169,10 +169,10 @@ importers:
version: 12.24.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next:
specifier: 16.0.10
version: 16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
version: 16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-safe-action:
specifier: ^7.10.8
version: 7.10.8(next@16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76)
version: 7.10.8(next@16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -227,6 +227,9 @@ importers:
recharts:
specifier: ^2.15.3
version: 2.15.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
sharp:
specifier: ^0.34.5
version: 0.34.5
socket.io:
specifier: ^4.8.1
version: 4.8.3
@@ -10133,7 +10136,7 @@ snapshots:
dependencies:
'@types/node': 22.19.3
tapable: 2.3.0
webpack: 5.104.1(esbuild@0.27.2)
webpack: 5.104.1(esbuild@0.25.10)
transitivePeerDependencies:
- '@swc/core'
- esbuild
@@ -10730,7 +10733,7 @@ snapshots:
bcryptjs@2.4.3: {}
better-auth@1.4.5(next@16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
better-auth@1.4.5(next@16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@better-auth/core': 1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0)
'@better-auth/telemetry': 1.4.5(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0))
@@ -10746,7 +10749,7 @@ snapshots:
nanostores: 1.1.0
zod: 4.3.5
optionalDependencies:
next: 16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
@@ -12660,9 +12663,9 @@ snapshots:
neo-async@2.6.2: {}
next-safe-action@7.10.8(next@16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76):
next-safe-action@7.10.8(next@16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76):
dependencies:
next: 16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
@@ -12691,12 +12694,12 @@ snapshots:
'@next/swc-linux-x64-musl': 15.5.2
'@next/swc-win32-arm64-msvc': 15.5.2
'@next/swc-win32-x64-msvc': 15.5.2
sharp: 0.34.4
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@16.0.10(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
next@16.0.10(@babel/core@7.26.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.0.10
'@swc/helpers': 0.5.15
@@ -12704,7 +12707,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.0.10
'@next/swc-darwin-x64': 16.0.10
@@ -13531,7 +13534,6 @@ snapshots:
'@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5
optional: true
shebang-command@2.0.0:
dependencies:
@@ -13785,12 +13787,12 @@ snapshots:
optionalDependencies:
'@babel/core': 7.26.10
styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.3):
styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.2.3):
dependencies:
client-only: 0.0.1
react: 19.2.3
optionalDependencies:
'@babel/core': 7.28.5
'@babel/core': 7.26.10
sucrase@3.35.1:
dependencies:
@@ -13878,14 +13880,14 @@ snapshots:
ansi-escapes: 4.3.2
supports-hyperlinks: 2.3.0
terser-webpack-plugin@5.3.16(esbuild@0.25.10)(webpack@5.104.1(esbuild@0.25.10)):
terser-webpack-plugin@5.3.16(esbuild@0.25.10)(webpack@5.104.1(esbuild@0.27.2)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
serialize-javascript: 6.0.2
terser: 5.44.1
webpack: 5.104.1(esbuild@0.27.2)
webpack: 5.104.1(esbuild@0.25.10)
optionalDependencies:
esbuild: 0.25.10
@@ -14240,7 +14242,7 @@ snapshots:
webpack-sources@3.3.3: {}
webpack@5.104.1(esbuild@0.27.2):
webpack@5.104.1(esbuild@0.25.10):
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.8
@@ -14264,7 +14266,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.0
terser-webpack-plugin: 5.3.16(esbuild@0.25.10)(webpack@5.104.1(esbuild@0.25.10))
terser-webpack-plugin: 5.3.16(esbuild@0.25.10)(webpack@5.104.1(esbuild@0.27.2))
watchpack: 2.5.0
webpack-sources: 3.3.3
transitivePeerDependencies:
@@ -48,14 +48,25 @@ export const AvatarWithUpload = (props: AvatarWithUploadProps) => {
const handleImageUpload = async (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
if (!file.type.includes("image")) {
toast.error("File not an image");
const allowedFormats = ["image/jpeg", "image/png", "image/webp"];
if (!allowedFormats.includes(file.type)) {
toast.error("Only JPG, PNG or WebP images are allowed.");
return;
}
const maxSizeInMB = 5;
if (file.size > maxSizeInMB * 1024 * 1024) {
toast.error(`Image must be smaller than ${maxSizeInMB}MB.`);
return;
}
submitImage.mutate(file);
};
return (
<div className="relative ">
@@ -68,7 +79,7 @@ export const AvatarWithUpload = (props: AvatarWithUploadProps) => {
onClick={() => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/*";
fileInput.accept = ".jpg,.jpeg,.png,.webp";
// @ts-ignore
fileInput.onchange = handleImageUpload;
fileInput.click();
+26 -8
View File
@@ -1,6 +1,6 @@
import * as Minio from "minio";
import {StorageDeleteInput, StorageGetInput, StorageResult, StorageUploadInput} from "../types";
import {getServerUrl} from "@/utils/get-server-url";
import {StorageDeleteInput, StorageGetInput, StorageMetaData, StorageResult, StorageUploadInput} from "../types";
import {generateFileUrl} from "@/features/storages/helpers";
type S3Config = {
endPointUrl: string;
@@ -30,7 +30,10 @@ async function ensureBucket(config: S3Config) {
if (!exists) await client.makeBucket(config.bucketName);
}
export async function uploadS3(config: S3Config, input: { data: StorageUploadInput }): Promise<StorageResult> {
export async function uploadS3(
config: S3Config,
input: { data: StorageUploadInput, metadata?: StorageMetaData }
): Promise<StorageResult> {
const client = await getS3Client(config);
await ensureBucket(config);
@@ -44,16 +47,32 @@ export async function uploadS3(config: S3Config, input: { data: StorageUploadInp
}
await client.putObject(config.bucketName, key, input.data.file as Buffer);
const baseUrl = getServerUrl();
if (input.data.url) {
const url = await generateFileUrl(input);
if (!url) {
return {
success: false,
provider: "s3",
response: "Unable to get url file"
};
}
return {
success: true,
provider: 's3',
url: url
};
}
return {
success: true,
provider: 's3',
url: `${baseUrl}/api/${input.data.path}`,
};
}
export async function getS3(config: S3Config, input: { data: StorageGetInput }): Promise<StorageResult> {
export async function getS3(config: S3Config, input: { data: StorageGetInput, metadata: StorageMetaData }): Promise<StorageResult> {
const client = await getS3Client(config);
const key = `${BASE_DIR}${input.data.path}`;
@@ -78,9 +97,8 @@ export async function getS3(config: S3Config, input: { data: StorageGetInput }):
};
}
export async function deleteS3(config: S3Config, input: { data: StorageDeleteInput }): Promise<StorageResult> {
export async function deleteS3(config: S3Config, input: { data: StorageDeleteInput, metadata?: StorageMetaData }): Promise<StorageResult> {
const client = await getS3Client(config);
// const key = input.data.path;
const key = `${BASE_DIR}${input.data.path}`;
try {
+15 -1
View File
@@ -8,6 +8,7 @@ import * as drizzleDb from "@/db";
import {ServerActionResult} from "@/types/action-type";
import {dispatchStorage} from "@/features/storages/dispatch";
import {StorageInput} from "@/features/storages/types";
import sharp from "sharp";
export const uploadUserImageAction = userAction.schema(
@@ -22,6 +23,19 @@ export const uploadUserImageAction = userAction.schema(
const buffer = Buffer.from(arrayBuffer);
const isPng = fileFormat === "png";
const isWebp = fileFormat === "webp";
const compressedBuffer = await sharp(buffer)
.resize({ width: 1024 })
.toFormat(isPng ? "png" : isWebp ? "webp" : "jpeg", {
quality: 80,
compressionLevel: isPng ? 9 : undefined
})
.toBuffer();
const settings = await db.query.setting.findFirst({
where: eq(drizzleDb.schemas.setting.name, "system"),
with: {
@@ -45,7 +59,7 @@ export const uploadUserImageAction = userAction.schema(
action: "upload",
data: {
path: path,
file: buffer,
file: compressedBuffer,
url: true
},
metadata: {