initial
This commit is contained in:
104
ts/smartvpn.classes.vpnconfig.ts
Normal file
104
ts/smartvpn.classes.vpnconfig.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import * as plugins from './smartvpn.plugins.js';
|
||||
import type {
|
||||
IVpnClientConfig,
|
||||
IVpnServerConfig,
|
||||
} from './smartvpn.interfaces.js';
|
||||
|
||||
/**
|
||||
* VPN configuration loader, saver, and validator.
|
||||
*/
|
||||
export class VpnConfig {
|
||||
/**
|
||||
* Validate a client config object. Throws on invalid config.
|
||||
*/
|
||||
public static validateClientConfig(config: IVpnClientConfig): void {
|
||||
if (!config.serverUrl) {
|
||||
throw new Error('VpnConfig: serverUrl is required');
|
||||
}
|
||||
if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
|
||||
throw new Error('VpnConfig: serverUrl must start with wss:// or ws://');
|
||||
}
|
||||
if (!config.serverPublicKey) {
|
||||
throw new Error('VpnConfig: serverPublicKey is required');
|
||||
}
|
||||
if (config.mtu !== undefined && (config.mtu < 576 || config.mtu > 65535)) {
|
||||
throw new Error('VpnConfig: mtu must be between 576 and 65535');
|
||||
}
|
||||
if (config.keepaliveIntervalSecs !== undefined && config.keepaliveIntervalSecs < 1) {
|
||||
throw new Error('VpnConfig: keepaliveIntervalSecs must be >= 1');
|
||||
}
|
||||
if (config.dns) {
|
||||
for (const dns of config.dns) {
|
||||
if (!VpnConfig.isValidIp(dns)) {
|
||||
throw new Error(`VpnConfig: invalid DNS address: ${dns}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a server config object. Throws on invalid config.
|
||||
*/
|
||||
public static validateServerConfig(config: IVpnServerConfig): void {
|
||||
if (!config.listenAddr) {
|
||||
throw new Error('VpnConfig: listenAddr is required');
|
||||
}
|
||||
if (!config.privateKey) {
|
||||
throw new Error('VpnConfig: privateKey is required');
|
||||
}
|
||||
if (!config.publicKey) {
|
||||
throw new Error('VpnConfig: publicKey is required');
|
||||
}
|
||||
if (!config.subnet) {
|
||||
throw new Error('VpnConfig: subnet is required');
|
||||
}
|
||||
if (!VpnConfig.isValidSubnet(config.subnet)) {
|
||||
throw new Error(`VpnConfig: invalid subnet: ${config.subnet}`);
|
||||
}
|
||||
if (config.mtu !== undefined && (config.mtu < 576 || config.mtu > 65535)) {
|
||||
throw new Error('VpnConfig: mtu must be between 576 and 65535');
|
||||
}
|
||||
if (config.keepaliveIntervalSecs !== undefined && config.keepaliveIntervalSecs < 1) {
|
||||
throw new Error('VpnConfig: keepaliveIntervalSecs must be >= 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a config from a JSON file.
|
||||
*/
|
||||
public static async loadFromFile<T>(filePath: string): Promise<T> {
|
||||
const content = await plugins.fs.promises.readFile(filePath, 'utf-8');
|
||||
return JSON.parse(content) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a config to a JSON file.
|
||||
*/
|
||||
public static async saveToFile<T>(filePath: string, config: T): Promise<void> {
|
||||
const content = JSON.stringify(config, null, 2);
|
||||
await plugins.fs.promises.writeFile(filePath, content, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic IP address validation.
|
||||
*/
|
||||
private static isValidIp(ip: string): boolean {
|
||||
const parts = ip.split('.');
|
||||
if (parts.length !== 4) return false;
|
||||
return parts.every((part) => {
|
||||
const num = parseInt(part, 10);
|
||||
return !isNaN(num) && num >= 0 && num <= 255 && String(num) === part;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic subnet validation (CIDR notation).
|
||||
*/
|
||||
private static isValidSubnet(subnet: string): boolean {
|
||||
const [ip, prefix] = subnet.split('/');
|
||||
if (!ip || !prefix) return false;
|
||||
if (!VpnConfig.isValidIp(ip)) return false;
|
||||
const prefixNum = parseInt(prefix, 10);
|
||||
return !isNaN(prefixNum) && prefixNum >= 0 && prefixNum <= 32;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user