202 lines
7.4 KiB
TypeScript
202 lines
7.4 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import { Shield, ShieldAlert, RefreshCw, Wifi, Power } from "lucide-react";
|
|
|
|
interface Props {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const VpnGuard: React.FC<Props> = ({ children }) => {
|
|
|
|
const VPN_API = "http://10.0.0.1";
|
|
const DEFAULT_API = "https://portal-sf.docva.com"
|
|
|
|
const [checking, setChecking] = useState(true);
|
|
const [vpnConnected, setVpnConnected] = useState(true);
|
|
const [isRetrying, setIsRetrying] = useState(false);
|
|
|
|
const checkVPN = async () => {
|
|
try {
|
|
setChecking(true);
|
|
|
|
const res = await window.electron?.checkRegion();
|
|
console.log("VpnGuard", res);
|
|
if (res.status === 200) {
|
|
setVpnConnected(true);
|
|
} else {
|
|
setVpnConnected(false);
|
|
return;
|
|
}
|
|
// setVpnConnected(res?.success === true);
|
|
} catch (error) {
|
|
console.error("VPN check failed:", error);
|
|
setVpnConnected(false);
|
|
} finally {
|
|
setChecking(false);
|
|
setIsRetrying(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
checkVPN();
|
|
}, []);
|
|
|
|
const connectVPN = async () => {
|
|
setChecking(true);
|
|
try {
|
|
const res = await window.electron.connectVPN();
|
|
console.log("connectVPN result → ", res);
|
|
|
|
// Only proceed if VPN actually connected
|
|
if (res === "Windows VPN connected silently") {
|
|
|
|
const vpnRes = await window.electron.updateBaseURL(VPN_API);
|
|
console.log("vpnRes", vpnRes);
|
|
|
|
if (vpnRes) {
|
|
// Run VPN check again AFTER URL changes
|
|
await checkVPN();
|
|
} else {
|
|
console.warn("Base URL updated but health check failed");
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
|
|
const vpnRes = await window.electron.updateBaseURL(VPN_API);
|
|
console.log("vpnRes", vpnRes);
|
|
|
|
if (vpnRes) {
|
|
|
|
// Run VPN check again AFTER URL changes
|
|
await checkVPN();
|
|
} else {
|
|
console.warn("Base URL updated but health check failed");
|
|
}
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
};
|
|
|
|
|
|
const disconnectVPN = async () => {
|
|
setChecking(true);
|
|
try {
|
|
const res = await window.electron.disconnectVPN();;
|
|
console.log("disconnectVPN →", res);
|
|
|
|
// Restore original base URL
|
|
const baseRes = await window.electron.updateBaseURL(DEFAULT_API);
|
|
console.log("baseRes disconnect →", baseRes);
|
|
|
|
setVpnConnected(true);
|
|
|
|
// await checkVPN();
|
|
} catch (error) {
|
|
console.log("Disconnect Error:", error);
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
};
|
|
|
|
|
|
// Checking state
|
|
if (checking && !isRetrying) {
|
|
return (
|
|
<div className="min-h-screen flex flex-col justify-center items-center bg-base-200">
|
|
<div className="relative">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-24 h-24 border-4 border-primary opacity-20 rounded-full animate-ping"></div>
|
|
</div>
|
|
<div className="relative bg-base-100 rounded-full p-6 shadow-xl">
|
|
<Shield className="w-12 h-12 text-primary animate-pulse" />
|
|
</div>
|
|
</div>
|
|
<div className="mt-8 text-center">
|
|
<h2 className="text-2xl font-semibold mb-2">
|
|
Verifying Connection
|
|
</h2>
|
|
<p className="text-base-content opacity-70">Checking VPN status...</p>
|
|
<div className="flex justify-center mt-4 gap-1">
|
|
<span className="loading loading-dots loading-md text-primary"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// VPN not connected state
|
|
if (!vpnConnected) {
|
|
return (
|
|
<div className="min-h-screen flex flex-col justify-center items-center bg-base-200 p-4">
|
|
<div className="max-w-md w-full">
|
|
<div className="card bg-base-100 shadow-2xl">
|
|
<div className="card-body items-center text-center">
|
|
|
|
{/* Icon */}
|
|
<div className="relative mb-4">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-20 h-20 border-4 border-error opacity-20 rounded-full animate-pulse"></div>
|
|
</div>
|
|
<div className="relative bg-error bg-opacity-10 rounded-full p-4">
|
|
<ShieldAlert className="w-12 h-12" />
|
|
</div>
|
|
</div>
|
|
|
|
<h2 className="card-title text-3xl mb-3">VPN Required</h2>
|
|
<p className="mb-6 opacity-80">
|
|
Please connect to your company VPN to access this application.
|
|
</p>
|
|
|
|
{/* Connect VPN */}
|
|
<button
|
|
className="btn btn-primary btn-wide gap-2"
|
|
onClick={connectVPN}
|
|
disabled={isRetrying || vpnConnected} // Disable if retrying OR already connected
|
|
>
|
|
{isRetrying ? (
|
|
<>
|
|
<span className="loading loading-spinner loading-sm"></span>
|
|
Checking Connection...
|
|
</>
|
|
) : (
|
|
<>
|
|
<RefreshCw className="w-5 h-5" />
|
|
Connect VPN
|
|
</>
|
|
)}
|
|
</button>
|
|
|
|
{/* Disconnect VPN (Disabled when NOT connected) */}
|
|
<button
|
|
className="btn btn-error btn-wide gap-2 mt-3"
|
|
onClick={disconnectVPN}
|
|
// disabled={!vpnConnected || checking} // Disable if not connected
|
|
>
|
|
<Power className="w-4 h-4" />
|
|
Disconnect VPN
|
|
</button>
|
|
|
|
<div className="divider"></div>
|
|
<p className="text-xs opacity-60">
|
|
Need help? Contact IT support at{" "}
|
|
<a href="mailto:support@docva.com" className="link link-primary">
|
|
support@docva.com
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|
|
return (
|
|
<>
|
|
{children}
|
|
</>
|
|
);
|
|
|
|
};
|
|
|
|
export default VpnGuard; |