mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: refactoring, adding express api.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user