75 lines
1.8 KiB
TypeScript
75 lines
1.8 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
|
|
const execFile = plugins.util.promisify(plugins.childProcess.execFile);
|
|
|
|
export class NftNotRootError extends Error {
|
|
constructor() {
|
|
super('Not running as root — nftables commands require root privileges');
|
|
this.name = 'NftNotRootError';
|
|
}
|
|
}
|
|
|
|
export class NftCommandError extends Error {
|
|
public readonly command: string;
|
|
public readonly stderr: string;
|
|
|
|
constructor(command: string, stderr: string) {
|
|
super(`nft command failed: ${command} — ${stderr}`);
|
|
this.name = 'NftCommandError';
|
|
this.command = command;
|
|
this.stderr = stderr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Low-level executor for nft CLI commands.
|
|
*/
|
|
export class NftExecutor {
|
|
private dryRun: boolean;
|
|
|
|
constructor(options?: { dryRun?: boolean }) {
|
|
this.dryRun = options?.dryRun ?? false;
|
|
}
|
|
|
|
/** Check if running as root (euid 0). */
|
|
public isRoot(): boolean {
|
|
return process.getuid?.() === 0;
|
|
}
|
|
|
|
/**
|
|
* Execute a single nft command.
|
|
* The command may or may not start with "nft " — the prefix is stripped if present.
|
|
* Returns stdout on success.
|
|
*/
|
|
public async exec(command: string): Promise<string> {
|
|
if (this.dryRun) {
|
|
return '';
|
|
}
|
|
|
|
// Strip "nft " prefix if present
|
|
const args = command.startsWith('nft ') ? command.slice(4) : command;
|
|
|
|
const { stdout } = await execFile('nft', args.split(/\s+/));
|
|
return stdout;
|
|
}
|
|
|
|
/**
|
|
* Execute multiple nft commands sequentially.
|
|
* By default stops on first failure. Set continueOnError to keep going.
|
|
*/
|
|
public async execBatch(
|
|
commands: string[],
|
|
options?: { continueOnError?: boolean }
|
|
): Promise<void> {
|
|
for (const cmd of commands) {
|
|
try {
|
|
await this.exec(cmd);
|
|
} catch (err) {
|
|
if (!options?.continueOnError) {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|