feat: Add backup system for ZIP downloads

This commit is contained in:
FluxKit
2026-02-19 14:40:44 +00:00
parent bebfcbb816
commit 1bc9b4f7e2
2 changed files with 74 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ import { logsRouter } from "./routes/logs.ts";
import { userSettingsRouter } from "./routes/usersettings.ts";
import gitlabRouter from "./routes/gitlab.ts";
import { giteaRouter } from "./routes/gitea.ts";
import { backupRouter } from "./routes/backup.ts";
import { dockerRouter } from "./routes/docker.ts";
import { exportRouter } from "./routes/export.ts";
import { appUpdateRouter } from "./routes/appUpdate.ts";
@@ -78,6 +79,8 @@ app.use(userSettingsRouter.allowedMethods());
app.use(gitlabRouter.routes());
app.use(giteaRouter.routes());
app.use(giteaRouter.allowedMethods());
app.use(backupRouter.routes());
app.use(backupRouter.allowedMethods());
app.use(gitlabRouter.allowedMethods());
app.use(dockerRouter.routes());
app.use(dockerRouter.allowedMethods());

71
src/routes/backup.ts Normal file
View File

@@ -0,0 +1,71 @@
import { Router } from "@oak/oak";
import { authMiddleware } from "../middleware/auth.ts";
const router = new Router();
const GITEA_URL = Deno.env.get("GITEA_URL") || "https://git.kronos-soulution.de";
const GITEA_TOKEN = Deno.env.get("GITEA_TOKEN") || "";
interface GiteaRepo {
id: number;
name: string;
full_name: string;
description: string | null;
default_branch: string;
html_url: string;
updated_at: string;
size: number;
owner: { login: string };
}
// Get all repos with download info
router.get("/api/backup/repos", authMiddleware, async (ctx) => {
try {
const response = await fetch(`${GITEA_URL}/api/v1/user/repos?limit=100`, {
headers: { "Authorization": `token ${GITEA_TOKEN}` }
});
if (!response.ok) throw new Error("Failed to fetch repos");
const repos: GiteaRepo[] = await response.json();
ctx.response.body = repos.map(r => ({
id: r.id,
name: r.name,
fullName: r.full_name,
description: r.description,
branch: r.default_branch,
url: r.html_url,
updatedAt: r.updated_at,
size: r.size,
downloadUrl: `${GITEA_URL}/${r.full_name}/archive/${r.default_branch}.zip`
}));
} catch (err) {
ctx.response.status = 500;
ctx.response.body = { error: err instanceof Error ? err.message : "Failed to fetch repos" };
}
});
// Proxy download (to avoid CORS issues)
router.get("/api/backup/download/:owner/:repo", authMiddleware, async (ctx) => {
const { owner, repo } = ctx.params;
const branch = ctx.request.url.searchParams.get("branch") || "main";
try {
const response = await fetch(
`${GITEA_URL}/${owner}/${repo}/archive/${branch}.zip`,
{ headers: { "Authorization": `token ${GITEA_TOKEN}` } }
);
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
ctx.response.headers.set("Content-Type", "application/zip");
ctx.response.headers.set("Content-Disposition", `attachment; filename="${repo}-${branch}.zip"`);
ctx.response.body = response.body;
} catch (err) {
ctx.response.status = 500;
ctx.response.body = { error: err instanceof Error ? err.message : "Download failed" };
}
});
export const backupRouter = router;