feat: Working on backend storage providers.

This commit is contained in:
charlesgauthereau
2026-01-13 22:22:01 +01:00
parent 67a89a12ac
commit d8e9e2dac3
15 changed files with 2506 additions and 108 deletions
+54
View File
@@ -0,0 +1,54 @@
import type {
StorageProviderKind,
StorageInput,
StorageResult,
} from '../types';
import {uploadLocal, getLocal, deleteLocal} from './local';
type ProviderHandler = {
upload: (config: any, input: StorageInput & { action: 'upload' }) => Promise<StorageResult>;
get: (config: any, input: StorageInput & { action: 'get' }) => Promise<StorageResult>;
delete: (config: any, input: StorageInput & { action: 'delete' }) => Promise<StorageResult>;
};
const handlers: Record<StorageProviderKind, ProviderHandler> = {
local: {
upload: uploadLocal,
get: getLocal,
delete: deleteLocal,
},
// s3: {
// upload: uploadS3,
// get: getS3,
// delete: deleteS3,
// },
// gcs: null as any,
// azure: null as any,
};
export async function dispatchViaProvider(
kind: StorageProviderKind,
config: any,
input: StorageInput
): Promise<StorageResult> {
const provider = handlers[kind];
if (!provider) {
return {
success: false,
provider: kind,
error: `Unsupported storage provider: ${kind}`,
};
}
try {
return await provider[input.action](config, input as any);
} catch (err: any) {
return {
success: false,
provider: kind,
error: err.message || 'Storage provider error',
};
}
}
+69
View File
@@ -0,0 +1,69 @@
"use server"
import {mkdir, writeFile, unlink, readFile} from 'fs/promises';
import path from 'path';
import {StorageDeleteInput, StorageGetInput, StorageResult, StorageUploadInput} from '../types';
import fs from "node:fs";
import {getServerUrl} from "@/utils/get-server-url";
const BASE_DIR = "/private/uploads/files/";
export async function uploadLocal(
config: { baseDir?: string },
input: { data: StorageUploadInput }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, input.data.path);
await mkdir(fullPath, {recursive: true});
await writeFile(fullPath, input.data.file);
return {
success: true,
provider: 'local',
url: path.join(fullPath),
};
}
export async function getLocal(
config: { baseDir?: string },
input: { data: StorageGetInput }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const filePath = path.join(base, input.data.path)
const fileName = path.basename(input.data.path);
const file = await readFile(filePath);
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
return({
success: false,
provider: 'local',
});
}
const crypto = require("crypto");
const baseUrl = getServerUrl();
const expiresAt = Date.now() + 60 * 1000;
const token = crypto.createHash("sha256").update(`${fileName}${expiresAt}`).digest("hex");
return {
success: true,
provider: 'local',
file: file,
url: `${baseUrl}/api/files/${fileName}?token=${token}&expires=${expiresAt}`,
};
}
export async function deleteLocal(
config: { baseDir?: string },
input: { data: StorageDeleteInput }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, input.data.path);
await unlink(fullPath);
return {
success: true,
provider: 'local',
};
}