mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
272 lines
6.6 KiB
TypeScript
272 lines
6.6 KiB
TypeScript
import type { NodeItem } from '@/features/nodes/types';
|
|
|
|
export const WS_CONNECTED_LAST_SEEN = '__OPENFLARE_WS_CONNECTED__';
|
|
|
|
export function isWSConnectedLastSeen(value: string | null | undefined) {
|
|
return value === WS_CONNECTED_LAST_SEEN;
|
|
}
|
|
|
|
export function isMeaningfulTime(value: string | null | undefined) {
|
|
return (
|
|
Boolean(value) &&
|
|
!isWSConnectedLastSeen(value) &&
|
|
!String(value).startsWith('0001-01-01')
|
|
);
|
|
}
|
|
|
|
export function getNodeStatusVariant(status: NodeItem['status']) {
|
|
if (status === 'online') {
|
|
return 'success';
|
|
}
|
|
|
|
if (status === 'pending') {
|
|
return 'warning';
|
|
}
|
|
|
|
return 'danger';
|
|
}
|
|
|
|
export function getNodeStatusLabel(status: NodeItem['status']) {
|
|
if (status === 'online') {
|
|
return '在线';
|
|
}
|
|
|
|
if (status === 'pending') {
|
|
return '待接入';
|
|
}
|
|
|
|
return '离线';
|
|
}
|
|
|
|
export function getApplyVariant(result: NodeItem['latest_apply_result']) {
|
|
if (result === 'success') {
|
|
return 'success';
|
|
}
|
|
|
|
if (result === 'warning') {
|
|
return 'warning';
|
|
}
|
|
|
|
if (result === 'failed') {
|
|
return 'danger';
|
|
}
|
|
|
|
return 'warning';
|
|
}
|
|
|
|
export function getApplyLabel(result: NodeItem['latest_apply_result']) {
|
|
if (result === 'success') {
|
|
return '成功';
|
|
}
|
|
|
|
if (result === 'warning') {
|
|
return '警告';
|
|
}
|
|
|
|
if (result === 'failed') {
|
|
return '失败';
|
|
}
|
|
|
|
return '暂无';
|
|
}
|
|
|
|
export function getUpdateMode(node: NodeItem) {
|
|
if (node.update_requested) {
|
|
if (node.update_channel === 'preview') {
|
|
return { label: '等待预览更新', variant: 'warning' as const };
|
|
}
|
|
|
|
return { label: '等待更新', variant: 'warning' as const };
|
|
}
|
|
|
|
if (node.auto_update_enabled) {
|
|
return { label: '自动', variant: 'success' as const };
|
|
}
|
|
|
|
return { label: '手动', variant: 'info' as const };
|
|
}
|
|
|
|
export function getOpenrestyStatusVariant(
|
|
status: NodeItem['openresty_status'],
|
|
) {
|
|
if (status === 'healthy') {
|
|
return 'success';
|
|
}
|
|
|
|
if (status === 'unhealthy') {
|
|
return 'danger';
|
|
}
|
|
|
|
return 'warning';
|
|
}
|
|
|
|
export function getOpenrestyStatusLabel(status: NodeItem['openresty_status']) {
|
|
if (status === 'healthy') {
|
|
return '健康';
|
|
}
|
|
|
|
if (status === 'unhealthy') {
|
|
return '异常';
|
|
}
|
|
|
|
return '未知';
|
|
}
|
|
|
|
function parseVersionParts(version: string) {
|
|
const normalized = version.trim().replace(/^v/i, '');
|
|
if (!normalized || normalized.toLowerCase() === 'unknown') {
|
|
return null;
|
|
}
|
|
|
|
return normalized.split('.').map((segment) => {
|
|
const matched = segment.trim().match(/^\d+/);
|
|
return matched ? Number.parseInt(matched[0], 10) : 0;
|
|
});
|
|
}
|
|
|
|
function isOlderVersion(current: string, target: string) {
|
|
const currentParts = parseVersionParts(current);
|
|
const targetParts = parseVersionParts(target);
|
|
if (!currentParts || !targetParts) {
|
|
return false;
|
|
}
|
|
|
|
const maxLength = Math.max(currentParts.length, targetParts.length);
|
|
for (let index = 0; index < maxLength; index += 1) {
|
|
const currentPart = currentParts[index] ?? 0;
|
|
const targetPart = targetParts[index] ?? 0;
|
|
if (currentPart < targetPart) {
|
|
return true;
|
|
}
|
|
if (currentPart > targetPart) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function shouldShowManualUpdate(
|
|
agentVersion: string,
|
|
serverVersion: string,
|
|
) {
|
|
const normalizedServerVersion = serverVersion.trim();
|
|
const normalizedAgentVersion = agentVersion.trim();
|
|
|
|
if (
|
|
!normalizedServerVersion ||
|
|
normalizedServerVersion.toLowerCase() === 'dev' ||
|
|
!normalizedAgentVersion ||
|
|
normalizedAgentVersion.toLowerCase() === 'unknown'
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return isOlderVersion(normalizedAgentVersion, normalizedServerVersion);
|
|
}
|
|
|
|
export function getServerUrl(value: string) {
|
|
return value.trim().replace(/\/+$/, '');
|
|
}
|
|
|
|
const installerScriptUrl =
|
|
'https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh';
|
|
|
|
export function buildNodeInstallCommand(serverUrl: string, agentToken: string) {
|
|
return [
|
|
`curl -fsSL ${installerScriptUrl} | bash -s -- \\`,
|
|
` --server-url ${serverUrl} \\`,
|
|
` --agent-token ${agentToken}`,
|
|
].join('\n');
|
|
}
|
|
|
|
export function buildNodeDockerInstallCommand(
|
|
serverUrl: string,
|
|
agentToken: string,
|
|
) {
|
|
const image = 'ghcr.io/rain-kl/openflare-agent:latest';
|
|
|
|
return [
|
|
`docker pull ${image}`,
|
|
`docker rm -f openflare-agent 2>/dev/null || true`,
|
|
`docker run -d --name openflare-agent --restart unless-stopped \\`,
|
|
` -p 80:80 -p 443:443 \\`,
|
|
` -e OPENFLARE_SERVER_URL=${serverUrl} \\`,
|
|
` -e OPENFLARE_AGENT_TOKEN=${agentToken} \\`,
|
|
` ${image}`,
|
|
].join('\n');
|
|
}
|
|
|
|
export function getRelayStatusVariant(status: string | null | undefined) {
|
|
if (status === 'healthy') {
|
|
return 'success';
|
|
}
|
|
if (status === 'unhealthy') {
|
|
return 'danger';
|
|
}
|
|
return 'warning';
|
|
}
|
|
|
|
export function getRelayStatusLabel(status: string | null | undefined) {
|
|
if (status === 'healthy') {
|
|
return '健康';
|
|
}
|
|
if (status === 'unhealthy') {
|
|
return '异常';
|
|
}
|
|
return '未知';
|
|
}
|
|
|
|
const relayInstallerScriptUrl =
|
|
'https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-relay.sh';
|
|
|
|
export function buildRelayInstallCommand(serverUrl: string, discoveryToken: string) {
|
|
return [
|
|
`curl -fsSL ${relayInstallerScriptUrl} | bash -s -- \\`,
|
|
` --server-url ${serverUrl} \\`,
|
|
` --discovery-token ${discoveryToken}`,
|
|
].join('\n');
|
|
}
|
|
|
|
export function buildRelayDockerInstallCommand(
|
|
serverUrl: string,
|
|
discoveryToken: string,
|
|
) {
|
|
const image = 'ghcr.io/rain-kl/openflare-relay:latest';
|
|
|
|
return [
|
|
`docker pull ${image}`,
|
|
`docker rm -f openflare-relay 2>/dev/null || true`,
|
|
`docker run -d --name openflare-relay --net host --restart unless-stopped \\`,
|
|
` -e OPENFLARE_SERVER_URL=${serverUrl} \\`,
|
|
` -e OPENFLARE_DISCOVERY_TOKEN=${discoveryToken} \\`,
|
|
` ${image}`,
|
|
].join('\n');
|
|
}
|
|
|
|
export function buildTunnelInstallCommand(serverUrl: string, tunnelToken: string) {
|
|
const flaredInstallerScriptUrl =
|
|
'https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-flared.sh';
|
|
return [
|
|
`curl -fsSL ${flaredInstallerScriptUrl} | bash -s -- \\`,
|
|
` --server-url ${serverUrl} \\`,
|
|
` --tunnel-token ${tunnelToken}`,
|
|
].join('\n');
|
|
}
|
|
|
|
export function buildTunnelDockerInstallCommand(
|
|
serverUrl: string,
|
|
tunnelToken: string,
|
|
) {
|
|
const image = 'ghcr.io/rain-kl/openflare-flared:latest';
|
|
|
|
return [
|
|
`docker pull ${image}`,
|
|
`docker rm -f openflare-flared 2>/dev/null || true`,
|
|
`docker run -d --name openflare-flared --restart unless-stopped \\`,
|
|
` -e OPENFLARE_SERVER_URL=${serverUrl} \\`,
|
|
` -e OPENFLARE_TUNNEL_TOKEN=${tunnelToken} \\`,
|
|
` ${image}`,
|
|
].join('\n');
|
|
}
|