fix: refactoring, adding express api.

This commit is contained in:
charlesgauthereau
2026-01-31 22:15:01 +01:00
parent 7c5d57ddc1
commit cc87cf68b5
33 changed files with 1028 additions and 142 deletions
@@ -24,18 +24,27 @@ export async function uploadGoogleDrive(
? await ensureFolderPath(client, folderPath, config.folderId)
: config.folderId;
const existing = await findFileByName(client, fileName, folderId);
if (existing) return {success: false, provider: "google-drive", error: "File already exists"};
let fileStream: Readable;
const file = input.data.file;
if (Buffer.isBuffer(file) || file instanceof Uint8Array) {
fileStream = Readable.from(file);
} else if ((file as any).pipe) {
fileStream = file as Readable;
} else {
throw new Error("Unsupported file type for streaming upload");
}
await client.files.create({
requestBody: {name: fileName, parents: [folderId]},
media: {body: Readable.from(input.data.file as Buffer)},
media: {body: fileStream},
fields: "id",
supportsAllDrives: true,
});
if (input.data.url) {
const url = await generateFileUrl(input);
if (!url) {
@@ -68,11 +77,15 @@ export async function getGoogleDrive(
const fileId = await resolveFilePath(client, input.data.path, config.folderId);
if (!fileId) return {success: false, provider: "google-drive", error: "File not found"};
const res = await client.files.get(
{fileId, alt: "media", supportsAllDrives: true},
{responseType: "arraybuffer"}
{responseType: "stream"}
);
const stream = res.data as Readable;
if (input.data.signedUrl) {
const url = await generateFileUrl(input);
@@ -88,7 +101,7 @@ export async function getGoogleDrive(
return {
success: true,
provider: "google-drive",
file: Buffer.from(res.data as ArrayBuffer),
file: stream,
url: url,
};
}
@@ -96,7 +109,7 @@ export async function getGoogleDrive(
return {
success: true,
provider: "google-drive",
file: Buffer.from(res.data as ArrayBuffer),
file: stream,
};
}
@@ -1,9 +1,3 @@
// export type GoogleDriveConfig = {
// clientEmail: string;
// privateKey: string;
// folderId: string;
// };
export type GoogleDriveConfig = {
clientId: string;
+55 -41
View File
@@ -1,95 +1,109 @@
"use server"
import {mkdir, writeFile, unlink, readFile} from 'fs/promises';
import {mkdir, unlink} from 'fs/promises';
import path from 'path';
import {StorageDeleteInput, StorageGetInput, StorageMetaData, StorageResult, StorageUploadInput} from '../types';
import fs from "node:fs";
import {getServerUrl} from "@/utils/get-server-url";
import {generateFileUrl} from "@/features/storages/helpers";
import {Readable} from "node:stream";
const BASE_DIR = "/private/uploads/";
export async function uploadLocal(
config: { baseDir?: string },
input: { data: StorageUploadInput, metadata?: StorageMetaData }
input: { data: StorageUploadInput; metadata?: StorageMetaData }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, input.data.path);
const dir = path.dirname(fullPath);
await mkdir(dir, {recursive: true});
await writeFile(fullPath, input.data.file);
await mkdir(dir, { recursive: true });
if (input.data.url) {
const url = await generateFileUrl(input);
if (!url) {
return {
success: false,
provider: "local",
response: "Unable to get url file"
};
try {
const file = input.data.file;
if (Buffer.isBuffer(file)) {
await fs.promises.writeFile(fullPath, input.data.file);
} else if (file instanceof Readable) {
await new Promise<void>((resolve, reject) => {
const writable = fs.createWriteStream(fullPath);
file.pipe(writable);
writable.on("finish", resolve);
writable.on("error", reject);
});
} else {
return { success: false, provider: "local", error: "Unsupported file type. Must be Buffer or ReadableStream" };
}
return {
success: true,
provider: 'local',
url: url
};
if (input.data.url) {
const url = await generateFileUrl(input);
if (!url) {
return { success: false, provider: "local", response: "Unable to get URL" };
}
return { success: true, provider: "local", url };
}
return { success: true, provider: "local" };
} catch (err: any) {
try { await unlink(fullPath); } catch {}
return { success: false, provider: "local", error: err.message || "Upload failed" };
}
return {
success: true,
provider: 'local',
};
}
export async function getLocal(
config: { baseDir?: string },
input: { data: StorageGetInput, metadata: StorageMetaData }
input: { data: StorageGetInput; metadata: StorageMetaData }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const filePath = path.join(process.cwd(), base, input.data.path)
const fileName = path.basename(input.data.path);
const file = await readFile(filePath);
const filePath = path.join(process.cwd(), base, input.data.path);
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
return ({
return {
success: false,
provider: 'local',
});
provider: "local",
error: "File not found",
};
}
let fileStream: fs.ReadStream | undefined;
try {
fileStream = fs.createReadStream(filePath);
} catch (err: any) {
console.error("Error creating read stream:", err);
return {
success: false,
provider: "local",
error: err.message,
};
}
if (input.data.signedUrl) {
const url = await generateFileUrl(input);
if (!url) {
return {
success: false,
provider: "local",
response: "Unable to get url file"
error: "Unable to generate signed URL",
};
}
return {
success: true,
provider: "local",
file: file,
url: url,
file: fileStream,
url,
};
}
return {
success: true,
provider: "local",
file: file,
file: fileStream,
};
}
export async function deleteLocal(
config: { baseDir?: string },
input: { data: StorageDeleteInput, metadata?: StorageMetaData }
+37 -39
View File
@@ -1,6 +1,6 @@
import * as Minio from "minio";
import {StorageDeleteInput, StorageGetInput, StorageMetaData, StorageResult, StorageUploadInput} from "../types";
import {generateFileUrl} from "@/features/storages/helpers";
import {Readable} from "node:stream";
type S3Config = {
endPointUrl: string;
@@ -32,72 +32,70 @@ async function ensureBucket(config: S3Config) {
export async function uploadS3(
config: S3Config,
input: { data: StorageUploadInput, metadata?: StorageMetaData }
input: { data: StorageUploadInput, metadata?: StorageMetaData }
): Promise<StorageResult> {
const client = await getS3Client(config);
console.log(client);
await ensureBucket(config);
const key = `${BASE_DIR}${input.data.path}`;
const file = input.data.file;
console.log(key)
let uploadStream: Readable;
if (Buffer.isBuffer(file) || file instanceof Uint8Array) {
uploadStream = Readable.from(file);
} else if ((file as any).pipe) {
uploadStream = file;
} else {
throw new Error("Unsupported file type for streaming upload");
}
try {
await client.statObject(config.bucketName, key);
return {success: false, provider: "s3", error: "File already exists"};
} catch {
// continue if not found
const result = await client.putObject(config.bucketName, key, uploadStream, input.data.size);
console.log(result);
} catch (err: any) {
return {success: false, provider: "s3", error: err.message};
}
await client.putObject(config.bucketName, key, input.data.file as Buffer);
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',
};
return {success: true, provider: "s3"};
}
export async function getS3(config: S3Config, input: { data: StorageGetInput, metadata: StorageMetaData }): Promise<StorageResult> {
const client = await getS3Client(config);
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}`;
try {
await client.statObject(config.bucketName, key);
} catch {
return {success: false, provider: "s3", error: "File not found"};
}
const presignedUrl = await client.presignedGetObject(config.bucketName, key, 60);
const fileStream = await client.getObject(config.bucketName, key);
const chunks: Buffer[] = [];
for await (const chunk of fileStream) chunks.push(chunk as Buffer);
const buffer = Buffer.concat(chunks);
let presignedUrl: string | undefined;
if (input.data.signedUrl) {
presignedUrl = await client.presignedGetObject(config.bucketName, key, input.data.expiresInSeconds ?? 60);
}
return {
success: true,
provider: "s3",
file: buffer,
file: fileStream as unknown as Buffer | Readable,
url: presignedUrl,
};
}
export async function deleteS3(config: S3Config, input: { data: StorageDeleteInput, metadata?: StorageMetaData }): Promise<StorageResult> {
export async function deleteS3(config: S3Config, input: {
data: StorageDeleteInput,
metadata?: StorageMetaData
}): Promise<StorageResult> {
const client = await getS3Client(config);
const key = `${BASE_DIR}${input.data.path}`;
+6 -3
View File
@@ -1,3 +1,5 @@
import {Readable} from "node:stream";
export type StorageProviderKind =
| 'local'
| 's3'
@@ -20,9 +22,10 @@ export type StorageMetaData = {
export interface StorageUploadInput {
path: string;
file: Buffer | Uint8Array;
file: Readable | Buffer | Uint8Array;
url?: boolean;
contentType?: string;
size?: number;
}
export interface StorageGetInput {
@@ -37,7 +40,7 @@ export interface StorageDeleteInput {
export type StorageInput =
| { action: 'upload'; data: StorageUploadInput, metadata?: StorageMetaData }
| { action: 'get'; data: StorageGetInput, metadata: StorageMetaData}
| { action: 'get'; data: StorageGetInput, metadata: StorageMetaData }
| { action: 'delete'; data: StorageDeleteInput, metadata?: StorageMetaData }
| { action: 'ping'; };
@@ -45,7 +48,7 @@ export interface StorageResult {
success: boolean;
provider: StorageProviderKind | null;
url?: string;
file?: Buffer;
file?: Buffer | Readable;
error?: string;
response?: any;
}