feat: bash exec and queue exported as API

This commit is contained in:
Jerry Tian 2025-04-06 16:04:01 -04:00
parent aa6df63025
commit b5979cf25b
4 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,38 @@
import { NextRequest, NextResponse } from "next/server";
import {
executeScript,
getQueueStats,
} from "../../../services/bashExecService";
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { scriptPath, args } = body;
if (!scriptPath) {
return NextResponse.json(
{ error: "Script path is required" },
{ status: 400 },
);
}
const result = await executeScript({
scriptPath,
args,
});
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
// Endpoint to get queue statistics
export async function GET() {
try {
const stats = getQueueStats();
return NextResponse.json(stats);
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

View File

@ -0,0 +1,79 @@
import { execa } from "execa";
import PQueue from "p-queue";
// Configure the queue with a concurrency limit
const scriptQueue = new PQueue({ concurrency: 5 });
export type ScriptParams = {
scriptPath: string;
args?: string[];
onProgress?: (data: string) => void;
};
export type ScriptResult = {
success: boolean;
stdout: string;
stderr: string;
error?: string;
};
/**
* Executes a bash script with queue management
*/
export async function executeScript(
params: ScriptParams,
): Promise<ScriptResult> {
// Add the script execution to the queue
const result = await scriptQueue.add(async () => {
try {
// Execute the bash script
const { stdout, stderr } = await execa(
"bash",
[params.scriptPath, ...(params.args || [])],
{
buffer: true, // Changed to true to ensure we get the complete output
},
);
// If onProgress is provided, we can use it to stream script output
if (params.onProgress && stdout) {
params.onProgress(stdout.toString());
}
return {
success: true,
stdout: stdout ? stdout.toString() : "",
stderr: stderr ? stderr.toString() : "",
};
} catch (error: any) {
// Handle script execution errors
return {
success: false,
stdout: error.stdout ? error.stdout.toString() : "",
stderr: error.stderr ? error.stderr.toString() : "",
error: error.message || "Failed to execute script",
};
}
});
return result as ScriptResult;
}
// Get current queue size and pending tasks
export function getQueueStats() {
return {
size: scriptQueue.size,
pending: scriptQueue.pending,
isPaused: scriptQueue.isPaused,
};
}
// Pause the queue (no further tasks will be executed until resumed)
export function pauseQueue() {
scriptQueue.pause();
}
// Resume the queue
export function resumeQueue() {
scriptQueue.start();
}

32
package-lock.json generated
View File

@ -36,6 +36,7 @@
"next": "^14.2.5",
"open": "^10.0.3",
"ora": "^8.0.1",
"p-queue": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"regenerator-runtime": "0.13.7",
@ -26910,6 +26911,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-queue": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz",
"integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==",
"dependencies": {
"eventemitter3": "^5.0.1",
"p-timeout": "^6.1.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-queue/node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/p-reduce": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz",
@ -26932,6 +26953,17 @@
"node": ">=8"
}
},
"node_modules/p-timeout": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
"integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",

View File

@ -56,6 +56,7 @@
"next": "^14.2.5",
"open": "^10.0.3",
"ora": "^8.0.1",
"p-queue": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"regenerator-runtime": "0.13.7",