D3V-Server/backend/routes/wireguard.js

360 lines
9.9 KiB
JavaScript

import express from "express";
import archiver from "archiver";
import internalWireguard from "../internal/wireguard.js";
import internalAuditLog from "../internal/audit-log.js";
import jwtdecode from "../lib/express/jwt-decode.js";
import db from "../db.js";
const router = express.Router({
caseSensitive: true,
strict: true,
mergeParams: true,
});
// Protect all WireGuard routes
router.use(jwtdecode());
/**
* GET /api/wireguard
* Get WireGuard interfaces info
*/
router.get("/", async (_req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:list");
const ifaces = await internalWireguard.getInterfacesInfo(knex, access, accessData);
res.status(200).json(ifaces);
} catch (err) {
next(err);
}
});
/**
* POST /api/wireguard
* Create a new WireGuard interface
*/
router.post("/", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:create");
const iface = await internalWireguard.createInterface(knex, req.body, access, accessData);
await internalAuditLog.add(access, {
action: "created",
object_type: "wireguard-server",
object_id: iface.id,
meta: req.body,
});
res.status(201).json(iface);
} catch (err) {
next(err);
}
});
/**
* PUT /api/wireguard/:id
* Update a WireGuard interface
*/
router.put("/:id", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:update");
const iface = await internalWireguard.updateInterface(knex, req.params.id, req.body, access, accessData);
await internalAuditLog.add(access, {
action: "updated",
object_type: "wireguard-server",
object_id: iface.id,
meta: req.body,
});
res.status(200).json(iface);
} catch (err) {
next(err);
}
});
/**
* DELETE /api/wireguard/:id
* Delete a WireGuard interface
*/
router.delete("/:id", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:delete");
const result = await internalWireguard.deleteInterface(knex, req.params.id, access, accessData);
await internalAuditLog.add(access, {
action: "deleted",
object_type: "wireguard-server",
object_id: req.params.id,
meta: {},
});
res.status(200).json(result);
} catch (err) {
next(err);
}
});
/**
* POST /api/wireguard/:id/links
* Update peering links for a WireGuard interface
*/
router.post("/:id/links", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:update");
const result = await internalWireguard.updateInterfaceLinks(knex, req.params.id, req.body.linked_servers || [], access, accessData);
await internalAuditLog.add(access, {
action: "updated",
object_type: "wireguard-server-links",
object_id: req.params.id,
meta: req.body,
});
res.status(200).json(result);
} catch (err) {
next(err);
}
});
/**
* GET /api/wireguard/client
* List all WireGuard clients with live status
*/
router.get("/client", async (_req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:list");
const clients = await internalWireguard.getClients(knex, access, accessData);
res.status(200).json(clients);
} catch (err) {
next(err);
}
});
/**
* POST /api/wireguard/client
* Create a new WireGuard client
*/
router.post("/client", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:create");
const client = await internalWireguard.createClient(knex, req.body, access, accessData);
await internalAuditLog.add(access, {
action: "created",
object_type: "wireguard-client",
object_id: client.id,
meta: req.body,
});
res.status(201).json(client);
} catch (err) {
next(err);
}
});
/**
* GET /api/wireguard/client/:id
* Get a specific WireGuard client
*/
router.get("/client/:id", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:get");
const query = knex("wg_client").where("id", req.params.id);
if (accessData.permission_visibility !== "all") {
query.andWhere("owner_user_id", access.token.getUserId(1));
}
const client = await query.first();
if (!client) {
return res.status(404).json({ error: { message: "Client not found" } });
}
res.status(200).json(client);
} catch (err) {
next(err);
}
});
/**
* PUT /api/wireguard/client/:id
* Update a WireGuard client
*/
router.put("/client/:id", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:update");
const client = await internalWireguard.updateClient(knex, req.params.id, req.body, access, accessData);
await internalAuditLog.add(access, {
action: "updated",
object_type: "wireguard-client",
object_id: client.id,
meta: req.body,
});
res.status(200).json(client);
} catch (err) {
next(err);
}
});
/**
* DELETE /api/wireguard/client/:id
* Delete a WireGuard client
*/
router.delete("/client/:id", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:delete");
const result = await internalWireguard.deleteClient(knex, req.params.id, access, accessData);
await internalAuditLog.add(access, {
action: "deleted",
object_type: "wireguard-client",
object_id: req.params.id,
meta: {},
});
res.status(200).json(result);
} catch (err) {
next(err);
}
});
/**
* POST /api/wireguard/client/:id/enable
* Enable a WireGuard client
*/
router.post("/client/:id/enable", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:update");
const client = await internalWireguard.toggleClient(knex, req.params.id, true, access, accessData);
await internalAuditLog.add(access, {
action: "enabled",
object_type: "wireguard-client",
object_id: client.id,
meta: {},
});
res.status(200).json(client);
} catch (err) {
next(err);
}
});
/**
* POST /api/wireguard/client/:id/disable
* Disable a WireGuard client
*/
router.post("/client/:id/disable", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:update");
const client = await internalWireguard.toggleClient(knex, req.params.id, false, access, accessData);
await internalAuditLog.add(access, {
action: "disabled",
object_type: "wireguard-client",
object_id: client.id,
meta: {},
});
res.status(200).json(client);
} catch (err) {
next(err);
}
});
/**
* GET /api/wireguard/client/:id/configuration
* Download WireGuard client configuration file
*/
router.get("/client/:id/configuration", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:get");
const query = knex("wg_client").where("id", req.params.id);
if (accessData.permission_visibility !== "all") {
query.andWhere("owner_user_id", access.token.getUserId(1));
}
const client = await query.first();
if (!client) {
return res.status(404).json({ error: { message: "Client not found" } });
}
const config = await internalWireguard.getClientConfiguration(knex, req.params.id);
const safeName = client.name.replace(/[^a-zA-Z0-9_.-]/g, "-").substring(0, 32);
res.set("Content-Disposition", `attachment; filename="${safeName}.conf"`);
res.set("Content-Type", "text/plain");
res.status(200).send(config);
} catch (err) {
next(err);
}
});
/**
* GET /api/wireguard/client/:id/qrcode.svg
* Get QR code SVG for client configuration
*/
router.get("/client/:id/qrcode.svg", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:get");
const query = knex("wg_client").where("id", req.params.id);
if (accessData.permission_visibility !== "all") {
query.andWhere("owner_user_id", access.token.getUserId(1));
}
const client = await query.first();
if (!client) {
return res.status(404).json({ error: { message: "Client not found" } });
}
const svg = await internalWireguard.getClientQRCode(knex, req.params.id);
res.set("Content-Type", "image/svg+xml");
res.status(200).send(svg);
} catch (err) {
next(err);
}
});
/**
* GET /api/wireguard/client/:id/configuration.zip
* Download WireGuard client configuration as a ZIP archive
*/
router.get("/client/:id/configuration.zip", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:get");
const query = knex("wg_client").where("id", req.params.id);
if (accessData.permission_visibility !== "all") {
query.andWhere("owner_user_id", access.token.getUserId(1));
}
const client = await query.first();
if (!client) {
return res.status(404).json({ error: { message: "Client not found" } });
}
const configStr = await internalWireguard.getClientConfiguration(knex, req.params.id);
const svgStr = await internalWireguard.getClientQRCode(knex, req.params.id);
const safeName = client.name.replace(/[^a-zA-Z0-9_.-]/g, "-").substring(0, 32);
res.set("Content-Disposition", `attachment; filename="${safeName}.zip"`);
res.set("Content-Type", "application/zip");
const archive = archiver("zip", { zlib: { level: 9 } });
archive.on("error", (err) => next(err));
archive.pipe(res);
archive.append(configStr, { name: `${safeName}.conf` });
archive.append(svgStr, { name: `${safeName}-qrcode.svg` });
await archive.finalize();
} catch (err) {
next(err);
}
});
export default router;