Add initial implementation of EMLy Bug Report API with MySQL integration and Docker support

This commit is contained in:
Flavio Fois
2026-02-16 11:11:50 +01:00
parent 1001321fe7
commit 0cad94dadd
19 changed files with 1055 additions and 0 deletions

109
src/routes/admin.ts Normal file
View File

@@ -0,0 +1,109 @@
import { Elysia, t } from "elysia";
import { adminKeyGuard } from "../middleware/auth";
import {
listBugReports,
getBugReport,
getFile,
deleteBugReport,
updateBugReportStatus,
} from "../services/bugReportService";
import { Log } from "../logger";
import type { BugReportStatus } from "../types";
export const adminRoutes = new Elysia({ prefix: "/api/admin" })
.use(adminKeyGuard)
.get(
"/bug-reports",
async ({ query }) => {
const page = parseInt(query.page || "1");
const pageSize = Math.min(parseInt(query.pageSize || "20"), 100);
const status = query.status as BugReportStatus | undefined;
Log("ADMIN", `List bug reports page=${page} pageSize=${pageSize} status=${status || "all"}`);
return await listBugReports({ page, pageSize, status });
},
{
query: t.Object({
page: t.Optional(t.String()),
pageSize: t.Optional(t.String()),
status: t.Optional(
t.Union([
t.Literal("new"),
t.Literal("in_review"),
t.Literal("resolved"),
t.Literal("closed"),
])
),
}),
detail: { summary: "List bug reports (paginated)" },
}
)
.get(
"/bug-reports/:id",
async ({ params, error }) => {
Log("ADMIN", `Get bug report id=${params.id}`);
const result = await getBugReport(parseInt(params.id));
if (!result) return error(404, { success: false, message: "Report not found" });
return result;
},
{
params: t.Object({ id: t.String() }),
detail: { summary: "Get bug report with file metadata" },
}
)
.patch(
"/bug-reports/:id/status",
async ({ params, body, error }) => {
Log("ADMIN", `Update status id=${params.id} status=${body.status}`);
const updated = await updateBugReportStatus(
parseInt(params.id),
body.status
);
if (!updated)
return error(404, { success: false, message: "Report not found" });
return { success: true, message: "Status updated" };
},
{
params: t.Object({ id: t.String() }),
body: t.Object({
status: t.Union([
t.Literal("new"),
t.Literal("in_review"),
t.Literal("resolved"),
t.Literal("closed"),
]),
}),
detail: { summary: "Update bug report status" },
}
)
.get(
"/bug-reports/:id/files/:fileId",
async ({ params, error, set }) => {
const file = await getFile(parseInt(params.id), parseInt(params.fileId));
if (!file)
return error(404, { success: false, message: "File not found" });
set.headers["content-type"] = file.mime_type;
set.headers["content-disposition"] =
`attachment; filename="${file.filename}"`;
return new Response(file.data);
},
{
params: t.Object({ id: t.String(), fileId: t.String() }),
detail: { summary: "Download a bug report file" },
}
)
.delete(
"/bug-reports/:id",
async ({ params, error }) => {
Log("ADMIN", `Delete bug report id=${params.id}`);
const deleted = await deleteBugReport(parseInt(params.id));
if (!deleted)
return error(404, { success: false, message: "Report not found" });
return { success: true, message: "Report deleted" };
},
{
params: t.Object({ id: t.String() }),
detail: { summary: "Delete a bug report and its files" },
}
);