360 lines
9.9 KiB
JavaScript
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;
|