mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-22 02:08:31 +02:00
147 lines
5 KiB
TypeScript
147 lines
5 KiB
TypeScript
import logger from "@server/logger";
|
|
import config from "@server/lib/config";
|
|
import { createWebSocketClient } from "./routers/ws/client";
|
|
import { addPeer, deletePeer } from "./routers/gerbil/peers";
|
|
import { db, exitNodes } from "./db";
|
|
import { TraefikConfigManager } from "./lib/traefikConfig";
|
|
import { tokenManager } from "./lib/tokenManager";
|
|
import { APP_VERSION } from "./lib/consts";
|
|
import axios from "axios";
|
|
|
|
export async function createHybridClientServer() {
|
|
logger.info("Starting hybrid client server...");
|
|
|
|
// Start the token manager
|
|
await tokenManager.start();
|
|
|
|
const token = await tokenManager.getToken();
|
|
|
|
const monitor = new TraefikConfigManager();
|
|
|
|
await monitor.start();
|
|
|
|
// Create client
|
|
const client = createWebSocketClient(
|
|
token,
|
|
config.getRawConfig().hybrid!.endpoint!,
|
|
{
|
|
reconnectInterval: 5000,
|
|
pingInterval: 30000,
|
|
pingTimeout: 10000
|
|
}
|
|
);
|
|
|
|
// Register message handlers
|
|
client.registerHandler("remoteExitNode/peers/add", async (message) => {
|
|
const { publicKey, allowedIps } = message.data;
|
|
|
|
// TODO: we are getting the exit node twice here
|
|
// NOTE: there should only be one gerbil registered so...
|
|
const [exitNode] = await db.select().from(exitNodes).limit(1);
|
|
await addPeer(exitNode.exitNodeId, {
|
|
publicKey: publicKey,
|
|
allowedIps: allowedIps || []
|
|
});
|
|
});
|
|
|
|
client.registerHandler("remoteExitNode/peers/remove", async (message) => {
|
|
const { publicKey } = message.data;
|
|
|
|
// TODO: we are getting the exit node twice here
|
|
// NOTE: there should only be one gerbil registered so...
|
|
const [exitNode] = await db.select().from(exitNodes).limit(1);
|
|
await deletePeer(exitNode.exitNodeId, publicKey);
|
|
});
|
|
|
|
// /update-proxy-mapping
|
|
client.registerHandler("remoteExitNode/update-proxy-mapping", async (message) => {
|
|
try {
|
|
const [exitNode] = await db.select().from(exitNodes).limit(1);
|
|
if (!exitNode) {
|
|
logger.error("No exit node found for proxy mapping update");
|
|
return;
|
|
}
|
|
|
|
const response = await axios.post(`${exitNode.endpoint}/update-proxy-mapping`, message.data);
|
|
logger.info(`Successfully updated proxy mapping: ${response.status}`);
|
|
} catch (error) {
|
|
// pull data out of the axios error to log
|
|
if (axios.isAxiosError(error)) {
|
|
logger.error("Error updating proxy mapping:", {
|
|
message: error.message,
|
|
code: error.code,
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
url: error.config?.url,
|
|
method: error.config?.method
|
|
});
|
|
} else {
|
|
logger.error("Error updating proxy mapping:", error);
|
|
}
|
|
}
|
|
});
|
|
|
|
// /update-destinations
|
|
client.registerHandler("remoteExitNode/update-destinations", async (message) => {
|
|
try {
|
|
const [exitNode] = await db.select().from(exitNodes).limit(1);
|
|
if (!exitNode) {
|
|
logger.error("No exit node found for destinations update");
|
|
return;
|
|
}
|
|
|
|
const response = await axios.post(`${exitNode.endpoint}/update-destinations`, message.data);
|
|
logger.info(`Successfully updated destinations: ${response.status}`);
|
|
} catch (error) {
|
|
// pull data out of the axios error to log
|
|
if (axios.isAxiosError(error)) {
|
|
logger.error("Error updating destinations:", {
|
|
message: error.message,
|
|
code: error.code,
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
url: error.config?.url,
|
|
method: error.config?.method
|
|
});
|
|
} else {
|
|
logger.error("Error updating destinations:", error);
|
|
}
|
|
}
|
|
});
|
|
|
|
client.registerHandler("remoteExitNode/traefik/reload", async (message) => {
|
|
await monitor.HandleTraefikConfig();
|
|
});
|
|
|
|
// Listen to connection events
|
|
client.on("connect", () => {
|
|
logger.info("Connected to WebSocket server");
|
|
client.sendMessage("remoteExitNode/register", {
|
|
remoteExitNodeVersion: APP_VERSION
|
|
});
|
|
});
|
|
|
|
client.on("disconnect", () => {
|
|
logger.info("Disconnected from WebSocket server");
|
|
});
|
|
|
|
client.on("message", (message) => {
|
|
logger.info(
|
|
`Received message: ${message.type} ${JSON.stringify(message.data)}`
|
|
);
|
|
});
|
|
|
|
// Connect to the server
|
|
try {
|
|
await client.connect();
|
|
logger.info("Connection initiated");
|
|
} catch (error) {
|
|
logger.error("Failed to connect:", error);
|
|
}
|
|
|
|
client.sendMessageInterval(
|
|
"remoteExitNode/ping",
|
|
{ timestamp: Date.now() / 1000 },
|
|
60000
|
|
); // send every minute
|
|
}
|