chore: initial commit
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import Client from "ssh2-sftp-client";
|
||||
import { readFileSync } from "node:fs";
|
||||
import process from "node:process";
|
||||
import { Worker, isMainThread, workerData } from "node:worker_threads";
|
||||
|
||||
type Config = {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
type Profiles = Record<string, Config>;
|
||||
|
||||
type ConfigFile = {
|
||||
profiles: Profiles;
|
||||
};
|
||||
|
||||
const CONFIG_PATH = new URL("./config.json", import.meta.url);
|
||||
|
||||
function loadConfig(configArg?: string): Config {
|
||||
let configFile: ConfigFile;
|
||||
|
||||
try {
|
||||
configFile = JSON.parse(readFileSync(CONFIG_PATH, "utf-8")) as ConfigFile;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Could not read config file at ${CONFIG_PATH.href}. Copy config.example.json to config.json and fill in your credentials.`,
|
||||
);
|
||||
}
|
||||
|
||||
const profiles = configFile.profiles ?? {};
|
||||
const profileName = configArg ?? "default";
|
||||
const config = profiles[profileName];
|
||||
|
||||
if (!config) {
|
||||
throw new Error(
|
||||
`Unknown profile "${profileName}". Available profiles: ${Object.keys(profiles).join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!config.host || !config.username || !config.password) {
|
||||
throw new Error(
|
||||
`Profile "${profileName}" is missing host, username, or password in config.json.`,
|
||||
);
|
||||
}
|
||||
|
||||
return { ...config, port: Number(config.port) || 22 };
|
||||
}
|
||||
|
||||
class SftpService {
|
||||
private readonly config: Config;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
async uploadFile(sourcePath: string, remotePath: string) {
|
||||
const client = new Client();
|
||||
|
||||
try {
|
||||
await client.connect(this.config);
|
||||
await client.put(sourcePath, remotePath);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(remotePath: string) {
|
||||
const client = new Client();
|
||||
|
||||
try {
|
||||
await client.connect(this.config);
|
||||
await client.delete(remotePath);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runWorker(runIndex: number, configArg?: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = new Worker(new URL(import.meta.url), {
|
||||
workerData: { runIndex, configArg },
|
||||
});
|
||||
|
||||
worker.on("exit", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Worker ${runIndex} exited with code ${code}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
worker.on("error", (err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
async function executeIteration(runIndex: number, configArg?: string) {
|
||||
const sftp = new SftpService(loadConfig(configArg));
|
||||
|
||||
const remotePath = `./${runIndex}-test.txt`;
|
||||
const sourcePath = "./test.txt";
|
||||
|
||||
console.log(`[Worker ${runIndex}] Starting run number ${runIndex}`);
|
||||
|
||||
console.log(`[Worker ${runIndex}] Uploading file...`);
|
||||
await sftp.uploadFile(sourcePath, remotePath);
|
||||
console.log(`[Worker ${runIndex}] File uploaded`);
|
||||
|
||||
console.log(`[Worker ${runIndex}] Deleting file...`);
|
||||
await sftp.deleteFile(remotePath);
|
||||
console.log(`[Worker ${runIndex}] File deleted`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (isMainThread) {
|
||||
const configArg = process.argv[2];
|
||||
const TOTAL_RUNS = 6;
|
||||
|
||||
console.log(`[Main] Spawning ${TOTAL_RUNS} parallel workers...`);
|
||||
|
||||
const workerPromises: Promise<void>[] = [];
|
||||
|
||||
for (let i = 0; i < TOTAL_RUNS; i++) {
|
||||
workerPromises.push(runWorker(i, configArg));
|
||||
}
|
||||
|
||||
await Promise.all(workerPromises);
|
||||
console.log("[Main] All workers completed execution successfully.");
|
||||
} else {
|
||||
const { runIndex, configArg } = workerData;
|
||||
await executeIteration(runIndex, configArg);
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user