Update
This commit is contained in:
1 parent
87d6c9b3ce
commit
9a08bfbf44
2 files changed
+47
-12
No files matched your search
+31
-8
@@ -27,9 +27,11 @@ const port = Number(process.env.PORT ?? 3000);
|
|||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const frontendDist = path.resolve(__dirname, "../../frontend/dist");
|
const frontendDist = path.resolve(__dirname, "../../frontend/dist");
|
||||||
const serveFrontend = process.env.SERVE_FRONTEND !== "false";
|
const serveFrontend = process.env.SERVE_FRONTEND !== "false";
|
||||||
|
const requestBodyLimit = process.env.REQUEST_BODY_LIMIT ?? "250mb";
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json({ limit: "50mb" }));
|
app.use(express.text({ type: ["text/plain", "text/sql"], limit: requestBodyLimit }));
|
||||||
|
app.use(express.json({ limit: requestBodyLimit }));
|
||||||
|
|
||||||
app.get("/api/health", async (_req, res) => {
|
app.get("/api/health", async (_req, res) => {
|
||||||
await query("SELECT 1");
|
await query("SELECT 1");
|
||||||
@@ -3340,6 +3342,18 @@ app.get("/api/setup/status", async (_req, res) => {
|
|||||||
res.json({ required: !(await hasAdminUser()) });
|
res.json({ required: !(await hasAdminUser()) });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function restoreSqlFromRequestBody(body: unknown) {
|
||||||
|
if (typeof body === "string") {
|
||||||
|
return requireString(body, "sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPlainObject(body)) {
|
||||||
|
return requireString(body.sql, "sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw badRequest("sql is required");
|
||||||
|
}
|
||||||
|
|
||||||
app.post("/api/setup/admin", async (req, res) => {
|
app.post("/api/setup/admin", async (req, res) => {
|
||||||
if (await hasAdminUser()) {
|
if (await hasAdminUser()) {
|
||||||
res.status(409).json({ error: "Setup wurde bereits abgeschlossen" });
|
res.status(409).json({ error: "Setup wurde bereits abgeschlossen" });
|
||||||
@@ -3390,7 +3404,7 @@ app.post("/api/setup/restore", async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sql = requireString(req.body.sql, "sql");
|
const sql = restoreSqlFromRequestBody(req.body);
|
||||||
const result = await restoreTicketTrackerBackup(sql);
|
const result = await restoreTicketTrackerBackup(sql);
|
||||||
res.status(201).json(result);
|
res.status(201).json(result);
|
||||||
});
|
});
|
||||||
@@ -3401,7 +3415,7 @@ app.post("/api/setup/restore/validate", async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sql = requireString(req.body.sql, "sql");
|
const sql = restoreSqlFromRequestBody(req.body);
|
||||||
res.json(await validateTicketTrackerBackup(sql));
|
res.json(await validateTicketTrackerBackup(sql));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4157,12 +4171,12 @@ app.get("/api/admin/export/database", requireAdmin, async (_req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/admin/restore/database/validate", requireAdmin, async (req, res) => {
|
app.post("/api/admin/restore/database/validate", requireAdmin, async (req, res) => {
|
||||||
const sql = requireString(req.body.sql, "sql");
|
const sql = restoreSqlFromRequestBody(req.body);
|
||||||
res.json(await validateTicketTrackerBackup(sql));
|
res.json(await validateTicketTrackerBackup(sql));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/admin/restore/database", requireAdmin, async (req, res) => {
|
app.post("/api/admin/restore/database", requireAdmin, async (req, res) => {
|
||||||
const sql = requireString(req.body.sql, "sql");
|
const sql = restoreSqlFromRequestBody(req.body);
|
||||||
const result = await restoreTicketTrackerBackup(sql);
|
const result = await restoreTicketTrackerBackup(sql);
|
||||||
|
|
||||||
res.clearCookie("tickettracker_session", {
|
res.clearCookie("tickettracker_session", {
|
||||||
@@ -7781,10 +7795,19 @@ if (serveFrontend) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use((error: Error & { status?: number }, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
app.use((error: Error & { status?: number; statusCode?: number; type?: string }, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
res.status(error.status ?? 500).json({
|
|
||||||
error: error.status ? error.message : "Internal server error"
|
if (error.type === "entity.too.large" || error.status === 413 || error.statusCode === 413) {
|
||||||
|
res.status(413).json({
|
||||||
|
error: `Upload ist zu groß. Aktuelles Limit: ${requestBodyLimit}.`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = error.status ?? error.statusCode ?? 500;
|
||||||
|
res.status(status).json({
|
||||||
|
error: status < 500 ? error.message : "Internal server error"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+16
-4
@@ -140,6 +140,10 @@ async function request<T>(url: string, options: ApiRequestInit = {}): Promise<T>
|
|||||||
throw new ApiError("Anfrage hat zu lange gedauert", 408);
|
throw new ApiError("Anfrage hat zu lange gedauert", 408);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error instanceof TypeError) {
|
||||||
|
throw new ApiError("Verbindung zum Server wurde unterbrochen. Bitte prüfen, ob Backend und Frontend laufen und ob das Backup kleiner als das Upload-Limit ist.", 503);
|
||||||
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
@@ -223,14 +227,18 @@ export function createSetupAdmin(payload: { username: string; displayName: strin
|
|||||||
export function restoreSetupBackup(sql: string) {
|
export function restoreSetupBackup(sql: string) {
|
||||||
return request<{ ok: boolean }>("/api/setup/restore", {
|
return request<{ ok: boolean }>("/api/setup/restore", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ sql })
|
timeoutMs: 10 * 60 * 1000,
|
||||||
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||||
|
body: sql
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateSetupBackup(sql: string) {
|
export function validateSetupBackup(sql: string) {
|
||||||
return request<RestoreValidationResult>("/api/setup/restore/validate", {
|
return request<RestoreValidationResult>("/api/setup/restore/validate", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ sql })
|
timeoutMs: 10 * 60 * 1000,
|
||||||
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||||
|
body: sql
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,14 +295,18 @@ export async function downloadDatabaseExport() {
|
|||||||
export function restoreDatabaseBackup(sql: string) {
|
export function restoreDatabaseBackup(sql: string) {
|
||||||
return request<{ ok: boolean }>("/api/admin/restore/database", {
|
return request<{ ok: boolean }>("/api/admin/restore/database", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ sql })
|
timeoutMs: 10 * 60 * 1000,
|
||||||
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||||
|
body: sql
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateDatabaseBackup(sql: string) {
|
export function validateDatabaseBackup(sql: string) {
|
||||||
return request<RestoreValidationResult>("/api/admin/restore/database/validate", {
|
return request<RestoreValidationResult>("/api/admin/restore/database/validate", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ sql })
|
timeoutMs: 10 * 60 * 1000,
|
||||||
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||||
|
body: sql
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user