fix: working on adding internal postgres database.

This commit is contained in:
charlesgauthereau
2026-02-14 22:23:04 +01:00
parent 12535a59e8
commit b4da6fdd9f
16 changed files with 118 additions and 454 deletions
+3 -1
View File
@@ -1,6 +1,7 @@
import {NextResponse} from "next/server";
import fs from "fs";
import path from "path";
import {env} from "@/env.mjs";
export async function POST(request: Request) {
@@ -29,7 +30,8 @@ export async function POST(request: Request) {
}
const uploadDir = path.join(process.cwd(), "/private/uploads/");
// const uploadDir = path.join(process.cwd(), "/private/uploads/");
const uploadDir = path.join(env.PRIVATE_PATH, "/uploads/");
const oldFilePath = path.join(uploadDir, "tmp", id);
const newFilePath = path.join(uploadDir, filePath);
+4 -2
View File
@@ -4,20 +4,21 @@ services:
context: .
dockerfile: docker/dockerfile/Dockerfile
target: prod
image: portabase/portabase:latest
# image: portabase/portabase:latest
ports:
- '8887:80'
environment:
TZ: "Europe/Paris"
env_file:
- .env
volumes:
- portabase-data:/data
depends_on:
db:
condition: service_healthy
container_name: portabase-app-prod
db:
container_name: portabase-db-prod
image: postgres:16-alpine
ports:
- "5433:5432"
@@ -34,4 +35,5 @@ services:
retries: 5
volumes:
portabase-data:
postgres-data:
+23 -3
View File
@@ -18,6 +18,21 @@ RUN apt-get update && apt-get install -y \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt bullseye-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list
RUN apt-get update && apt-get install -y \
postgresql-18 \
postgresql-client-18 \
postgresql-contrib-18 \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/usr/lib/postgresql/18/bin:${PATH}"
FROM --platform=$BUILDPLATFORM golang:1.23-bookworm AS tusd-base
ARG TUSD_VERSION=2.8.0
@@ -76,6 +91,9 @@ ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=80
ENV HOSTNAME="0.0.0.0"
ENV PGDATA=/data/postgres
ENV PRIVATE_PATH=/data/private
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
@@ -94,14 +112,16 @@ RUN chmod +x /usr/local/bin/tusd
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
#RUN mkdir -p .next /app/private/uploads \
# && chown -R nextjs:nodejs .next /app/private /app/public
RUN mkdir -p .next /data/private/uploads \
&& chown -R nextjs:nodejs .next /data/private /app/public
RUN mkdir -p .next /app/private/uploads \
&& chown -R nextjs:nodejs .next /app/private /app/public
USER root
COPY ./docker/entrypoints/app-prod-entrypoint.sh /app/app-prod-entrypoint.sh
RUN chmod +x /app/app-prod-entrypoint.sh
EXPOSE 80
#USER nextjs
ENTRYPOINT ["sh","/app/app-prod-entrypoint.sh"]
+46 -2
View File
@@ -7,9 +7,53 @@ else
echo "[WARN] No TZ provided, using default container timezone"
fi
mkdir -p /app/private/uploads/tmp
POSTGRES_BIN=$(ls -d /usr/lib/postgresql/*/bin | head -n 1)
if [ -z "$POSTGRES_BIN" ]; then
echo "PostgreSQL binaries not found"
exit 1
fi
export PATH="$POSTGRES_BIN:$PATH"
if [ -z "$DATABASE_URL" ]; then
echo "[INFO] No DATABASE_URL provided, starting internal Postgres..."
mkdir -p "$PGDATA"
chown -R postgres:postgres "$PGDATA"
if [ ! -f "$PGDATA/PG_VERSION" ]; then
echo "[INFO] Initializing database cluster..."
su postgres -c "initdb -D '$PGDATA'"
fi
su postgres -c "pg_ctl -D '$PGDATA' -o \"-c listen_addresses='localhost'\" -w start"
until pg_isready -h 127.0.0.1 -p 5432; do
echo "Waiting for Postgres..."
sleep 1
done
DB_USER="${POSTGRES_USER:-portabase_user}"
DB_PASS="${POSTGRES_PASSWORD:-JaB6b1SUtIWYvt7srnOt}"
DB_NAME="${POSTGRES_DB:-portabase_db}"
USER_EXISTS=$(su postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'\"")
if [ "$USER_EXISTS" != "1" ]; then
su postgres -c "psql -c \"CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';\""
fi
DB_EXISTS=$(su - postgres -c "psql -tAc \"SELECT 1 FROM pg_database WHERE datname='$DB_NAME'\"")
if [ "$DB_EXISTS" != "1" ]; then
su postgres -c "psql -c \"CREATE DATABASE $DB_NAME OWNER $DB_USER;\""
fi
export DATABASE_URL="postgres://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME"
fi
mkdir -p /data/private/uploads/tmp
echo "▶ Starting tusd server..."
tusd --base-path /tus/files/ --upload-dir /app/private/uploads/tmp --hooks-http http://127.0.0.1:3000/api/tus/hooks --port 1080 --max-size 21474836480 &
tusd --base-path /tus/files/ --upload-dir /data/private/uploads/tmp --hooks-http http://127.0.0.1:3000/api/tus/hooks --port 1080 --max-size 21474836480 &
echo "▶ Starting Next.js server..."
PORT=3000 node server.js &
@@ -8,7 +8,7 @@ import {v4 as uuidv4} from "uuid";
import {eq} from "drizzle-orm";
import {z} from "zod";
import {storeBackupFiles} from "@/features/storages/helpers";
import {getFileExtension} from "@/features/api/upload/helpers/file";
import {getFileExtension} from "@/utils/common";
export const uploadBackupAction = userAction
@@ -46,18 +46,6 @@ export const uploadBackupAction = userAction
const fileName = `${uuid}${fileExtension}`;
const buffer = Buffer.from(arrayBuffer);
// const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
//
// if (!settings) {
// return {
// success: false,
// actionError: {
// message: "Settings not set",
// status: 500,
// cause: "Unknown error",
// },
// };
// }
const [backup] = await db
.insert(drizzleDb.schemas.backup)
@@ -68,30 +56,8 @@ export const uploadBackupAction = userAction
})
.returning();
await storeBackupFiles(backup, database, buffer, fileName)
// let success: boolean, message: string, filePath: string;
//
// const result =
// settings.storage === "local"
// ? await uploadLocalPrivate(fileName, buffer)
// : await uploadS3Private(`${database.project?.slug}/${fileName}`, buffer, env.S3_BUCKET_NAME!);
//
// ({success, message, filePath} = result);
//
// if (!success) {
// return {
// success: false,
// actionError: {
// message: "An error has occurred while uploading file",
// status: 500,
// cause: "Unknown error",
// },
// };
// }
return {
success: true,
value: backup,
+4
View File
@@ -1,6 +1,7 @@
import {createEnv} from "@t3-oss/env-nextjs";
import {z} from "zod";
import packageJson from "../package.json" with {type: "json"};
import path from "path";
const {version} = packageJson;
@@ -41,6 +42,8 @@ export const env = createEnv({
.string()
.default(process.env.NODE_ENV === "production" ? "0 7 * * *" : "* * * * *"),
PRIVATE_PATH: z.string(),
},
client: {
NEXT_PUBLIC_PROJECT_VERSION: z.string().optional(),
@@ -77,5 +80,6 @@ export const env = createEnv({
RETENTION_CRON: process.env.RETENTION_CRON,
PRIVATE_PATH: process.env.PRIVATE_PATH || path.join(process.cwd(), 'private')
},
});
-6
View File
@@ -1,6 +0,0 @@
import {Request, Response, NextFunction} from "express";
export function loggingMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`[API - SERVICES - V1] Received ${req.method} request : ${req.url} at ${new Date().toISOString()}`);
next();
}
-10
View File
@@ -1,10 +0,0 @@
import express, { Router } from "express";
import uploadRouter from "./upload";
import { loggingMiddleware } from "@/features/api/middleware";
const router: Router = express.Router();
router.use(loggingMiddleware);
router.use("/upload", uploadRouter);
export default router;
-134
View File
@@ -1,134 +0,0 @@
import fs from "fs";
import {Backup, DatabaseWith} from "@/db/schema/07_database";
import {dispatchStorage} from "@/features/storages/dispatch";
import type {StorageInput, StorageResult} from "@/features/storages/types";
import {db} from "@/db";
import * as drizzleDb from "@/db";
import {eq} from "drizzle-orm";
import {withUpdatedAt} from "@/db/utils";
import {sendNotificationsBackupRestore} from "@/features/notifications/helpers";
import {eventEmitter} from "@/features/shared/event";
import forge from "node-forge";
import crypto from "node:crypto";
/**
* Save stream to a temporary file and upload to all storage providers in parallel.
*/
export default async function uploadTempFileToProviders(
backup: Backup,
database: DatabaseWith,
// inputStream: NodeJS.ReadableStream,
tmpPath: string,
fileName: string
): Promise<StorageResult[]> {
const stats = fs.statSync(tmpPath);
const fileSize = stats.size;
console.log(tmpPath);
const settings = await db.query.setting.findFirst({
where: eq(drizzleDb.schemas.setting.name, "system"),
with: {storageChannel: true},
});
const defaultPolicy = settings?.storageChannel
? [{
id: null,
storageChannelId: settings.storageChannel.id,
enabled: settings.storageChannel.enabled,
}]
: [];
const enabledPolicies = database.storagePolicies?.filter(p => p.enabled) ?? [];
const policies = enabledPolicies.length ? enabledPolicies : defaultPolicy;
if (!policies.length) {
await db.update(drizzleDb.schemas.backup)
.set(withUpdatedAt({status: "failed"}))
.where(eq(drizzleDb.schemas.backup.id, backup.id));
fs.existsSync(tmpPath) && fs.unlinkSync(tmpPath);
return [];
}
const storagePath = `backups/${database.project?.slug}/${fileName}`;
const results = await Promise.all(
policies.map(async (policy) => {
const fileStream = fs.createReadStream(tmpPath);
const [backupStorage] = await db.insert(drizzleDb.schemas.backupStorage)
.values({
backupId: backup.id,
storageChannelId: policy.storageChannelId,
status: "pending",
path: storagePath,
})
.returning();
const input: StorageInput = {
action: "upload",
data: {path: storagePath, file: fileStream, size: fileSize},
};
let result: StorageResult;
try {
result = policy.id
? await dispatchStorage(input, policy.id)
: await dispatchStorage(input, undefined, policy.storageChannelId);
} catch (err: any) {
console.error(err);
result = {success: false, provider: null, error: err.message};
}
await db.update(drizzleDb.schemas.backupStorage)
.set(withUpdatedAt({status: result.success ? "success" : "failed"}))
.where(eq(drizzleDb.schemas.backupStorage.id, backupStorage.id));
return result;
})
);
const backupStatus = results.some(r => r.success) ? "success" : "failed";
await db.update(drizzleDb.schemas.backup)
.set(withUpdatedAt({
fileSize: fileSize,
status: backupStatus
}))
.where(eq(drizzleDb.schemas.backup.id, backup.id));
fs.existsSync(tmpPath) && fs.unlinkSync(tmpPath);
console.log(results);
eventEmitter.emit('modification', {update: true});
if (!backupStatus) {
await sendNotificationsBackupRestore(database, "error_backup");
}
await sendNotificationsBackupRestore(database, "success_backup");
return results;
}
export function createDecryptionStream(
encryptedAesKeyHex: string,
ivHex: string
) {
const privateKeyPem = fs.readFileSync(
"private/keys/server_private.pem",
"utf8"
);
const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
const encryptedBytes = forge.util.hexToBytes(encryptedAesKeyHex);
const aesKeyBytes = privateKey.decrypt(encryptedBytes, "RSA-OAEP", {
md: forge.md.sha256.create(),
mgf1: {md: forge.md.sha256.create()},
});
const aesKey = Buffer.from(aesKeyBytes, "binary");
const iv = Buffer.from(ivHex, "hex");
return crypto.createDecipheriv("aes-256-cbc", aesKey, iv);
}
-27
View File
@@ -1,27 +0,0 @@
import fs from "fs";
import path from "path";
import {promisify} from "util";
import {pipeline} from "stream";
const pipelineAsync = promisify(pipeline);
const TMP_DIR = path.join(process.cwd(), "private/uploads/tmp");
fs.mkdirSync(TMP_DIR, { recursive: true });
export async function saveStreamToTempFile(stream: NodeJS.ReadableStream, fileName: string): Promise<string> {
const tmpPath = path.join(TMP_DIR, fileName);
const writeStream = fs.createWriteStream(tmpPath);
await pipelineAsync(stream, writeStream);
return tmpPath;
}
export function getFileExtension(dbType: string) {
switch (dbType) {
case "postgresql":
return ".dump";
case "mysql":
return ".sql";
default:
return ".dump";
}
}
-201
View File
@@ -1,201 +0,0 @@
import express, {Request, Response, Router} from "express";
import {v4 as uuidv4} from "uuid";
import {and, eq} from "drizzle-orm";
import * as drizzleDb from "@/db";
import {db} from "@/db";
import {isUuidv4} from "@/utils/verify-uuid";
import uploadTempFileToProviders, {createDecryptionStream} from "@/features/api/upload/helpers/common";
import {sendNotificationsBackupRestore} from "@/features/notifications/helpers";
import {Backup} from "@/db/schema/07_database";
import {withUpdatedAt} from "@/db/utils";
import {eventEmitter} from "@/features/shared/event";
import {getFileExtension, saveStreamToTempFile} from "@/features/api/upload/helpers/file";
const router: Router = express.Router();
// router.post("/:agentId", async (req: Request, res: Response) => {
// try {
// const agentId = req.params.agentId as string | undefined;
// const generatedId = req.headers["x-generated-id"] as string | undefined;
// const status = req.headers["x-status"] as string | undefined;
// const encryptedAesKeyHex = req.headers["x-aes-key"] as string | undefined;
// const ivHex = req.headers["x-iv"] as string | undefined;
// const method = (req.headers["x-method"] as string) ?? "manual";
// const extension = req.headers["x-extension"] as string | undefined;
//
//
// if (!generatedId || !encryptedAesKeyHex || !ivHex || !agentId || !status) {
// return res.status(400).json({error: "Missing required headers/params"});
// }
//
// if (!isUuidv4(generatedId)) {
// return res.status(400).json({error: "generatedId is not a valid UUID"});
// }
//
// const agent = await db.query.agent.findFirst({
// where: eq(drizzleDb.schemas.agent.id, agentId),
// });
//
// if (!agent) {
// return res.status(404).json({error: "Agent not found"});
// }
//
//
// const database = await db.query.database.findFirst({
// where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
// with: {
// project: true,
// storagePolicies: true,
// },
// });
//
// if (!database) {
// return res.status(404).json({error: "Database not found"});
// }
//
// let backup: Backup | null | undefined = null;
//
// if (method === "automatic") {
// [backup] = await db
// .insert(drizzleDb.schemas.backup)
// .values({
// status: "ongoing",
// databaseId: database.id,
// })
// .returning();
// } else {
// backup = await db.query.backup.findFirst({
// where: and(
// eq(drizzleDb.schemas.backup.databaseId, database.id),
// eq(drizzleDb.schemas.backup.status, "ongoing")
// ),
// });
// }
//
// if (!backup) {
// return res.status(404).json({error: "Backup not found"});
// }
//
//
// if (status === "success") {
//
// const decipher = createDecryptionStream(encryptedAesKeyHex, ivHex);
//
// const fileExt = extension || getFileExtension(database.dbms);
// const fileName = `${uuidv4()}${fileExt}`;
//
// const decryptedStream = req.pipe(decipher);
//
// const tmpPath = await saveStreamToTempFile(decryptedStream, fileName);
//
//
//
//
// if (!tmpPath) {
// return res.status(500).json({
// error: "Unable to save tmp backup file",
// });
// }
//
// uploadTempFileToProviders(backup, database, tmpPath, fileName);
//
// return res.json({success: true});
// } else {
// await db
// .update(drizzleDb.schemas.backup)
// .set(withUpdatedAt({status: 'failed'}))
// .where(eq(drizzleDb.schemas.backup.id, backup.id));
//
// eventEmitter.emit('modification', {update: true});
//
// await sendNotificationsBackupRestore(database, "error_backup");
//
// return res.status(200).json({
// error: "Backup successfully updated with status failed",
// });
// }
// } catch (err: any) {
// console.error("Upload error:", err);
// return res.status(500).json({
// error: "Upload failed",
// detail: err.message,
// });
// }
// });
router.post("/:agentId", async (req: Request, res: Response) => {
const agentId = req.params.agentId as string | undefined;
const generatedId = req.headers["x-generated-id"] as string | undefined;
const status = req.headers["x-status"] as string | undefined;
const encryptedAesKeyHex = req.headers["x-aes-key"] as string | undefined;
const ivHex = req.headers["x-iv"] as string | undefined;
const method = (req.headers["x-method"] as string) ?? "manual";
const extension = req.headers["x-extension"] as string | undefined;
if (!generatedId || !encryptedAesKeyHex || !ivHex || !agentId || !status) {
return res.status(400).json({error: "Missing required headers/params"});
}
if (!isUuidv4(generatedId)) {
return res.status(400).json({error: "generatedId is not a valid UUID"});
}
const agent = await db.query.agent.findFirst({ where: eq(drizzleDb.schemas.agent.id, agentId) });
if (!agent) return res.status(404).json({error: "Agent not found"});
const database = await db.query.database.findFirst({
where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
with: { project: true, storagePolicies: true },
});
if (!database) return res.status(404).json({error: "Database not found"});
let backup: Backup | null = null;
if (method === "automatic") {
[backup] = await db.insert(drizzleDb.schemas.backup).values({ status: "ongoing", databaseId: database.id }).returning();
} else {
backup = await db.query.backup.findFirst({
where: and(
eq(drizzleDb.schemas.backup.databaseId, database.id),
eq(drizzleDb.schemas.backup.status, "ongoing")
),
});
}
if (!backup) return res.status(404).json({error: "Backup not found"});
res.status(202).json({success: true, message: "Backup received, processing in background"});
(async () => {
try {
if (status === "success") {
const decipher = createDecryptionStream(encryptedAesKeyHex!, ivHex!);
const fileExt = extension || getFileExtension(database.dbms);
const fileName = `${uuidv4()}${fileExt}`;
const decryptedStream = req.pipe(decipher);
const tmpPath = await saveStreamToTempFile(decryptedStream, fileName);
if (!tmpPath) throw new Error("Unable to save tmp backup file");
await uploadTempFileToProviders(backup!, database, tmpPath, fileName);
} else {
await db.update(drizzleDb.schemas.backup)
.set(withUpdatedAt({status: 'failed'}))
.where(eq(drizzleDb.schemas.backup.id, backup!.id));
eventEmitter.emit('modification', {update: true});
await sendNotificationsBackupRestore(database, "error_backup");
}
} catch (err) {
console.error("Background backup processing failed:", err);
try {
await db.update(drizzleDb.schemas.backup)
.set(withUpdatedAt({status: 'failed'}))
.where(eq(drizzleDb.schemas.backup.id, backup!.id));
} catch (_) {
}
}
})();
});
export default router;
+6 -2
View File
@@ -1,4 +1,6 @@
import fs from "node:fs";
import {env} from "@/env.mjs";
import path from "path";
/**
@@ -6,7 +8,8 @@ import fs from "node:fs";
*/
export function getPublicServerKeyContent() {
try {
return fs.readFileSync("private/keys/server_public.pem", "utf8");
const keyPath = path.join(env.PRIVATE_PATH, '/keys/server_public.pem')
return fs.readFileSync(keyPath, "utf8");
} catch (error: any) {
console.error("Error :", error);
return {
@@ -22,7 +25,8 @@ export function getPublicServerKeyContent() {
*/
export function getMasterServerKeyContent() {
try {
return fs.readFileSync("private/keys/master_key.bin");
const keyPath = path.join(env.PRIVATE_PATH, '/keys/master_key.bin')
return fs.readFileSync(keyPath);
} catch (error: any) {
console.error("Error :", error);
return {
+14 -9
View File
@@ -5,15 +5,17 @@ import {StorageDeleteInput, StorageGetInput, StorageMetaData, StorageResult, Sto
import fs from "node:fs";
import {generateFileUrl} from "@/features/storages/helpers";
import {Readable} from "node:stream";
import {env} from "@/env.mjs";
const BASE_DIR = "/private/uploads/";
const BASE_DIR = path.join(env.PRIVATE_PATH, '/uploads')
export async function uploadLocal(
config: { baseDir?: string },
input: { data: StorageUploadInput; metadata?: StorageMetaData }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, input.data.path);
const base = config.baseDir ? path.join(process.cwd(), config.baseDir ?? "") : BASE_DIR;
const fullPath = path.join(base, input.data.path);
const dir = path.dirname(fullPath);
await mkdir(dir, { recursive: true });
@@ -52,8 +54,9 @@ export async function getLocal(
config: { baseDir?: string },
input: { data: StorageGetInput; metadata: StorageMetaData }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const filePath = path.join(process.cwd(), base, input.data.path);
const base = config.baseDir ? path.join(process.cwd(), config.baseDir ?? "") : BASE_DIR;
const filePath = path.join(base, input.data.path);
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
@@ -108,8 +111,10 @@ export async function deleteLocal(
config: { baseDir?: string },
input: { data: StorageDeleteInput, metadata?: StorageMetaData }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, input.data.path);
const base = config.baseDir ? path.join(process.cwd(), config.baseDir ?? "") : BASE_DIR;
const fullPath = path.join(base, input.data.path);
await unlink(fullPath);
return {
success: true,
@@ -121,8 +126,8 @@ export async function pingLocal(
config: { baseDir?: string }
): Promise<StorageResult> {
const base = config.baseDir || BASE_DIR;
const fullPath = path.join(process.cwd(), base, "ping.txt");
const base = path.join(process.cwd(), config.baseDir ?? "") || BASE_DIR;
const fullPath = path.join(base, "ping.txt");
await fs.promises.writeFile(fullPath, "ping");
await fs.promises.readFile(fullPath);
+3 -20
View File
@@ -16,6 +16,9 @@ const privateS3Dir = "backups/";
export async function uploadLocalPrivate(fileName: string, buffer: any) {
try {
const privatePath = path.join(env.PRIVATE_PATH!, '/keys/master_key.bin')
await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
await writeFile(path.join(process.cwd(), privateLocalDir, fileName), buffer);
@@ -96,26 +99,6 @@ export async function deleteLocalPrivate(fileName: string) {
}
// export async function getFileUrlPresignedLocal(fileName: string) {
// try {
// const filePath = path.join(privateLocalDir, fileName);
// await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
//
// if (!fs.existsSync(filePath)) {
// console.error("File not found at:", filePath);
// return `File not found at: ${filePath}`;
// }
// const crypto = require("crypto");
// const baseUrl = getServerUrl();
//
// const expiresAt = Date.now() + 60 * 1000; // expires in 1 minute
// const token = crypto.createHash("sha256").update(`${fileName}${expiresAt}`).digest("hex");
// return `${baseUrl}/api/files/${fileName}?token=${token}&expires=${expiresAt}`;
// } catch (error) {
// throw error;
// }
// }
export async function getFileUrlPresignedS3(fileName: string) {
try {
return await createPresignedUrlToDownload({
+10
View File
@@ -39,3 +39,13 @@ export function buildOrganizationWithMembers(
export function getFileExtension(dbType: string) {
switch (dbType) {
case "postgresql":
return ".dump";
case "mysql":
return ".sql";
default:
return ".dump";
}
}
+4 -2
View File
@@ -3,6 +3,7 @@ import path from 'path';
import {generateKeyPair} from 'crypto';
import {promisify} from 'util';
import {randomBytes} from 'crypto';
import {env} from "@/env.mjs";
const generateKeyPairAsync = promisify(generateKeyPair);
@@ -13,9 +14,10 @@ const generateKeyPairAsync = promisify(generateKeyPair);
* @param {string} [dir] path to directory
* @returns {Promise<{privateKeyPath:string, publicKeyPath:string}>}
*/
export async function generateRSAKeys(dir = path.join(process.cwd(), 'private/keys')) {
export async function generateRSAKeys(dir = path.join(env.PRIVATE_PATH, '/keys')) {
await fs.mkdir(dir, {recursive: true});
const privateKeyPath = path.join(dir, 'server_private.pem');
const publicKeyPath = path.join(dir, 'server_public.pem');
@@ -47,7 +49,7 @@ export async function generateRSAKeys(dir = path.join(process.cwd(), 'private/ke
* @param {string} [filePath] Path to store the key
* @returns {Promise<Buffer>} The master key
*/
export async function getOrCreateMasterKey(filePath = path.join(process.cwd(), 'private/keys', 'master_key.bin')) {
export async function getOrCreateMasterKey(filePath = path.join(env.PRIVATE_PATH, '/keys', 'master_key.bin')) {
await fs.mkdir(path.dirname(filePath), {recursive: true});