Files
iistwin/server/vpn/vpn.routes.ts
Ильяс Султанов 586a591773 fix(vpn): восстановление VPN-модуля после затирания chat/push fix'ом
- Добавлена ветка bot.type === 'vpn' в messenger.messages.routes.ts
- Добавлен полный модуль server/vpn/ (routes, service, db, bot, config)
- Подключены VPN-маршруты в server/routes/index.ts
- Добавлена раздача /apps статики и COPY в Dockerfile
- apps/ пока содержит .gitkeep и README; APK/IPA нужно добавить отдельно
2026-07-17 09:32:43 +03:00

88 lines
3.0 KiB
TypeScript

import { Router } from "express";
import type { Request, Response } from "express";
import {
fetchSubscription,
authorizeSession,
closeSession,
recordTraffic,
VpnError,
} from "./vpn.service";
import { SUBSCRIPTION_UPDATE_INTERVAL_HOURS } from "./vpn.config";
function handleVpnError(res: Response, err: unknown): void {
if (err instanceof VpnError) {
res.status(403).type("text/plain").send(err.message);
return;
}
console.error("[VPN API] error:", err);
res.status(500).type("text/plain").send("Internal error");
}
export function registerVpnRoutes(router: Router): void {
// Subscription endpoint used by olcbox clients
router.get("/api/vpn/sub/:token", async (req: Request, res: Response) => {
try {
const token = req.params.token;
const hwid = req.headers["x-hwid"] as string | undefined;
const uri = await fetchSubscription(token, hwid);
res.setHeader("profile-update-interval", String(SUBSCRIPTION_UPDATE_INTERVAL_HOURS));
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.send(uri);
} catch (err) {
handleVpnError(res, err);
}
});
// olrtc srv AuthHook
router.post("/api/vpn/olrtc/auth", async (req: Request, res: Response) => {
try {
const { device_id, room_url } = req.body || {};
if (!device_id || !room_url) {
res.status(400).json({ success: false, error: "missing device_id or room_url" });
return;
}
const result = await authorizeSession(room_url, device_id);
res.json({ success: true, session_id: result.sessionId });
} catch (err) {
if (err instanceof VpnError) {
res.status(403).json({ success: false, error: err.message });
return;
}
console.error("[VPN Auth] error:", err);
res.status(500).json({ success: false, error: "internal error" });
}
});
// olrtc srv OnSessionClose
router.post("/api/vpn/olrtc/close", async (req: Request, res: Response) => {
try {
const { session_id, room_url } = req.body || {};
if (!session_id || !room_url) {
res.status(400).json({ success: false, error: "missing fields" });
return;
}
await closeSession(room_url, session_id);
res.json({ success: true });
} catch (err) {
console.error("[VPN Close] error:", err);
res.status(500).json({ success: false, error: "internal error" });
}
});
// olrtc srv OnTraffic
router.post("/api/vpn/olrtc/traffic", async (req: Request, res: Response) => {
try {
const { session_id, room_url, bytes_in, bytes_out } = req.body || {};
if (!session_id || !room_url || typeof bytes_in !== "number" || typeof bytes_out !== "number") {
res.status(400).json({ success: false, error: "missing fields" });
return;
}
await recordTraffic(room_url, session_id, bytes_in, bytes_out);
res.json({ success: true });
} catch (err) {
console.error("[VPN Traffic] error:", err);
res.status(500).json({ success: false, error: "internal error" });
}
});
}