284 lines
9.6 KiB
JavaScript
284 lines
9.6 KiB
JavaScript
import { renderSidebarContent } from './sidebarRenderer.js';
|
|
import { generateUUID } from './constants.js';
|
|
|
|
export function isRemoteWorker(extension, worker) {
|
|
// Check if explicitly marked as cloud worker
|
|
if (worker.type === "cloud") {
|
|
return true;
|
|
}
|
|
// Otherwise check by host (backward compatibility)
|
|
const parsed = extension._parseHostInput(worker.host || window.location.hostname);
|
|
const host = parsed.host || window.location.hostname;
|
|
return host !== "localhost" && host !== "127.0.0.1" && host !== window.location.hostname;
|
|
}
|
|
|
|
export function isCloudWorker(extension, worker) {
|
|
return worker.type === "cloud";
|
|
}
|
|
|
|
export async function saveWorkerSettings(extension, workerId) {
|
|
const worker = extension.config.workers.find((w) => w.id === workerId);
|
|
if (!worker) {
|
|
return;
|
|
}
|
|
|
|
// Get form values
|
|
const name = document.getElementById(`name-${workerId}`).value;
|
|
const workerType = document.getElementById(`worker-type-${workerId}`).value;
|
|
const isRemote = workerType === "remote" || workerType === "cloud";
|
|
const isCloud = workerType === "cloud";
|
|
const rawHost = isRemote ? document.getElementById(`host-${workerId}`).value : window.location.hostname;
|
|
const parsedHost = isRemote ? extension._parseHostInput(rawHost) : { host: window.location.hostname, port: null };
|
|
const host = isRemote ? parsedHost.host : window.location.hostname;
|
|
let port = parseInt(document.getElementById(`port-${workerId}`).value);
|
|
const cudaDevice = isRemote ? undefined : parseInt(document.getElementById(`cuda-${workerId}`).value);
|
|
const extraArgs = isRemote ? undefined : document.getElementById(`args-${workerId}`).value;
|
|
|
|
if (isRemote && Number.isFinite(parsedHost.port)) {
|
|
port = parsedHost.port;
|
|
}
|
|
|
|
// Validate
|
|
if (!name.trim()) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Validation Error",
|
|
detail: "Worker name is required",
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if ((workerType === "remote" || workerType === "cloud") && !host.trim()) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Validation Error",
|
|
detail: "Host is required for remote workers",
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!isCloud && (isNaN(port) || port < 1 || port > 65535)) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Validation Error",
|
|
detail: "Port must be between 1 and 65535",
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check for port conflicts
|
|
// Remote workers can reuse ports, but local workers cannot share ports with each other or master
|
|
if (!isRemote) {
|
|
// Check if port conflicts with master
|
|
const masterPort = parseInt(window.location.port) || (window.location.protocol === "https:" ? 443 : 80);
|
|
if (port === masterPort) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Port Conflict",
|
|
detail: `Port ${port} is already in use by the master server`,
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if port conflicts with other local workers
|
|
const localPortConflict = extension.config.workers.some(
|
|
(w) => w.id !== workerId && w.port === port && !w.host // local workers have no host or host is null
|
|
);
|
|
|
|
if (localPortConflict) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Port Conflict",
|
|
detail: `Port ${port} is already in use by another local worker`,
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
} else {
|
|
// For remote workers, only check conflicts with other workers on the same host
|
|
const sameHostConflict = extension.config.workers.some((w) => w.id !== workerId && w.port === port && w.host === host.trim());
|
|
|
|
if (sameHostConflict) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Port Conflict",
|
|
detail: `Port ${port} is already in use by another worker on ${host}`,
|
|
life: 3000,
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
await extension.api.updateWorker(workerId, {
|
|
name: name.trim(),
|
|
type: workerType,
|
|
host: isRemote ? host.trim() : null,
|
|
port,
|
|
cuda_device: isRemote ? null : cudaDevice,
|
|
extra_args: isRemote ? null : extraArgs ? extraArgs.trim() : "",
|
|
});
|
|
|
|
// Update local config
|
|
worker.name = name.trim();
|
|
worker.type = workerType;
|
|
if (isRemote) {
|
|
worker.host = host.trim();
|
|
delete worker.cuda_device;
|
|
delete worker.extra_args;
|
|
} else {
|
|
delete worker.host;
|
|
worker.cuda_device = cudaDevice;
|
|
worker.extra_args = extraArgs ? extraArgs.trim() : "";
|
|
}
|
|
worker.port = port;
|
|
|
|
// Sync to state
|
|
extension.state.updateWorker(workerId, { enabled: worker.enabled });
|
|
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "success",
|
|
summary: "Settings Saved",
|
|
detail: `Worker ${name} settings updated`,
|
|
life: 3000,
|
|
});
|
|
|
|
// Refresh the UI
|
|
if (extension.panelElement) {
|
|
renderSidebarContent(extension, extension.panelElement);
|
|
}
|
|
} catch (error) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Save Failed",
|
|
detail: error.message,
|
|
life: 5000,
|
|
});
|
|
}
|
|
}
|
|
|
|
export function cancelWorkerSettings(extension, workerId) {
|
|
// Collapse the settings panel
|
|
extension.toggleWorkerExpanded(workerId);
|
|
|
|
// Reset form values to original
|
|
const worker = extension.config.workers.find((w) => w.id === workerId);
|
|
if (worker) {
|
|
document.getElementById(`name-${workerId}`).value = worker.name;
|
|
document.getElementById(`host-${workerId}`).value = worker.host || "";
|
|
document.getElementById(`port-${workerId}`).value = worker.port;
|
|
document.getElementById(`cuda-${workerId}`).value = worker.cuda_device || 0;
|
|
document.getElementById(`args-${workerId}`).value = worker.extra_args || "";
|
|
|
|
// Reset remote checkbox
|
|
const remoteCheckbox = document.getElementById(`remote-${workerId}`);
|
|
if (remoteCheckbox) {
|
|
remoteCheckbox.checked = extension.isRemoteWorker(worker);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function deleteWorker(extension, workerId) {
|
|
const worker = extension.config.workers.find((w) => w.id === workerId);
|
|
if (!worker) {
|
|
return;
|
|
}
|
|
|
|
// Confirm deletion
|
|
if (!confirm(`Are you sure you want to delete worker "${worker.name}"?`)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await extension.api.deleteWorker(workerId);
|
|
|
|
// Remove from local config
|
|
const index = extension.config.workers.findIndex((w) => w.id === workerId);
|
|
if (index !== -1) {
|
|
extension.config.workers.splice(index, 1);
|
|
}
|
|
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "success",
|
|
summary: "Worker Deleted",
|
|
detail: `Worker ${worker.name} has been removed`,
|
|
life: 3000,
|
|
});
|
|
|
|
// Refresh the UI
|
|
if (extension.panelElement) {
|
|
renderSidebarContent(extension, extension.panelElement);
|
|
}
|
|
} catch (error) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Delete Failed",
|
|
detail: error.message,
|
|
life: 5000,
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function addNewWorker(extension) {
|
|
// Generate new worker ID using UUID (fallback for non-secure contexts)
|
|
const newId = generateUUID();
|
|
|
|
// Find next available port
|
|
const usedPorts = extension.config.workers.map((w) => w.port);
|
|
let nextPort = 8189;
|
|
while (usedPorts.includes(nextPort)) {
|
|
nextPort++;
|
|
}
|
|
|
|
// Create new worker object
|
|
const newWorker = {
|
|
id: newId,
|
|
name: `Worker ${extension.config.workers.length + 1}`,
|
|
port: nextPort,
|
|
cuda_device: extension.config.workers.length,
|
|
enabled: true, // Default to enabled for convenience
|
|
extra_args: "",
|
|
};
|
|
|
|
// Add to config
|
|
extension.config.workers.push(newWorker);
|
|
|
|
// Save immediately
|
|
try {
|
|
await extension.api.updateWorker(newId, {
|
|
name: newWorker.name,
|
|
port: newWorker.port,
|
|
cuda_device: newWorker.cuda_device,
|
|
extra_args: newWorker.extra_args,
|
|
enabled: newWorker.enabled,
|
|
});
|
|
|
|
// Sync to state
|
|
extension.state.updateWorker(newId, { enabled: true });
|
|
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "success",
|
|
summary: "Worker Added",
|
|
detail: `New worker created on port ${nextPort}`,
|
|
life: 3000,
|
|
});
|
|
|
|
// Refresh UI and expand the new worker
|
|
extension.state.setWorkerExpanded(newId, true);
|
|
if (extension.panelElement) {
|
|
renderSidebarContent(extension, extension.panelElement);
|
|
}
|
|
} catch (error) {
|
|
extension.app.extensionManager.toast.add({
|
|
severity: "error",
|
|
summary: "Failed to Add Worker",
|
|
detail: error.message,
|
|
life: 5000,
|
|
});
|
|
}
|
|
}
|