- Добавлена ветка 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 нужно добавить отдельно
88 lines
3.0 KiB
TypeScript
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" });
|
|
}
|
|
});
|
|
}
|