mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
chore(release): 1.2.5-rc.8
This commit is contained in:
+1
-1
@@ -26,5 +26,5 @@ keywords:
|
|||||||
- web-ui
|
- web-ui
|
||||||
- agent
|
- agent
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
version: 1.2.5-rc.7
|
version: 1.2.5-rc.8
|
||||||
date-released: "2026-02-01"
|
date-released: "2026-02-01"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portabase",
|
"name": "portabase",
|
||||||
"version": "1.2.5-rc.7",
|
"version": "1.2.5-rc.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_ENV=development tsx watch server/server.ts",
|
"dev": "NODE_ENV=development tsx watch server/server.ts",
|
||||||
|
|||||||
+173
-101
@@ -13,117 +13,189 @@ import {getFileExtension, saveStreamToTempFile} from "@/features/api/upload/help
|
|||||||
|
|
||||||
const router: Router = express.Router();
|
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) => {
|
router.post("/:agentId", async (req: Request, res: Response) => {
|
||||||
try {
|
const agentId = req.params.agentId as string | undefined;
|
||||||
const agentId = req.params.agentId as string | undefined;
|
const generatedId = req.headers["x-generated-id"] as string | undefined;
|
||||||
const generatedId = req.headers["x-generated-id"] as string | undefined;
|
const status = req.headers["x-status"] as string | undefined;
|
||||||
const status = req.headers["x-status"] as string | undefined;
|
const encryptedAesKeyHex = req.headers["x-aes-key"] as string | undefined;
|
||||||
const encryptedAesKeyHex = req.headers["x-aes-key"] as string | undefined;
|
const ivHex = req.headers["x-iv"] as string | undefined;
|
||||||
const ivHex = req.headers["x-iv"] as string | undefined;
|
const method = (req.headers["x-method"] as string) ?? "manual";
|
||||||
const method = (req.headers["x-method"] as string) ?? "manual";
|
const extension = req.headers["x-extension"] as string | undefined;
|
||||||
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"});
|
||||||
|
}
|
||||||
|
|
||||||
if (!generatedId || !encryptedAesKeyHex || !ivHex || !agentId || !status) {
|
const agent = await db.query.agent.findFirst({ where: eq(drizzleDb.schemas.agent.id, agentId) });
|
||||||
return res.status(400).json({error: "Missing required headers/params"});
|
if (!agent) return res.status(404).json({error: "Agent not found"});
|
||||||
}
|
|
||||||
|
|
||||||
if (!isUuidv4(generatedId)) {
|
const database = await db.query.database.findFirst({
|
||||||
return res.status(400).json({error: "generatedId is not a valid UUID"});
|
where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
|
||||||
}
|
with: { project: true, storagePolicies: true },
|
||||||
|
});
|
||||||
|
if (!database) return res.status(404).json({error: "Database not found"});
|
||||||
|
|
||||||
const agent = await db.query.agent.findFirst({
|
let backup: Backup | null = null;
|
||||||
where: eq(drizzleDb.schemas.agent.id, agentId),
|
if (method === "automatic") {
|
||||||
});
|
[backup] = await db.insert(drizzleDb.schemas.backup).values({ status: "ongoing", databaseId: database.id }).returning();
|
||||||
|
} else {
|
||||||
if (!agent) {
|
backup = await db.query.backup.findFirst({
|
||||||
return res.status(404).json({error: "Agent not found"});
|
where: and(
|
||||||
}
|
eq(drizzleDb.schemas.backup.databaseId, database.id),
|
||||||
|
eq(drizzleDb.schemas.backup.status, "ongoing")
|
||||||
|
),
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
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;
|
export default router;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user