117 lines
3.7 KiB
JavaScript
117 lines
3.7 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
import './styles.css';
|
|
import { api } from './api.js';
|
|
import { ClientOverviewPage } from './components/ClientOverviewPage.jsx';
|
|
|
|
function App() {
|
|
const previewReady = new URLSearchParams(window.location.search).has('preview-ready');
|
|
const [state, setState] = useState(null);
|
|
const [subscriptionUrl, setSubscriptionUrl] = useState('');
|
|
const [servers, setServers] = useState([]);
|
|
const [pendingTag, setPendingTag] = useState('');
|
|
const [busy, setBusy] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
async function loadState() {
|
|
const data = await api.state();
|
|
setState(data);
|
|
setServers(data.servers || []);
|
|
setPendingTag((current) => (
|
|
(data.servers || []).some((server) => server.tag === current)
|
|
? current
|
|
: data.selectedTag || ''
|
|
));
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadState().catch((err) => setError(err.message));
|
|
const timer = setInterval(() => loadState().catch(() => {}), 5000);
|
|
return () => clearInterval(timer);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!state?.mode) return;
|
|
const isGateway = state.mode === 'gateway';
|
|
document.title = isGateway ? 'Harbor Gateway' : 'Harbor Connect';
|
|
document.getElementById('harbor-favicon').href = isGateway
|
|
? '/harbor-gateway.svg'
|
|
: '/harbor-connect.svg';
|
|
}, [state?.mode]);
|
|
|
|
async function run(action) {
|
|
setBusy(true);
|
|
setError('');
|
|
try {
|
|
await action();
|
|
await loadState();
|
|
} catch (err) {
|
|
setError(err.message);
|
|
throw err;
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
async function fetchSubscription() {
|
|
return run(async () => {
|
|
const data = await api.subscription.fetch(subscriptionUrl);
|
|
setServers(data.servers || []);
|
|
setPendingTag('');
|
|
});
|
|
}
|
|
|
|
async function refreshSubscription() {
|
|
const data = await api.subscription.refresh();
|
|
setState((current) => current ? {
|
|
...current,
|
|
userInfo: data.userInfo,
|
|
fetchedAt: data.fetchedAt,
|
|
servers: data.servers,
|
|
selectedTag: data.selectedTag,
|
|
} : current);
|
|
setServers(data.servers || []);
|
|
setPendingTag(data.selectedTag || '');
|
|
return data;
|
|
}
|
|
|
|
async function forgetSubscription() {
|
|
return run(async () => {
|
|
await api.subscription.forget();
|
|
setSubscriptionUrl('');
|
|
setServers([]);
|
|
setPendingTag('');
|
|
});
|
|
}
|
|
|
|
if (!state) return <div className="app-loading">Harbor</div>;
|
|
|
|
return (
|
|
<div className="app client-app">
|
|
<div className="app-body client-mode">
|
|
<main className="app-main">
|
|
<ClientOverviewPage
|
|
state={previewReady ? { ...state, mode: 'client', hasSubscription: true, subscriptionHost: 'harbor.example', selectedTag: 'Amsterdam', proxyPort: 8082 } : state}
|
|
busy={busy}
|
|
error={error}
|
|
subscriptionUrl={subscriptionUrl}
|
|
setSubscriptionUrl={setSubscriptionUrl}
|
|
servers={previewReady ? [{ tag: 'Amsterdam', server: '127.0.0.1', server_port: 443 }] : servers}
|
|
pendingTag={previewReady ? 'Amsterdam' : pendingTag}
|
|
setPendingTag={setPendingTag}
|
|
onFetchSubscription={fetchSubscription}
|
|
onRefreshSubscription={refreshSubscription}
|
|
onForgetSubscription={forgetSubscription}
|
|
onApply={(tag) => run(() => api.apply(tag))}
|
|
onRestart={() => run(api.singbox.restart)}
|
|
onStop={() => run(api.singbox.stop)}
|
|
onSetGatewayAuto={(enabled) => run(() => api.gatewayAuto.setEnabled(enabled))}
|
|
/>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
createRoot(document.getElementById('root')).render(<App />);
|