mirror of
https://github.com/open-webui/desktop.git
synced 2026-07-16 10:34:27 -04:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 398dc3128e | |||
| d991908205 | |||
| 2528096e07 | |||
| aeba0e7c22 | |||
| ffbbb8f5b2 | |||
| 119409a2dc | |||
| cb564555dc | |||
| 00d739d0d0 |
+8
-4
@@ -16,12 +16,9 @@ import {
|
||||
} from "electron";
|
||||
import path, { join } from "path";
|
||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||
import log from "electron-log";
|
||||
|
||||
import icon from "../../resources/icon.png?asset";
|
||||
import trayIconImage from "../../resources/assets/tray.png?asset";
|
||||
|
||||
import {
|
||||
getLogFilePath,
|
||||
checkUrlAndOpen,
|
||||
getConfig,
|
||||
getServerLog,
|
||||
@@ -38,6 +35,12 @@ import {
|
||||
uninstallPython,
|
||||
} from "./utils";
|
||||
|
||||
import log from "electron-log";
|
||||
log.transports.file.resolvePathFn = () => getLogFilePath("main");
|
||||
|
||||
import icon from "../../resources/icon.png?asset";
|
||||
import trayIconImage from "../../resources/assets/tray.png?asset";
|
||||
|
||||
// Main application logic
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let tray: Tray | null = null;
|
||||
@@ -485,6 +488,7 @@ if (!gotTheLock) {
|
||||
});
|
||||
|
||||
ipcMain.handle("app:reset", async (event) => {
|
||||
await stopServerHandler();
|
||||
return await resetApp();
|
||||
});
|
||||
|
||||
|
||||
+76
-12
@@ -12,6 +12,18 @@ import { app, shell, Notification } from "electron";
|
||||
import { execFileSync, exec, spawn, execSync, execFile } from "child_process";
|
||||
|
||||
import log from "electron-log";
|
||||
log.transports.file.resolvePathFn = () => getLogFilePath("main");
|
||||
|
||||
const serverLogger = log.create({ logId: "server" });
|
||||
serverLogger.transports.file.resolvePath = () => getLogFilePath(`server`);
|
||||
|
||||
export const getLogFilePath = (name: string = "main"): string => {
|
||||
const logDir = path.join(getUserDataPath(), "logs");
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir, { recursive: true });
|
||||
}
|
||||
return path.join(logDir, `${name}.log`);
|
||||
};
|
||||
|
||||
export const getAppPath = (): string => {
|
||||
let appPath = app.getAppPath();
|
||||
@@ -346,6 +358,9 @@ export const installPython = async (
|
||||
encoding: "utf-8",
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
log.info("Successfully installed uv package");
|
||||
@@ -387,6 +402,9 @@ export const isPythonInstalled = (installationPath?: string) => {
|
||||
encoding: "utf-8",
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
log.info("Installed Python Version:", pythonVersion.trim());
|
||||
@@ -406,6 +424,9 @@ export const isUvInstalled = (installationPath?: string) => {
|
||||
encoding: "utf-8",
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -449,6 +470,39 @@ export const uninstallPython = (installationPath?: string): boolean => {
|
||||
export const resetApp = async (): Promise<void> => {
|
||||
await uninstallPython();
|
||||
log.info("Uninstalled Python environment");
|
||||
|
||||
// remove /data folder
|
||||
const dataPath = getOpenWebUIDataPath();
|
||||
if (fs.existsSync(dataPath)) {
|
||||
try {
|
||||
fs.rmSync(dataPath, { recursive: true });
|
||||
log.info("Removed data directory:", dataPath);
|
||||
} catch (error) {
|
||||
log.error("Failed to remove data directory:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// remove config file
|
||||
const configPath = path.join(getUserDataPath(), "config.json");
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
fs.unlinkSync(configPath);
|
||||
log.info("Removed config file:", configPath);
|
||||
} catch (error) {
|
||||
log.error("Failed to remove config file:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// remove secret key file
|
||||
const secretKeyPath = path.join(getOpenWebUIDataPath(), ".key");
|
||||
if (fs.existsSync(secretKeyPath)) {
|
||||
try {
|
||||
fs.unlinkSync(secretKeyPath);
|
||||
log.info("Removed secret key file:", secretKeyPath);
|
||||
} catch (error) {
|
||||
log.error("Failed to remove secret key file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
@@ -518,6 +572,9 @@ export const installPackage = (
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -568,6 +625,9 @@ export const isPackageInstalled = (packageName: string): boolean => {
|
||||
encoding: "utf-8",
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -645,7 +705,12 @@ export const startServer = async (
|
||||
const childProcess = spawn(pythonPath, commandArgs, {
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: { ...process.env },
|
||||
env: {
|
||||
...process.env,
|
||||
...(process.platform === "win32"
|
||||
? { PYTHONIOENCODING: "utf-8" }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!childProcess.pid) {
|
||||
@@ -658,24 +723,23 @@ export const startServer = async (
|
||||
|
||||
const appendLog = (source: string) => (data: Buffer) => {
|
||||
const logLine = data.toString().trim();
|
||||
const tag = `[${source}][PID:${childProcess.pid}]:`;
|
||||
logLines.push(`${tag} ${logLine}`);
|
||||
// (Optional) also log to main process console
|
||||
// log.info(`${tag} ${logLine}`);
|
||||
const line = `[${source}][PID:${childProcess.pid}]: ${logLine}`;
|
||||
logLines.push(line);
|
||||
serverLogger.info(line); // Log to console
|
||||
};
|
||||
childProcess.stdout?.on("data", appendLog("stdout"));
|
||||
childProcess.stderr?.on("data", appendLog("stderr"));
|
||||
childProcess.on("close", (code, signal) => {
|
||||
logLines.push(
|
||||
`[process][PID:${childProcess.pid}] Exited with code ${code} signal ${signal}`
|
||||
);
|
||||
const line = `[process][PID:${childProcess.pid}] Exited with code ${code} signal ${signal}`;
|
||||
serverLogger.info(line);
|
||||
logLines.push(line);
|
||||
serverPIDs.delete(childProcess.pid);
|
||||
// Note: we keep the logs available until manually cleared
|
||||
});
|
||||
childProcess.on("error", (err) => {
|
||||
logLines.push(
|
||||
`[process][PID:${childProcess.pid}] Error: ${err.message}`
|
||||
);
|
||||
const line = `[process][PID:${childProcess.pid}] Error: ${err.message}`;
|
||||
serverLogger.error(line);
|
||||
logLines.push(line);
|
||||
});
|
||||
|
||||
// Compute URL directly, do not try to parse logs
|
||||
@@ -746,7 +810,7 @@ export const checkUrlAndOpen = async (
|
||||
url: string,
|
||||
callback: Function = async () => {}
|
||||
) => {
|
||||
const maxAttempts = 150; // 5 minutes with 2-second intervals
|
||||
const maxAttempts = 1800; // 60 minutes with 2-second intervals
|
||||
const interval = 2000; // 2 seconds
|
||||
let attempts = 0;
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{:else if installed === false}
|
||||
<Installation bind:installed />
|
||||
{:else}
|
||||
<Controls />
|
||||
<Controls bind:installed />
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import About from "./Controls/About.svelte";
|
||||
import { info } from "../stores";
|
||||
|
||||
let { installed = $bindable(false) } = $props();
|
||||
|
||||
let selectedTab = $state("general");
|
||||
|
||||
onMount(async () => {});
|
||||
@@ -104,7 +106,7 @@
|
||||
class="flex-1 pt-1 sm:mt-0 overflow-y-scroll pr-1 scrollbar-hidden"
|
||||
>
|
||||
{#if selectedTab === "general"}
|
||||
<General info={$info} />
|
||||
<General bind:installed info={$info} />
|
||||
{:else if selectedTab === "about"}
|
||||
<About />
|
||||
{/if}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Spinner from "../common/Spinner.svelte";
|
||||
import ConfirmDialog from "../common/ConfirmDialog.svelte";
|
||||
|
||||
let { info } = $props();
|
||||
let { info, installed = $bindable(false) } = $props();
|
||||
|
||||
let config = $state(null);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
confirmLabel="Reset"
|
||||
onConfirm={async () => {
|
||||
try {
|
||||
installed = null;
|
||||
config = null;
|
||||
await window.electronAPI.resetApp();
|
||||
toast.success("App has been reset successfully.");
|
||||
|
||||
@@ -11,20 +11,12 @@
|
||||
import galaxyImage from "../assets/images/galaxy.jpg";
|
||||
import greenImage from "../assets/images/green.jpg";
|
||||
import adamImage from "../assets/images/adam.jpg";
|
||||
import earthImage from "../assets/images/earth.jpg";
|
||||
import nasaImage from "../assets/images/nasa.jpg";
|
||||
import neomImage from "../assets/images/neom.jpg";
|
||||
|
||||
let { installed = $bindable() } = $props();
|
||||
|
||||
let images = [
|
||||
galaxyImage,
|
||||
greenImage,
|
||||
adamImage,
|
||||
earthImage,
|
||||
nasaImage,
|
||||
neomImage,
|
||||
];
|
||||
let images = [galaxyImage, greenImage, adamImage, nasaImage, neomImage];
|
||||
|
||||
let mounted = $state(false);
|
||||
let currentTime = Date.now();
|
||||
@@ -218,7 +210,7 @@
|
||||
{`Continue`}
|
||||
</div>
|
||||
|
||||
<!-- <button
|
||||
<div
|
||||
class="text-xs mt-3 text-gray-500 cursor-pointer"
|
||||
in:fly={{
|
||||
delay: 500,
|
||||
@@ -226,8 +218,16 @@
|
||||
y: 10,
|
||||
}}
|
||||
>
|
||||
To connect to an existing server, click here.
|
||||
</button> -->
|
||||
By continuing, you agree to our
|
||||
<button
|
||||
class="underline"
|
||||
onclick={() => {
|
||||
window.electronAPI.openInBrowser(
|
||||
"https://github.com/open-webui/desktop/blob/main/LICENSE"
|
||||
);
|
||||
}}>license agreement</button
|
||||
>.
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,19 +7,11 @@
|
||||
import galaxyImage from "../assets/images/galaxy.jpg";
|
||||
import greenImage from "../assets/images/green.jpg";
|
||||
import adamImage from "../assets/images/adam.jpg";
|
||||
import earthImage from "../assets/images/earth.jpg";
|
||||
import nasaImage from "../assets/images/nasa.jpg";
|
||||
import neomImage from "../assets/images/neom.jpg";
|
||||
import { fly } from "svelte/transition";
|
||||
|
||||
let images = [
|
||||
galaxyImage,
|
||||
greenImage,
|
||||
adamImage,
|
||||
earthImage,
|
||||
nasaImage,
|
||||
neomImage,
|
||||
];
|
||||
let images = [galaxyImage, greenImage, adamImage, nasaImage, neomImage];
|
||||
|
||||
let startTime = $state(null);
|
||||
let currentTime = $state(null);
|
||||
|
||||
Reference in New Issue
Block a user