@swimmingliu/autovpn 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/cli/commands/index.js +5 -1
- package/dist/cli/main.js +30 -0
- package/dist/cli/native-commands.js +1 -1
- package/dist/cli/output.js +1 -0
- package/dist/doctor/checks.js +47 -23
- package/dist/pipeline/deploy.js +15 -7
- package/dist/runtime/managed-tools.js +269 -0
- package/dist/server/http.js +195 -0
- package/dist/server/options.js +34 -0
- package/dist/server/runtime.js +151 -0
- package/dist/server/web-adapter.js +87 -0
- package/dist/web/renderer/app.js +1332 -0
- package/dist/web/renderer/assets/VPN-logo.png +0 -0
- package/dist/web/renderer/assets/vpn-auto-logo-v2-minimal.svg +41 -0
- package/dist/web/renderer/i18n.js +137 -0
- package/dist/web/renderer/index.html +48 -0
- package/dist/web/renderer/state.js +81 -0
- package/dist/web/renderer/styles.css +2010 -0
- package/dist/web/renderer/views.js +1258 -0
- package/package.json +4 -4
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="164" y1="128" x2="846" y2="896" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0" stop-color="#8789F7"/>
|
|
5
|
+
<stop offset="0.58" stop-color="#5B5CE2"/>
|
|
6
|
+
<stop offset="1" stop-color="#4B4DCC"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="line" x1="364" y1="614" x2="670" y2="420" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0" stop-color="#FFFFFF"/>
|
|
10
|
+
<stop offset="1" stop-color="#E8ECFF"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="shadow" x="52" y="60" width="920" height="920" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
14
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
15
|
+
<feOffset dy="28"/>
|
|
16
|
+
<feGaussianBlur stdDeviation="22"/>
|
|
17
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.1176 0 0 0 0 0.1529 0 0 0 0 0.2902 0 0 0 0.24 0"/>
|
|
18
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1"/>
|
|
19
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape"/>
|
|
20
|
+
</filter>
|
|
21
|
+
</defs>
|
|
22
|
+
|
|
23
|
+
<g filter="url(#shadow)">
|
|
24
|
+
<rect x="96" y="96" width="832" height="832" rx="220" fill="url(#bg)"/>
|
|
25
|
+
<rect x="144" y="144" width="736" height="736" rx="184" stroke="white" stroke-opacity="0.16" stroke-width="4"/>
|
|
26
|
+
<path d="M128 320C203 205 346 144 512 144C670 144 812 206 896 320V228C896 172 850 128 794 128H230C174 128 128 172 128 228V320Z" fill="white" fill-opacity="0.08"/>
|
|
27
|
+
</g>
|
|
28
|
+
|
|
29
|
+
<path d="M512 258C603 258 678 286 736 334V530C736 669 634 788 512 840C390 788 288 669 288 530V334C346 286 421 258 512 258Z" stroke="white" stroke-width="30" stroke-linejoin="round"/>
|
|
30
|
+
<path d="M374 602L448 528L526 580L650 454" stroke="url(#line)" stroke-width="34" stroke-linecap="round" stroke-linejoin="round"/>
|
|
31
|
+
<path d="M620 452H684V516" stroke="url(#line)" stroke-width="34" stroke-linecap="round" stroke-linejoin="round"/>
|
|
32
|
+
<circle cx="374" cy="602" r="36" fill="white"/>
|
|
33
|
+
<circle cx="448" cy="528" r="28" fill="#E5E9FF"/>
|
|
34
|
+
<circle cx="526" cy="580" r="28" fill="#E5E9FF"/>
|
|
35
|
+
<circle cx="650" cy="454" r="26" fill="white"/>
|
|
36
|
+
<path d="M402 420C432 393 469 380 512 380C560 380 600 398 636 432" stroke="white" stroke-opacity="0.86" stroke-width="18" stroke-linecap="round"/>
|
|
37
|
+
<circle cx="388" cy="434" r="11" fill="white"/>
|
|
38
|
+
<circle cx="650" cy="442" r="11" fill="white"/>
|
|
39
|
+
<path d="M446 670H578" stroke="white" stroke-opacity="0.22" stroke-width="18" stroke-linecap="round"/>
|
|
40
|
+
<path d="M470 710H554" stroke="white" stroke-opacity="0.16" stroke-width="16" stroke-linecap="round"/>
|
|
41
|
+
</svg>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export const SUPPORTED_LANGUAGES = ['zh-CN'];
|
|
2
|
+
export const LANGUAGE_STORAGE_KEY = 'vpn-automation-language';
|
|
3
|
+
|
|
4
|
+
const ZH_MESSAGES = {
|
|
5
|
+
locale: 'zh-CN',
|
|
6
|
+
appTitle: 'AutoVPN',
|
|
7
|
+
sidebarTitle: 'AutoVPN',
|
|
8
|
+
sidebarVersion: 'v.1.3.0',
|
|
9
|
+
brandSubtitle: '概览、运行、结果、订阅、日志、设置统一管理',
|
|
10
|
+
languageLabel: '',
|
|
11
|
+
saveButton: '保存配置',
|
|
12
|
+
runButton: '立即运行',
|
|
13
|
+
runButtonRunning: '运行中',
|
|
14
|
+
runButtonStopping: '停止中',
|
|
15
|
+
stopButton: '停止运行',
|
|
16
|
+
projectButton: '项目地址',
|
|
17
|
+
settingsButton: '设置',
|
|
18
|
+
shortcutActions: {
|
|
19
|
+
run: '开始运行',
|
|
20
|
+
settings: '设置统一管理',
|
|
21
|
+
results: '查看结果',
|
|
22
|
+
logs: '打开日志'
|
|
23
|
+
},
|
|
24
|
+
pageTitles: {
|
|
25
|
+
dashboard: '概览',
|
|
26
|
+
runs: '运行',
|
|
27
|
+
results: '结果',
|
|
28
|
+
subscriptions: '订阅',
|
|
29
|
+
logs: '日志',
|
|
30
|
+
settings: '设置'
|
|
31
|
+
},
|
|
32
|
+
pageSubtitles: {
|
|
33
|
+
dashboard: '只展示运行状态、系统状态摘要、核心指标和最近结果',
|
|
34
|
+
runs: '执行流水线、查看阶段进度和当前阶段详情',
|
|
35
|
+
results: '查看 pipeline 后最终留下的节点和区域统计',
|
|
36
|
+
subscriptions: '生成和分发 Clash、Clash Meta、Sing-box、Surge 订阅',
|
|
37
|
+
logs: '查看实时日志流、错误高亮和日志操作',
|
|
38
|
+
settings: '以分组卡片和弹窗方式管理数据源、测速与可达性配置'
|
|
39
|
+
},
|
|
40
|
+
nav: {
|
|
41
|
+
dashboard: '概览',
|
|
42
|
+
runs: '运行',
|
|
43
|
+
results: '结果',
|
|
44
|
+
subscriptions: '订阅',
|
|
45
|
+
logs: '日志',
|
|
46
|
+
settings: '设置'
|
|
47
|
+
},
|
|
48
|
+
currentTaskLabel: '当前任务',
|
|
49
|
+
runModeLabel: '运行模式',
|
|
50
|
+
logLinesLabel: '日志行数',
|
|
51
|
+
lastResultLabel: '最新结果',
|
|
52
|
+
lastUpdateLabel: '最后更新',
|
|
53
|
+
manualRunMode: '本地优先',
|
|
54
|
+
demoRunMode: '演示模式',
|
|
55
|
+
taskWaiting: '等待开始',
|
|
56
|
+
notAvailableValue: '—',
|
|
57
|
+
sidebarStatusTitle: '系统状态',
|
|
58
|
+
runStateLabels: {
|
|
59
|
+
idle: '待运行',
|
|
60
|
+
running: '运行中',
|
|
61
|
+
stopping: '停止中'
|
|
62
|
+
},
|
|
63
|
+
runResultLabels: {
|
|
64
|
+
idle: '未开始',
|
|
65
|
+
running: '执行中',
|
|
66
|
+
success: '已完成',
|
|
67
|
+
failed: '失败',
|
|
68
|
+
stopped: '已停止',
|
|
69
|
+
demo: '演示完成'
|
|
70
|
+
},
|
|
71
|
+
stageLabels: {
|
|
72
|
+
doctor: '环境检查',
|
|
73
|
+
extract: '提取节点',
|
|
74
|
+
dedupe: '节点去重',
|
|
75
|
+
speedtest: '节点测速',
|
|
76
|
+
availability: '站点验证',
|
|
77
|
+
postprocess: '节点处理',
|
|
78
|
+
render: '模板渲染',
|
|
79
|
+
obfuscate: '加密处理',
|
|
80
|
+
deploy: '打包部署',
|
|
81
|
+
verify: '结果校验'
|
|
82
|
+
},
|
|
83
|
+
statusLabels: {
|
|
84
|
+
pending: '待执行',
|
|
85
|
+
running: '运行中',
|
|
86
|
+
success: '成功',
|
|
87
|
+
failed: '失败'
|
|
88
|
+
},
|
|
89
|
+
emptyStates: {
|
|
90
|
+
noRunData: '暂无运行数据',
|
|
91
|
+
noArtifacts: '尚未生成订阅地址或产物文件',
|
|
92
|
+
noLogs: '尚未采集日志',
|
|
93
|
+
noSubscription: '尚未生成订阅地址'
|
|
94
|
+
},
|
|
95
|
+
readyValue: '待运行',
|
|
96
|
+
verifiedValue: '已验证',
|
|
97
|
+
idleValue: '空闲',
|
|
98
|
+
demoMode: '[演示] 当前以浏览器演示模式运行',
|
|
99
|
+
profileSaved: '[界面] 配置已保存',
|
|
100
|
+
profileReset: '[界面] 已恢复到上次保存的配置',
|
|
101
|
+
pipelineStarted: '[界面] 流水线已启动',
|
|
102
|
+
deployCredentialsMissing: '部署配置不完整:请填写 Cloudflare Token,或勾选“跳过部署”后仅执行本地生成。',
|
|
103
|
+
pipelineStopping: '[界面] 已请求停止当前流水线',
|
|
104
|
+
pipelineStopped: '[界面] 流水线已停止',
|
|
105
|
+
pipelineFinished: '[界面] 流水线结束,退出码:{code}',
|
|
106
|
+
pipelineFailed: '[界面] 流水线失败:{error}',
|
|
107
|
+
stopUnavailable: '[界面] 当前没有可停止的运行任务',
|
|
108
|
+
copiedToastMessage: '已复制到剪贴板',
|
|
109
|
+
copiedNodesToastMessage: '已复制 {count} 条节点',
|
|
110
|
+
copiedMessage: '[界面] 已复制:{value}',
|
|
111
|
+
copiedNodesLogMessage: '[界面] 已复制 {count} 条节点',
|
|
112
|
+
nothingToCopyMessage: '暂无可复制内容',
|
|
113
|
+
copyFailedToastMessage: '复制失败:{error}',
|
|
114
|
+
copyFailedLogMessage: '[界面] 复制失败:{error}',
|
|
115
|
+
openedPathMessage: '[界面] 已打开目录:{value}',
|
|
116
|
+
exportedLogsMessage: '[界面] 日志已导出:{value}',
|
|
117
|
+
exportLogsButton: '导出日志',
|
|
118
|
+
openArtifactsButton: '打开输出目录',
|
|
119
|
+
openLogsButton: '查看日志页',
|
|
120
|
+
resetButton: '恢复到已保存配置',
|
|
121
|
+
openFailed: '[界面] 打开失败:{error}'
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export function resolveLanguage() {
|
|
125
|
+
return 'zh-CN';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function getMessages() {
|
|
129
|
+
return ZH_MESSAGES;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function formatMessage(template, params = {}) {
|
|
133
|
+
return Object.entries(params).reduce(
|
|
134
|
+
(result, [key, value]) => result.replaceAll(`{${key}}`, String(value)),
|
|
135
|
+
template
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>AutoVPN</title>
|
|
7
|
+
<link rel="stylesheet" href="./styles.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body data-page="dashboard">
|
|
10
|
+
<div class="app-frame">
|
|
11
|
+
<div class="window-titlebar" aria-hidden="true"></div>
|
|
12
|
+
<div class="app-content-shell">
|
|
13
|
+
<div class="workspace-shell">
|
|
14
|
+
<aside class="sidebar">
|
|
15
|
+
<div class="sidebar-brand">
|
|
16
|
+
<img class="brand-mark" src="./assets/vpn-auto-logo-v2-minimal.svg" alt="" aria-hidden="true" />
|
|
17
|
+
<div class="brand-copy">
|
|
18
|
+
<div id="sidebarTitle" class="sidebar-title"></div>
|
|
19
|
+
<div id="sidebarVersion" class="sidebar-version"></div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<nav id="sidebarNav" class="sidebar-nav"></nav>
|
|
24
|
+
</aside>
|
|
25
|
+
|
|
26
|
+
<div class="main-shell">
|
|
27
|
+
<header class="topbar">
|
|
28
|
+
<div class="topbar-heading">
|
|
29
|
+
<div class="page-title-row">
|
|
30
|
+
<h1 id="pageTitle"></h1>
|
|
31
|
+
<span id="runStateBadge" class="badge neutral"></span>
|
|
32
|
+
</div>
|
|
33
|
+
<p id="pageSubtitle"></p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div id="pageActions" class="topbar-actions"></div>
|
|
37
|
+
</header>
|
|
38
|
+
|
|
39
|
+
<main id="pageContent" class="page-content"></main>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div id="toastRoot" class="toast-root" aria-live="polite" aria-atomic="true"></div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<script type="module" src="./app.js"></script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const STAGE_ORDER = [
|
|
2
|
+
'doctor',
|
|
3
|
+
'extract',
|
|
4
|
+
'dedupe',
|
|
5
|
+
'speedtest',
|
|
6
|
+
'availability',
|
|
7
|
+
'postprocess',
|
|
8
|
+
'render',
|
|
9
|
+
'obfuscate',
|
|
10
|
+
'deploy',
|
|
11
|
+
'verify'
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export const PAGE_ORDER = [
|
|
15
|
+
'dashboard',
|
|
16
|
+
'runs',
|
|
17
|
+
'results',
|
|
18
|
+
'subscriptions',
|
|
19
|
+
'logs',
|
|
20
|
+
'settings'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const PAGE_INDEX = Object.fromEntries(
|
|
24
|
+
PAGE_ORDER.map((name, index) => [name, String(index + 1).padStart(2, '0')])
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const METRIC_LABELS = {
|
|
28
|
+
raw_links: '原始节点',
|
|
29
|
+
postprocess_links: '去重节点',
|
|
30
|
+
deduped_links: '去重节点',
|
|
31
|
+
speedtest_links: '测速通过节点',
|
|
32
|
+
availability_links: '最终可用'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function buildStageModel(stageStatus = {}) {
|
|
36
|
+
return STAGE_ORDER.map((name) => ({
|
|
37
|
+
name,
|
|
38
|
+
status: stageStatus[name] ?? 'pending'
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveVerifyMetricValue(status = 'pending', messages) {
|
|
43
|
+
if (status === 'success') {
|
|
44
|
+
return messages.verifiedValue;
|
|
45
|
+
}
|
|
46
|
+
if (status === 'pending') {
|
|
47
|
+
return messages.readyValue;
|
|
48
|
+
}
|
|
49
|
+
return messages.statusLabels[status] ?? status;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveRunControlState(runState = 'idle') {
|
|
53
|
+
if (runState === 'running') {
|
|
54
|
+
return {
|
|
55
|
+
isBusy: true,
|
|
56
|
+
runDisabled: true,
|
|
57
|
+
stopDisabled: false
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (runState === 'stopping') {
|
|
62
|
+
return {
|
|
63
|
+
isBusy: true,
|
|
64
|
+
runDisabled: true,
|
|
65
|
+
stopDisabled: true
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
isBusy: false,
|
|
71
|
+
runDisabled: false,
|
|
72
|
+
stopDisabled: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function toMetricItems(counts = {}) {
|
|
77
|
+
return Object.entries(counts).map(([label, value]) => ({
|
|
78
|
+
label: METRIC_LABELS[label] ?? label,
|
|
79
|
+
value: String(value)
|
|
80
|
+
}));
|
|
81
|
+
}
|