v1.8.0 #429

Merged
LukeGus merged 198 commits from dev-1.8.0 into main 2025-11-05 16:36:16 +00:00
5 changed files with 144 additions and 38 deletions
Showing only changes of commit 00e19f054f - Show all commits

View File

@@ -72,12 +72,21 @@ function createWindow() {
mainWindow.setMenuBarVisibility(false); mainWindow.setMenuBarVisibility(false);
} }
mainWindow.webContents.setUserAgent( const customUserAgent = `Termix-Desktop/${appVersion} (${platform}; Electron/${electronVersion})`;
`Termix-Desktop/${appVersion} (${platform}; Electron/${electronVersion})`, mainWindow.webContents.setUserAgent(customUserAgent);
mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
details.requestHeaders["X-Electron-App"] = "true";
details.requestHeaders["User-Agent"] = customUserAgent;
callback({ requestHeaders: details.requestHeaders });
},
); );
if (isDev) { if (isDev) {
mainWindow.loadURL("http:://localhost:5173"); mainWindow.loadURL("http://localhost:5173");
mainWindow.webContents.openDevTools(); mainWindow.webContents.openDevTools();
} else { } else {
const indexPath = path.join(__dirname, "..", "dist", "index.html"); const indexPath = path.join(__dirname, "..", "dist", "index.html");

View File

@@ -241,6 +241,22 @@ export function Auth({
throw new Error(t("errors.loginFailed")); throw new Error(t("errors.loginFailed"));
} }
if (isInElectronWebView() && res.token) {
try {
localStorage.setItem("jwt", res.token);
window.parent.postMessage(
{
type: "AUTH_SUCCESS",
token: res.token,
source: "auth_component",
platform: "desktop",
timestamp: Date.now(),
},
"*",
);
} catch (e) {}
}
const [meRes] = await Promise.all([getUserInfo()]); const [meRes] = await Promise.all([getUserInfo()]);
setInternalLoggedIn(true); setInternalLoggedIn(true);
@@ -398,6 +414,22 @@ export function Auth({
localStorage.setItem("jwt", res.token); localStorage.setItem("jwt", res.token);
} }
if (isInElectronWebView() && res.token) {
try {
localStorage.setItem("jwt", res.token);
window.parent.postMessage(
{
type: "AUTH_SUCCESS",
token: res.token,
source: "totp_auth_component",
platform: "desktop",
timestamp: Date.now(),
},
"*",
);
} catch (e) {}
}
setInternalLoggedIn(true); setInternalLoggedIn(true);
setLoggedIn(true); setLoggedIn(true);
setIsAdmin(!!res.is_admin); setIsAdmin(!!res.is_admin);
@@ -485,6 +517,24 @@ export function Auth({
getUserInfo() getUserInfo()
.then((meRes) => { .then((meRes) => {
if (isInElectronWebView()) {
const token = getCookie("jwt") || localStorage.getItem("jwt");
if (token) {
try {
window.parent.postMessage(
{
type: "AUTH_SUCCESS",
token: token,
source: "oidc_callback",
platform: "desktop",
timestamp: Date.now(),
},
"*",
);
} catch (e) {}
}
}
setInternalLoggedIn(true); setInternalLoggedIn(true);
setLoggedIn(true); setLoggedIn(true);
setIsAdmin(!!meRes.is_admin); setIsAdmin(!!meRes.is_admin);
@@ -577,7 +627,8 @@ export function Auth({
if (showServerConfig === null) { if (showServerConfig === null) {
return ( return (
<div <div
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ""}`} className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
<div className="flex items-center justify-center h-32"> <div className="flex items-center justify-center h-32">
@@ -590,7 +641,8 @@ export function Auth({
if (showServerConfig) { if (showServerConfig) {
return ( return (
<div <div
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ""}`} className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
<ServerConfigComponent <ServerConfigComponent
@@ -645,7 +697,8 @@ export function Auth({
if (dbHealthChecking && !dbConnectionFailed) { if (dbHealthChecking && !dbConnectionFailed) {
return ( return (
<div <div
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ""}`} className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
<div className="flex items-center justify-center h-32"> <div className="flex items-center justify-center h-32">
@@ -663,7 +716,8 @@ export function Auth({
if (dbConnectionFailed) { if (dbConnectionFailed) {
return ( return (
<div <div
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ""}`} className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
<div className="mb-6 text-center"> <div className="mb-6 text-center">
@@ -722,7 +776,8 @@ export function Auth({
return ( return (
<div <div
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ""}`} className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
{isInElectronWebView() && ( {isInElectronWebView() && (

View File

@@ -57,16 +57,13 @@ export function ElectronLoginForm({
onAuthSuccess(); onAuthSuccess();
} catch (err) { } catch (err) {
console.error("[ElectronLoginForm] Error saving JWT:", err);
setError(t("errors.authTokenSaveFailed")); setError(t("errors.authTokenSaveFailed"));
setIsAuthenticating(false); setIsAuthenticating(false);
hasAuthenticatedRef.current = false; hasAuthenticatedRef.current = false;
} }
} }
} }
} catch (err) { } catch (err) {}
console.error("[ElectronLoginForm] Error processing message:", err);
}
}; };
window.addEventListener("message", handleMessage); window.addEventListener("message", handleMessage);
@@ -99,7 +96,9 @@ export function ElectronLoginForm({
let hasNotified = false; let hasNotified = false;
function postJWTToParent(token, source) { function postJWTToParent(token, source) {
if (hasNotified) return; if (hasNotified) {
return;
}
hasNotified = true; hasNotified = true;
try { try {
@@ -111,7 +110,6 @@ export function ElectronLoginForm({
timestamp: Date.now() timestamp: Date.now()
}, '*'); }, '*');
} catch (e) { } catch (e) {
console.error('[Electron WebView] Error posting message:', e);
} }
} }
@@ -143,7 +141,6 @@ export function ElectronLoginForm({
} }
} }
} catch (error) { } catch (error) {
console.error('[Electron WebView] Error in checkAuth:', error);
} }
return false; return false;
} }
@@ -178,27 +175,23 @@ export function ElectronLoginForm({
clearInterval(intervalId); clearInterval(intervalId);
}, 300000); }, 300000);
checkAuth(); setTimeout(() => checkAuth(), 500);
})(); })();
`; `;
try { try {
if (iframe.contentWindow) { if (iframe.contentWindow) {
iframe.contentWindow.postMessage( try {
{ type: "INJECT_SCRIPT", script: injectedScript }, iframe.contentWindow.eval(injectedScript);
"*", } catch (evalError) {
); iframe.contentWindow.postMessage(
{ type: "INJECT_SCRIPT", script: injectedScript },
iframe.contentWindow.eval(injectedScript); "*",
);
}
} }
} catch (err) { } catch (err) {}
console.warn( } catch (err) {}
"[ElectronLoginForm] Cannot inject script due to cross-origin restrictions",
);
}
} catch (err) {
console.error("[ElectronLoginForm] Error in handleLoad:", err);
}
}; };
const handleError = () => { const handleError = () => {

View File

@@ -1695,10 +1695,34 @@ export async function loginUser(
try { try {
const response = await authApi.post("/users/login", { username, password }); const response = await authApi.post("/users/login", { username, password });
if (isElectron() && response.data.token) { const hasToken = response.data.token;
if (isElectron() && hasToken) {
localStorage.setItem("jwt", response.data.token); localStorage.setItem("jwt", response.data.token);
} }
const isInIframe =
typeof window !== "undefined" && window.self !== window.top;
if (isInIframe && hasToken) {
localStorage.setItem("jwt", response.data.token);
try {
window.parent.postMessage(
{
type: "AUTH_SUCCESS",
token: response.data.token,
source: "login_api",
platform: "desktop",
timestamp: Date.now(),
},
"*",
);
} catch (e) {
console.error("[main-axios] Error posting message to parent:", e);
}
}
return { return {
token: response.data.token || "cookie-based", token: response.data.token || "cookie-based",
success: response.data.success, success: response.data.success,
@@ -2027,6 +2051,35 @@ export async function verifyTOTPLogin(
temp_token, temp_token,
totp_code, totp_code,
}); });
const hasToken = response.data.token;
if (isElectron() && hasToken) {
localStorage.setItem("jwt", response.data.token);
}
const isInIframe =
typeof window !== "undefined" && window.self !== window.top;
if (isInIframe && hasToken) {
localStorage.setItem("jwt", response.data.token);
try {
window.parent.postMessage(
{
type: "AUTH_SUCCESS",
token: response.data.token,
source: "totp_verify",
platform: "desktop",
timestamp: Date.now(),
},
"*",
);
} catch (e) {
console.error("[main-axios] Error posting message to parent:", e);
}
}
return response.data; return response.data;
} catch (error) { } catch (error) {
handleApiError(error as AxiosError, "verify TOTP login"); handleApiError(error as AxiosError, "verify TOTP login");

View File

@@ -40,7 +40,6 @@ function postJWTToWebView() {
const jwt = getCookie("jwt") || localStorage.getItem("jwt"); const jwt = getCookie("jwt") || localStorage.getItem("jwt");
if (!jwt) { if (!jwt) {
console.warn("JWT not found when trying to post to WebView");
return; return;
} }
@@ -53,11 +52,7 @@ function postJWTToWebView() {
timestamp: Date.now(), timestamp: Date.now(),
}), }),
); );
} catch (error) {}
console.log("JWT posted to React Native WebView");
} catch (error) {
console.error("Failed to post JWT to WebView:", error);
}
} }
interface AuthProps extends React.ComponentProps<"div"> { interface AuthProps extends React.ComponentProps<"div"> {
@@ -563,7 +558,8 @@ export function Auth({
return ( return (
<div <div
className={`w-full max-w-md flex flex-col bg-dark-bg ${className || ""}`} className={`w-full max-w-md flex flex-col bg-dark-bg overflow-y-auto my-2 ${className || ""}`}
style={{ maxHeight: "calc(100vh - 1rem)" }}
{...props} {...props}
> >
{isReactNativeWebView() && ( {isReactNativeWebView() && (