Projektstart
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "../db.js";
|
||||
import { logJobEvent } from "../queue/jobEvents.js";
|
||||
import { queueCleanupJob, removeQueueJob } from "../queue/queue.js";
|
||||
|
||||
const roleSchema = z.object({
|
||||
role: z.enum(["USER", "ADMIN"])
|
||||
@@ -25,6 +27,74 @@ export async function adminRoutes(app: FastifyInstance) {
|
||||
return { tenants };
|
||||
});
|
||||
|
||||
app.get("/tenants/:id/export", async (request, reply) => {
|
||||
const params = request.params as { id: string };
|
||||
const query = request.query as { format?: string };
|
||||
const tenant = await prisma.tenant.findUnique({
|
||||
where: { id: params.id },
|
||||
include: {
|
||||
users: true,
|
||||
mailboxAccounts: true,
|
||||
rules: { include: { conditions: true, actions: true } },
|
||||
jobs: { include: { unsubscribeAttempts: true, events: true } }
|
||||
}
|
||||
});
|
||||
if (!tenant) return reply.code(404).send({ message: "Tenant not found" });
|
||||
const scope = query.scope ?? "all";
|
||||
if (query.format === "csv") {
|
||||
const rows = [
|
||||
["type", "id", "email", "name", "createdAt"].join(",")
|
||||
];
|
||||
if (scope === "all" || scope === "users") {
|
||||
for (const user of tenant.users) {
|
||||
rows.push(["user", user.id, user.email, tenant.name, user.createdAt.toISOString()].join(","));
|
||||
}
|
||||
}
|
||||
if (scope === "all" || scope === "accounts") {
|
||||
for (const account of tenant.mailboxAccounts) {
|
||||
rows.push(["account", account.id, account.email, account.provider, account.createdAt.toISOString()].join(","));
|
||||
}
|
||||
}
|
||||
if (scope === "all" || scope === "jobs") {
|
||||
for (const job of tenant.jobs) {
|
||||
rows.push(["job", job.id, "", job.status, job.createdAt.toISOString()].join(","));
|
||||
}
|
||||
}
|
||||
reply.header("Content-Type", "text/csv");
|
||||
return reply.send(rows.join("\n"));
|
||||
}
|
||||
|
||||
if (scope === "users") return { users: tenant.users };
|
||||
if (scope === "accounts") return { accounts: tenant.mailboxAccounts };
|
||||
if (scope === "jobs") return { jobs: tenant.jobs };
|
||||
if (scope === "rules") return { rules: tenant.rules };
|
||||
return { tenant };
|
||||
});
|
||||
|
||||
app.delete("/tenants/:id", async (request, reply) => {
|
||||
const params = request.params as { id: string };
|
||||
const tenant = await prisma.tenant.findUnique({ where: { id: params.id } });
|
||||
if (!tenant) return reply.code(404).send({ message: "Tenant not found" });
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const jobs = await tx.cleanupJob.findMany({ where: { tenantId: tenant.id } });
|
||||
const jobIds = jobs.map((job) => job.id);
|
||||
await tx.cleanupJobEvent.deleteMany({ where: { jobId: { in: jobIds } } });
|
||||
await tx.unsubscribeAttempt.deleteMany({ where: { jobId: { in: jobIds } } });
|
||||
await tx.cleanupJob.deleteMany({ where: { tenantId: tenant.id } });
|
||||
await tx.ruleAction.deleteMany({ where: { rule: { tenantId: tenant.id } } });
|
||||
await tx.ruleCondition.deleteMany({ where: { rule: { tenantId: tenant.id } } });
|
||||
await tx.rule.deleteMany({ where: { tenantId: tenant.id } });
|
||||
await tx.mailItem.deleteMany({ where: { folder: { mailboxAccount: { tenantId: tenant.id } } } });
|
||||
await tx.mailboxFolder.deleteMany({ where: { mailboxAccount: { tenantId: tenant.id } } });
|
||||
await tx.mailboxAccount.deleteMany({ where: { tenantId: tenant.id } });
|
||||
await tx.user.deleteMany({ where: { tenantId: tenant.id } });
|
||||
await tx.tenant.delete({ where: { id: tenant.id } });
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
app.put("/tenants/:id", async (request, reply) => {
|
||||
const params = request.params as { id: string };
|
||||
const input = activeSchema.parse(request.body);
|
||||
@@ -99,7 +169,7 @@ export async function adminRoutes(app: FastifyInstance) {
|
||||
data: { password: hashed }
|
||||
});
|
||||
|
||||
return { user: updated };
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
app.get("/accounts", async () => {
|
||||
@@ -163,4 +233,43 @@ export async function adminRoutes(app: FastifyInstance) {
|
||||
});
|
||||
return { events };
|
||||
});
|
||||
|
||||
app.post("/jobs/:id/cancel", async (request, reply) => {
|
||||
const params = request.params as { id: string };
|
||||
const job = await prisma.cleanupJob.findUnique({ where: { id: params.id } });
|
||||
if (!job) return reply.code(404).send({ message: "Job not found" });
|
||||
|
||||
await prisma.cleanupJob.update({ where: { id: job.id }, data: { status: "CANCELED" } });
|
||||
await removeQueueJob(job.id);
|
||||
await logJobEvent(job.id, "info", "Job canceled by admin", 100);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
app.post("/jobs/:id/retry", async (request, reply) => {
|
||||
const params = request.params as { id: string };
|
||||
const job = await prisma.cleanupJob.findUnique({ where: { id: params.id } });
|
||||
if (!job) return reply.code(404).send({ message: "Job not found" });
|
||||
|
||||
const newJob = await prisma.cleanupJob.create({
|
||||
data: {
|
||||
tenantId: job.tenantId,
|
||||
mailboxAccountId: job.mailboxAccountId,
|
||||
dryRun: job.dryRun,
|
||||
unsubscribeEnabled: job.unsubscribeEnabled,
|
||||
routingEnabled: job.routingEnabled
|
||||
}
|
||||
});
|
||||
await queueCleanupJob(newJob.id, newJob.mailboxAccountId);
|
||||
await logJobEvent(newJob.id, "info", "Job queued by admin", 5);
|
||||
return { jobId: newJob.id };
|
||||
});
|
||||
|
||||
app.post("/impersonate/:userId", async (request, reply) => {
|
||||
const params = request.params as { userId: string };
|
||||
const user = await prisma.user.findUnique({ where: { id: params.userId } });
|
||||
if (!user) return reply.code(404).send({ message: "User not found" });
|
||||
|
||||
const token = app.jwt.sign({ sub: user.id, tenantId: user.tenantId });
|
||||
return { token };
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user