feat: добавлены правила маршрутизации по устройствам и управление ими через API
All checks were successful
Build and Deploy Gateway / build-and-deploy (push) Successful in 19s

Refs: None
This commit is contained in:
2026-05-09 09:12:03 +03:00
parent b3fad00f80
commit 4bb8507e3f
7 changed files with 506 additions and 42 deletions

View File

@@ -436,6 +436,7 @@ async function startSingbox() {
function publicState() {
const state = readJson(settings.statePath, {});
const customRules = readJson(settings.customRulesPath, []);
const deviceRules = readJson(settings.deviceRulesPath, []);
const { subscriptionUrl, ...rest } = state;
return {
mode: "gateway",
@@ -449,6 +450,7 @@ function publicState() {
subscriptionHost: maskSubscriptionUrl(subscriptionUrl),
hasSubscription: Boolean(subscriptionUrl),
customRules,
deviceRules,
appliedHistory: state.appliedHistory || [],
rulesUpdatedAt: state.rulesUpdatedAt || null,
rulesAppliedAt: state.rulesAppliedAt || null,
@@ -492,6 +494,21 @@ function normalizeCustomRules(input) {
}));
}
function normalizeDeviceRules(input) {
const rules = Array.isArray(input) ? input : [];
return rules.map((r, index) => ({
id: String(r.id || `dev-${Date.now()}-${index}`),
name: String(r.name || `Устройство ${index + 1}`).trim(),
enabled: r.enabled !== false,
sourceIps: normalizeList(r.sourceIps).filter((ip) =>
/^[\.\d:/]+$/.test(ip),
),
outbound: ["direct", "vpn", "block"].includes(r.outbound)
? r.outbound
: "direct",
}));
}
async function applySelectedServer(selectedTag) {
const cached = readJson(settings.subscriptionCachePath, null);
if (!cached?.config) {
@@ -679,6 +696,20 @@ async function handleApi(req, res) {
});
}
if (req.method === "GET" && req.url === "/api/device-rules") {
return sendJson(res, 200, {
success: true,
deviceRules: readJson(settings.deviceRulesPath, []),
});
}
if (req.method === "PUT" && req.url === "/api/device-rules") {
const body = await readBody(req);
const rules = normalizeDeviceRules(body.deviceRules);
writeJson(settings.deviceRulesPath, rules);
return sendJson(res, 200, { success: true, deviceRules: rules });
}
if (req.method === "GET" && req.url === "/api/rule-sets") {
return sendJson(res, 200, {
success: true,