feat: Add build scripts and runtime config support
Backend: - Add setup-backend.sh/bat for one-click backend setup - Fix test_auth.py mock settings (JWT_EXPIRE_HOURS) - Fix test_excel_export.py TEMPLATE_DIR reference Frontend: - Add config.json for runtime API URL configuration - Add init.js and settings.js for config loading - Update main.js to load config from external file - Update api.js to use dynamic API_BASE_URL - Update all pages to initialize config before API calls - Update package.json with extraResources for config Build: - Add build-client.sh/bat for packaging Electron + Sidecar - Add build-all.ps1 PowerShell script with -ApiUrl parameter - Add GitHub Actions workflow for Windows builds - Add scripts/README.md documentation This allows IT to configure backend URL without rebuilding. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,61 @@ let mainWindow;
|
||||
let sidecarProcess;
|
||||
let sidecarReady = false;
|
||||
let streamingActive = false;
|
||||
let appConfig = null;
|
||||
|
||||
/**
|
||||
* Load configuration from external config.json
|
||||
* Config file location:
|
||||
* - Development: client/config.json
|
||||
* - Packaged: <app>/resources/config.json
|
||||
*/
|
||||
function loadConfig() {
|
||||
const configPaths = [
|
||||
// Packaged app: resources folder
|
||||
app.isPackaged ? path.join(process.resourcesPath, "config.json") : null,
|
||||
// Development: client folder
|
||||
path.join(__dirname, "..", "config.json"),
|
||||
// Fallback: same directory
|
||||
path.join(__dirname, "config.json"),
|
||||
].filter(Boolean);
|
||||
|
||||
for (const configPath of configPaths) {
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const configData = fs.readFileSync(configPath, "utf-8");
|
||||
appConfig = JSON.parse(configData);
|
||||
console.log("Config loaded from:", configPath);
|
||||
console.log("Config:", appConfig);
|
||||
return appConfig;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to load config from:", configPath, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Default configuration
|
||||
appConfig = {
|
||||
apiBaseUrl: "http://localhost:8000/api",
|
||||
uploadTimeout: 600000,
|
||||
appTitle: "Meeting Assistant",
|
||||
whisper: {
|
||||
model: "medium",
|
||||
device: "cpu",
|
||||
compute: "int8"
|
||||
}
|
||||
};
|
||||
console.log("Using default config:", appConfig);
|
||||
return appConfig;
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
// Set window title from config
|
||||
const windowTitle = appConfig?.appTitle || "Meeting Assistant";
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
title: windowTitle,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
@@ -32,34 +82,65 @@ function startSidecar() {
|
||||
? path.join(process.resourcesPath, "sidecar")
|
||||
: path.join(__dirname, "..", "..", "sidecar");
|
||||
|
||||
const sidecarScript = path.join(sidecarDir, "transcriber.py");
|
||||
const venvPython = path.join(sidecarDir, "venv", "bin", "python");
|
||||
// Determine the sidecar executable path based on packaging and platform
|
||||
let sidecarExecutable;
|
||||
let sidecarArgs = [];
|
||||
|
||||
if (!fs.existsSync(sidecarScript)) {
|
||||
console.log("Sidecar script not found at:", sidecarScript);
|
||||
if (app.isPackaged) {
|
||||
// Packaged app: use PyInstaller-built executable
|
||||
if (process.platform === "win32") {
|
||||
sidecarExecutable = path.join(sidecarDir, "transcriber", "transcriber.exe");
|
||||
} else if (process.platform === "darwin") {
|
||||
sidecarExecutable = path.join(sidecarDir, "transcriber", "transcriber");
|
||||
} else {
|
||||
sidecarExecutable = path.join(sidecarDir, "transcriber", "transcriber");
|
||||
}
|
||||
} else {
|
||||
// Development mode: use Python script with venv
|
||||
const sidecarScript = path.join(sidecarDir, "transcriber.py");
|
||||
|
||||
if (!fs.existsSync(sidecarScript)) {
|
||||
console.log("Sidecar script not found at:", sidecarScript);
|
||||
console.log("Transcription will not be available.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for virtual environment Python
|
||||
let venvPython;
|
||||
if (process.platform === "win32") {
|
||||
venvPython = path.join(sidecarDir, "venv", "Scripts", "python.exe");
|
||||
} else {
|
||||
venvPython = path.join(sidecarDir, "venv", "bin", "python");
|
||||
}
|
||||
|
||||
sidecarExecutable = fs.existsSync(venvPython) ? venvPython : "python3";
|
||||
sidecarArgs = [sidecarScript];
|
||||
}
|
||||
|
||||
if (!fs.existsSync(sidecarExecutable)) {
|
||||
console.log("Sidecar executable not found at:", sidecarExecutable);
|
||||
console.log("Transcription will not be available.");
|
||||
return;
|
||||
}
|
||||
|
||||
const pythonPath = fs.existsSync(venvPython) ? venvPython : "python3";
|
||||
|
||||
try {
|
||||
// Get Whisper configuration from environment variables
|
||||
// Get Whisper configuration from config.json or environment variables
|
||||
const whisperConfig = appConfig?.whisper || {};
|
||||
const whisperEnv = {
|
||||
...process.env,
|
||||
WHISPER_MODEL: process.env.WHISPER_MODEL || "medium",
|
||||
WHISPER_DEVICE: process.env.WHISPER_DEVICE || "cpu",
|
||||
WHISPER_COMPUTE: process.env.WHISPER_COMPUTE || "int8",
|
||||
WHISPER_MODEL: process.env.WHISPER_MODEL || whisperConfig.model || "medium",
|
||||
WHISPER_DEVICE: process.env.WHISPER_DEVICE || whisperConfig.device || "cpu",
|
||||
WHISPER_COMPUTE: process.env.WHISPER_COMPUTE || whisperConfig.compute || "int8",
|
||||
};
|
||||
|
||||
console.log("Starting sidecar with:", pythonPath, sidecarScript);
|
||||
console.log("Starting sidecar with:", sidecarExecutable, sidecarArgs.join(" "));
|
||||
console.log("Whisper config:", {
|
||||
model: whisperEnv.WHISPER_MODEL,
|
||||
device: whisperEnv.WHISPER_DEVICE,
|
||||
compute: whisperEnv.WHISPER_COMPUTE,
|
||||
});
|
||||
|
||||
sidecarProcess = spawn(pythonPath, [sidecarScript], {
|
||||
sidecarProcess = spawn(sidecarExecutable, sidecarArgs, {
|
||||
cwd: sidecarDir,
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: whisperEnv,
|
||||
@@ -121,6 +202,9 @@ function startSidecar() {
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// Load configuration first
|
||||
loadConfig();
|
||||
|
||||
createWindow();
|
||||
startSidecar();
|
||||
|
||||
@@ -144,6 +228,12 @@ app.on("window-all-closed", () => {
|
||||
});
|
||||
|
||||
// IPC handlers
|
||||
|
||||
// Get app configuration (for renderer to initialize API settings)
|
||||
ipcMain.handle("get-config", () => {
|
||||
return appConfig;
|
||||
});
|
||||
|
||||
ipcMain.handle("navigate", (event, page) => {
|
||||
mainWindow.loadFile(path.join(__dirname, "pages", `${page}.html`));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user