### Install subs-check on Linux Source: https://github.com/beck-8/subs-check/blob/master/README.md Use these commands to install the application on Linux systems, including options for wget and proxy usage. ```bash # 默认安装 bash <(curl -fsSL https://raw.githubusercontent.com/beck-8/subs-check/master/install.sh) # 使用 wget bash <(wget -qO- https://raw.githubusercontent.com/beck-8/subs-check/master/install.sh) # 如果无法访问 GitHub,可使用代理 bash <(curl -fsSL https://ghfast.top/https://raw.githubusercontent.com/beck-8/subs-check/master/install.sh) https://ghfast.top/ # Alpine 等无 bash 环境 wget -qO /tmp/install.sh https://raw.githubusercontent.com/beck-8/subs-check/master/install.sh && sh /tmp/install.sh && rm -f /tmp/install.sh ``` -------------------------------- ### Install dependencies in Termux Source: https://github.com/beck-8/subs-check/blob/master/doc/android.md Installs Node.js and necessary system utilities required for the application. ```bash pkg update && pkg add nodejs ca-certificates which proot termux-exec -y ``` -------------------------------- ### Status Polling and Initialization Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Handles periodic status updates from the API and initial application setup. ```javascript function updateStatus() { fetch('/api/status', { headers: addApiKeyHeader() }) .then(response => { if (handleUnauthorized(response, false)) return; return response.json(); }) .then(data => { if (data) { if (data.checking) { dom.statusContainer.className = 'text-primary'; dom.statusIcon.className = 'bi bi-arrow-repeat me-1 rotate-animation'; dom.statusText.textContent = '正在检测中...'; if (data.pipeline) { updatePipelineBar(data.pipeline, !!data.hasSpeedTest); updatePipelineSteps(data.pipeline, !!data.hasSpeedTest); } else { // legacy fallback (older server) updateProgressBar(data.proxyCount, data.progress, data.available, data.phase, data.phaseResults); } } else { dom.statusContainer.className = 'text-success'; dom.statusIcon.className = 'bi bi-check-circle me-1'; dom.statusText.textContent = '空闲'; updatePhaseSteps(0, 0, 0, data.phaseResults); resetProgress(); } } }) .catch(error => { console.error('获取状态失败:', error); dom.statusContainer.className = 'text-danger'; dom.statusIcon.className = 'bi bi-exclamation-triangle me-1'; dom.statusText.textContent = '获取状态失败'; resetProgress(); }); } // 初始加载 updateStatus(); loadLogs(); // 检查API密钥 const apiKey = localStorage.getItem('apiKey'); if (!apiKey) { alert('请先输入API密钥以使用本系统'); dom.apiKey.focus(); } // 定时刷新 setInterval(loadLogs, 10000); setInterval(updateStatus, 3000); ``` -------------------------------- ### Initialize Linux environment and DNS Source: https://github.com/beck-8/subs-check/blob/master/doc/android.md Enters the proot environment and configures DNS settings if resolution issues occur. ```bash # 目的是为了让subs-check有个完整的Linux环境 termux-chroot # 如遇到DNS问题,请自行更改/etc/resolv.conf echo "nameserver 223.5.5.5" > /etc/resolv.conf ``` -------------------------------- ### Run from Source Source: https://github.com/beck-8/subs-check/blob/master/README.md Execute the application directly from source code using the Go toolchain. ```bash go run . -f ./config/config.yaml ``` -------------------------------- ### Run with Docker Source: https://github.com/beck-8/subs-check/blob/master/README.md Commands to deploy the application using Docker, with or without proxy settings. ```bash # 基础运行 docker run -d \ --name subs-check \ -p 8299:8299 \ -p 8199:8199 \ -v ./config:/app/config \ -v ./output:/app/output \ --restart always \ ghcr.io/beck-8/subs-check:latest # 使用代理运行 docker run -d \ --name subs-check \ -p 8299:8299 \ -p 8199:8199 \ -e HTTP_PROXY=http://192.168.1.1:7890 \ -e HTTPS_PROXY=http://192.168.1.1:7890 \ -v ./config:/app/config \ -v ./output:/app/output \ --restart always \ ghcr.io/beck-8/subs-check:latest ``` -------------------------------- ### Execute the program Source: https://github.com/beck-8/subs-check/blob/master/doc/android.md Runs the subs-check binary. ```bash ./subs-check ``` -------------------------------- ### Configure environment variables Source: https://github.com/beck-8/subs-check/blob/master/doc/android.md Sets SSL certificate paths and Node.js binary locations, either temporarily or persistently. ```bash # 临时设置环境变量 # 无Root权限的手机设置,有Root权限应该授权后无需设置 export SSL_CERT_FILE="/data/data/com.termux/files/usr/etc/tls/cert.pem" export NODEBIN_PATH="$(which node)" ``` ```bash # 设置持久环境变量,重新打开终端无需再次设置 echo 'export SSL_CERT_FILE="/data/data/com.termux/files/usr/etc/tls/cert.pem"' >> ~/.bashrc echo 'export NODEBIN_PATH="$(which node)"' >> ~/.bashrc source ~/.bashrc ``` -------------------------------- ### Admin Interface Initialization Script Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html JavaScript logic for initializing the Monaco editor, managing API keys via localStorage, and handling UI interactions. ```javascript var require = { paths: { 'vs': '/static/monaco/vs' } }; let editor; const PHASE_LABELS = { 1: { name: '测活', countLabel: '存活' }, 2: { name: '流媒体+重命名', countLabel: '完成' }, 3: { name: '测速', countLabel: '通过' } }; // 缓存高频访问的 DOM 元素 const dom = { apiKey: document.getElementById('apiKey'), toggleApiKey: document.getElementById('toggleApiKey'), saveApiKey: document.getElementById('saveApiKey'), logs: document.getElementById('logs'), phaseSteps: document.getElementById('phaseSteps'), progressText: document.getElementById('progressText'), progressPercent: document.getElementById('progressPercent'), processPercent: document.getElementById('processPercent'), progressBarTotal: document.getElementById('progressBarTotal'), progressBarSuccess: document.getElementById('progressBarSuccess'), successLabel: document.getElementById('successLabel'), successText: document.getElementById('successText'), statusContainer: document.getElementById('statusContainer'), statusIcon: document.getElementById('statusIcon'), statusText: document.getElementById('nextCheckTime'), versionInfo: document.getElementById('versionInfo'), }; // 初始化API密钥 const storedApiKey = localStorage.getItem('apiKey') || ''; dom.apiKey.value = storedApiKey; // 切换API密钥可见性 dom.toggleApiKey.addEventListener('click', function() { const icon = this.querySelector('i'); if (dom.apiKey.type === 'password') { dom.apiKey.type = 'text'; icon.className = 'bi bi-eye'; } else { dom.apiKey.type = 'password'; icon.className = 'bi bi-eye-slash'; } }); // 保存API密钥到本地存储 dom.saveApiKey.addEventListener('click', function() { const apiKey = dom.apiKey.value.trim(); localStorage.setItem('apiKey', apiKey); const button = this; const originalText = button.textContent; button.disabled = true; button.textContent = '验证中...'; fetch('/api/status', { headers: { 'X-API-Key': apiKey } }) .then(response => { if (response.status === 401) { alert('API密钥无效,请检查后重试'); return ``` -------------------------------- ### Configure Proxy Settings Source: https://github.com/beck-8/subs-check/blob/master/README.md Environment variables to route traffic through HTTP, SOCKS5, or SOCKS5H proxies. ```bash # HTTP 代理示例 export HTTP_PROXY=http://username:password@192.168.1.1:7890 export HTTPS_PROXY=http://username:password@192.168.1.1:7890 # SOCKS5 代理示例 export HTTP_PROXY=socks5://username:password@192.168.1.1:7890 export HTTPS_PROXY=socks5://username:password@192.168.1.1:7890 # SOCKS5H 代理示例 export HTTP_PROXY=socks5h://username:password@192.168.1.1:7890 export HTTPS_PROXY=socks5h://username:password@192.168.1.1:7890 ``` -------------------------------- ### Deploy Apprise using Docker Source: https://github.com/beck-8/subs-check/blob/master/README.md Commands to run the Apprise notification server in a Docker container, with options for basic deployment or using an HTTP proxy. ```bash # 基础运行 docker run --name apprise -p 8000:8000 --restart always -d caronc/apprise:latest # 使用代理运行 docker run --name apprise \ -p 8000:8000 \ -e HTTP_PROXY=http://192.168.1.1:7890 \ -e HTTPS_PROXY=http://192.168.1.1:7890 \ --restart always \ -d caronc/apprise:latest ``` -------------------------------- ### Monaco Editor Initialization Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Configures the Monaco editor with YAML syntax highlighting and custom UI overlay toggling. ```javascript // 初始化编辑器 require(['vs/editor/editor.main'], function() { monaco.languages.register({ id: 'yaml' }); monaco.languages.setMonarchTokensProvider('yaml', { tokenizer: { root: [ [/^\s*#.*$/, 'comment'], [/^\s*[\w\-]+:/, 'keyword'], [/^\s*-/, 'keyword'], [/"*?"/, 'string'], [/'*?'/, 'string'], ] } }); editor = monaco.editor.create(document.getElementById('editor'), { language: 'yaml', theme: 'vs', automaticLayout: true, minimap: { enabled: false }, fontSize: 13, lineHeight: 20, padding: { top: 10 }, scrollBeyondLastLine: false, renderLineHighlight: 'gutter', }); loadConfig(); const toggleConfigBtn = document.getElementById('toggleConfig'); const editorOverlay = document.getElementById('editorOverlay'); editorOverlay.style.display = 'flex'; toggleConfigBtn.addEventListener('click', function() { const isHidden = editorOverlay.style.display === 'flex'; editorOverlay.style.display = isHidden ? 'none' : 'flex'; const icon = toggleConfigBtn.querySelector('i'); icon.className = isHidden ? 'bi bi-eye' : 'bi bi-eye-slash'; }); editorOverlay.addEventListener('click', function() { editorOverlay.style.display = 'none'; const icon = toggleConfigBtn.querySelector('i'); icon.className = 'bi bi-eye'; }); }); ``` -------------------------------- ### Retrieve Version Information Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Fetches and displays the current system version from the API. ```javascript function getVersionInfo() { fetch('/api/version', { headers: addApiKeyHeader() }) .then(response => response.json()) .then(data => { if (data && data.version) { dom.versionInfo.textContent = '版本: ' + data.version; } }) .catch(error => { console.error('获取版本信息失败:', error); dom.versionInfo.textContent = '版本: 未知'; }); } getVersionInfo(); ``` -------------------------------- ### 配置自定义访问路径 Source: https://github.com/beck-8/subs-check/blob/master/doc/sub-store.md 通过修改配置文件中的 sub-store-path 来设置自定义访问路径,增强安全性。 ```bash # sub-store自定义访问路径,必须以/开头,后续访问订阅也要带上此路径 # 设置path之后,还可以开启订阅分享功能,无需暴露真实的path # sub-store-path: "/path" sub-store-path: "/diypath" ``` -------------------------------- ### Deploy with Docker Compose Source: https://github.com/beck-8/subs-check/blob/master/README.md Configuration file for deploying the application using Docker Compose. ```yaml version: "3" services: subs-check: image: ghcr.io/beck-8/subs-check:latest container_name: subs-check volumes: - ./config:/app/config - ./output:/app/output ports: - "8299:8299" - "8199:8199" environment: - TZ=Asia/Shanghai # - HTTP_PROXY=http://192.168.1.1:7890 # - HTTPS_PROXY=http://192.168.1.1:7890 # - API_KEY=subs-check restart: always network_mode: bridge ``` -------------------------------- ### Access Mihomo/Clash Subscription with Rules Source: https://github.com/beck-8/subs-check/blob/master/README.md Endpoint for retrieving Mihomo/Clash subscription files with default rule overrides applied. ```bash http://127.0.0.1:8299/api/file/mihomo ``` -------------------------------- ### Uninstall subs-check Source: https://github.com/beck-8/subs-check/blob/master/README.md Commands to stop, disable, and remove the application files and service configuration. ```bash systemctl stop subs-check systemctl disable subs-check rm -rf /opt/subs-check /etc/systemd/system/subs-check.service systemctl daemon-reload ``` -------------------------------- ### Access Subscription Links Source: https://github.com/beck-8/subs-check/blob/master/README.md List of URL endpoints for downloading subscription files in various formats supported by proxy clients. ```bash # 通用订阅 http://127.0.0.1:8299/download/sub # URI 订阅 http://127.0.0.1:8299/download/sub?target=URI # Mihomo/ClashMeta http://127.0.0.1:8299/download/sub?target=ClashMeta # Clash http://127.0.0.1:8299/download/sub?target=Clash # V2Ray http://127.0.0.1:8299/download/sub?target=V2Ray # ShadowRocket http://127.0.0.1:8299/download/sub?target=ShadowRocket # Quantumult http://127.0.0.1:8299/download/sub?target=QX # Sing-Box http://127.0.0.1:8299/download/sub?target=sing-box # Surge http://127.0.0.1:8299/download/sub?target=Surge # Surfboard http://127.0.0.1:8299/download/sub?target=Surfboard ``` -------------------------------- ### System Architecture Diagram Source: https://github.com/beck-8/subs-check/blob/master/README.md Visual representation of the data flow from subscription links through processing, storage, and final distribution. ```mermaid graph TD A[订阅链接] -->|获取订阅链接| B[subs-check] subgraph subs-check 处理流程 B -->|转成 YAML 格式| B1[节点去重] B1 -->|去除冗余节点| B2[测活] B2 -->|节点可用| B3[流媒体+重命名] B2 -->|节点不可用| X[丢弃] B3 -->|filter 通过| B4[测速] B3 -->|filter 不通过| X[丢弃] B4 -->|测速达标| B5[生成 all.yaml] B4 -->|测速不达标| X[丢弃] end B5 -->|保存到 output 目录| C[output 目录] B5 -->|上传 all.yaml| D[sub-store] C -->|保存到各位置| H1[R2/Gist/WebDAV/S3] H1 -->|存储完成| H2[发送消息通知] D -->|提供订阅转换服务| E[sub-store 转换服务] subgraph sub-store 独立功能 E -->|生成配置文件| E1[mihomo.yaml, base64.txt] E -->|其他格式转换| E2[Clash, V2Ray, ShadowRocket 等] E -->|订阅分享| E3[分享订阅链接] end E1 -->|保存到 output 目录| C C -->|文件服务| F[8199 端口: /sub] B -->|Web 管理| G[8199 端口: /admin] ``` -------------------------------- ### Fix Configuration File Permissions Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Use this command to resolve file permission issues when the application fails to load the configuration file. ```bash chmod 644 config/config.yaml ``` -------------------------------- ### Responsive Media Queries Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Adjusts layout behavior for desktop, tablet, and mobile screen widths. ```css @media (min-width: 992px) { .main-row .card { height: calc(100vh - 300px); min-height: 400px; } } @media (min-width: 1600px) { .main-row .card { height: calc(100vh - 350px); } } @media (max-width: 991px) { body { padding: 12px; } .main-row { display: block; } .main-row .card { height: auto; } .editor-wrapper { flex: none; height: calc(100vh - 285px); } .logs-container { flex: none; height: calc(100vh - 390px); } .card-header { padding: 0.5rem 0.75rem; } .card-body { padding: 0.75rem; } .card { margin-bottom: 15px; } } @media (max-width: 768px) { body { padding: 8px; } .editor-wrapper { flex: none; height: 420px; } .logs-container { flex: none; height: 350px; } .status-item { margin-bottom: 8px; } .config-path { max-width: 100%; white-space: normal; word-break: break-all; font-size: 0.85em; } .card { margin-bottom: 12px; } .progress-section { padding: 10px; } .phase-steps { flex-wrap: wrap; gap: 6px; } } ``` -------------------------------- ### Configuration and Status API Handlers Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Functions to load, save, and trigger system actions via API endpoints with authorization checks. ```javascript // 处理未授权响应 function handleUnauthorized(response, showAlert = true) { if (response.status === 401) { if (showAlert) { alert('API密钥无效或未提供,请检查您的API密钥'); } return true; } return false; } // 加载配置 function loadConfig() { return fetch('/api/config', { headers: addApiKeyHeader() }) .then(response => { if (handleUnauthorized(response, false)) return; if (!response.ok) throw new Error('加载配置失败'); return response.json(); }) .then(data => { if (editor && data) editor.setValue(data.content); return data; }) .catch(error => { console.error('加载配置失败:', error); }); } // 保存配置 document.getElementById('saveConfig').addEventListener('click', function() { const content = editor.getValue(); fetch('/api/config', { method: 'POST', headers: addApiKeyHeader({ 'Content-Type': 'application/json' }), body: JSON.stringify({ content }) }) .then(response => { if (handleUnauthorized(response, true)) throw new Error('未授权'); return response.json(); }) .then(data => { if (data.error) { alert('保存失败: ' + data.error); } else { alert(data.message); updateStatus(); } }) .catch(error => { if (error.message !== '未授权') alert('保存失败: ' + error.message); }); }); // 重新加载配置 document.getElementById('reloadConfig').addEventListener('click', function() { const button = this; const icon = button.querySelector('i'); icon.classList.add('rotate-animation'); button.disabled = true; loadConfig().finally(() => { setTimeout(() => { icon.classList.remove('rotate-animation'); button.disabled = false; }, 500); }); }); // 强制关闭 document.getElementById('forceClose').addEventListener('click', function() { if (!confirm('确定要强制停止当前阶段吗?已有结果将被保留。')) return; const button = this; const icon = button.querySelector('i'); const originalIcon = icon.className; icon.className = 'bi bi-arrow-repeat me-1 rotate-animation'; button.disabled = true; fetch('/api/force-close', { method: 'POST', headers: addApiKeyHeader() }) .then(response => { if (handleUnauthorized(response, true)) throw new Error('未授权'); return response.json(); }) .catch(error => { if (error.message !== '未授权') { console.error('强制停止失败:', error); alert('强制停止失败: ' + error.message); } }) .finally(() => { setTimeout(() => { icon.className = originalIcon; button.disabled = false; }, 500); }); }); // 立即检测 document.getElementById('triggerCheck').addEventListener('click', function() { const button = this; const icon = button.querySelector('i'); const originalIcon = icon.className; icon.className = 'bi bi-arrow-repeat me-1 rotate-animation'; button.disabled = true; fetch('/api/trigger-check', { method: 'POST', headers: addApiKeyHeader() }) .then(response => { if (handleUnauthorized(response, true)) throw new Error('未授权'); return response.json(); }) .then(data => { loadLogs(); }) .catch(error => { if (error.message !== '未授权') console.error('触发检测失败:', error); }) .finally(() => { setTimeout(() => { icon.className = originalIcon; button.disabled = false; }, 500); }); }); // 加载日志 function loadLogs() { return fetch('/api/logs', { headers: addApiKeyHeader() }) .then(response => { if (handleUnauthorized(response, false)) throw new Error('未授权'); return response.json(); }) .then(data => { if (data.error) { dom.logs.textContent = '加载日志失败: ' + data.error; } else { const isScrolledToBottom = ``` -------------------------------- ### Increase Inotify Watch Limit Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Run this command on Linux systems to resolve configuration watcher initialization failures caused by exceeding the inotify limit. ```bash sysctl fs.inotify.max_user_watches=524288 ``` -------------------------------- ### 配置 GitHub API 镜像地址 Source: https://github.com/beck-8/subs-check/blob/master/doc/gist.md 在配置文件中设置 GitHub API 的反代地址。 ```yaml github-api-mirror: "https://your-worker-url/github" ``` -------------------------------- ### Configure Notification Settings Source: https://github.com/beck-8/subs-check/blob/master/README.md YAML configuration snippet for setting the Apprise API server address, recipient URLs, and custom notification titles. ```yaml # 填写搭建的apprise API server 地址 # https://notify.xxxx.us.kg/notify apprise-api-server: "https://diydomain.com/notify" # 填写通知目标 # 支持100+ 个通知渠道,详细格式请参照 https://github.com/caronc/apprise recipient-url: # telegram格式:tgram://{bot_token}/{chat_id} # - tgram://xxxxxx/-1002149239223 # 钉钉格式:dingtalk://{Secret}@{ApiKey} # - dingtalk://xxxxxx@xxxxxxx # 自定义通知标题 notify-title: "🔔 节点状态更新" ``` -------------------------------- ### 获取不同格式的订阅链接 Source: https://github.com/beck-8/subs-check/blob/master/doc/gist.md 根据存储在 Gist 中的文件类型获取对应的订阅地址。 ```text https://gist.githubusercontent.com/YOUR_GITHUB_USERNAME/YOUR_GIST_ID/raw/all.yaml ``` ```text https://gist.githubusercontent.com/YOUR_GITHUB_USERNAME/YOUR_GIST_ID/raw/base64.txt ``` ```text https://gist.githubusercontent.com/YOUR_GITHUB_USERNAME/YOUR_GIST_ID/raw/mihomo.yaml ``` -------------------------------- ### Component and Utility Styles Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Styles for cards, buttons, alerts, and log-specific color coding. ```css .card { margin-bottom: 16px; border: 1px solid var(--border-color); border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden; } .card-header { background-color: var(--bg-card); border-bottom: 1px solid var(--border-color); font-weight: 600; font-size: 0.9rem; } .log-info { color: #a6e3a1; } .log-error { color: #f38ba8; } .log-warn { color: #f9e2af; } .log-debug { color: #89b4fa; } .log-time { color: #7f849c; } .status-bar { background-color: var(--bg-card); padding: 12px 16px; margin-bottom: 16px; border-radius: var(--radius); border: 1px solid var(--border-color); box-shadow: var(--shadow-sm); } .config-path { white-space: normal; word-break: break-all; color: var(--text-secondary); font-size: 0.85em; } .api-key-form { margin-bottom: 16px; } .btn { padding: 6px 14px; font-size: 0.85rem; border-radius: 8px; font-weight: 500; transition: all 0.2s ease; } .btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); } .btn:active { transform: translateY(0); } .btn-sm { padding: 4px 10px; font-size: 0.78rem; } .btn-primary { background-color: var(--accent-blue); border-color: var(--accent-blue); } .btn-danger { background-color: var(--accent-red); border-color: var(--accent-red); } .btn-success { background-color: var(--accent-green); border-color: var(--accent-green); } .alert { padding: 0.75rem 1rem; margin-bottom: 16px; border: none; border-radius: var(--radius); box-shadow: var(--shadow-sm); } .alert-info { background-color: #eef2ff; color: #3730a3; } .alert-info a { color: #3730a3; text-decoration: underline; font-weight: 500; } .form-control { border-radius: 8px; border: 1px solid var(--border-color); transition: border-color 0.2s ease, box-shadow 0.2s ease; } .form-control:focus { border-color: var(--accent-blue); box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.15); } h1 { color: var(--text-primary); font-size: 1.6rem; font-weight: 700; margin-bottom: 0.5rem; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .rotate-animation { animation: ``` -------------------------------- ### Configure GitHub Proxy Source: https://github.com/beck-8/subs-check/blob/master/README.md Set the proxy URL for GitHub requests in the configuration file. ```yaml # Github Proxy,获取订阅使用,结尾要带的 / # github-proxy: "https://ghfast.top/" github-proxy: "https://custom-domain/raw/" ``` -------------------------------- ### Configure WebDAV Storage Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Required configuration fields for WebDAV storage method. ```yaml save-method: webdav webdav-url: "https://..." webdav-username: "..." webdav-password: "..." ``` -------------------------------- ### Configure GitHub Gist Storage Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Required configuration fields for GitHub Gist storage method. ```yaml save-method: gist github-token: "ghp_..." github-gist-id: "..." ``` -------------------------------- ### Manage subs-check Service Source: https://github.com/beck-8/subs-check/blob/master/README.md Standard systemd commands to control the subs-check service. ```bash systemctl start subs-check # 启动 systemctl stop subs-check # 停止 systemctl restart subs-check # 重启 systemctl status subs-check # 状态 journalctl -u subs-check -f # 日志 ``` -------------------------------- ### Configure R2 Storage Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Required configuration fields for R2 storage method. ```yaml save-method: r2 worker-url: "https://..." worker-token: "..." ``` -------------------------------- ### Identify Process Using Port Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Use this command to find and terminate processes that are blocking the required HTTP server port. ```bash lsof -i :8199 ``` -------------------------------- ### Configure HTTP Server Port Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Update the configuration to use a different port if the default port is already in use. ```yaml listen-port: ":8200" ``` -------------------------------- ### Main Layout and Flexbox Containers Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Layout rules for the main content area, ensuring cards and editors maintain consistent heights. ```css .main-row { display: flex; flex-wrap: wrap; } .main-row > [class*="col-"] { display: flex; } .main-row .card { display: flex; flex-direction: column; width: 100%; } .main-row .card > .card-body { flex: 1 1 auto; overflow: hidden; display: flex; flex-direction: column; min-height: 0; } .editor-wrapper { position: relative; flex: 1 1 auto; min-height: 0; } #editor { position: absolute; inset: 0; border: none; margin-bottom: 0; } ``` -------------------------------- ### Configure S3 Storage Source: https://github.com/beck-8/subs-check/blob/master/_autodocs/errors.md Required configuration fields for S3 storage method. ```yaml save-method: s3 s3-endpoint: "..." s3-access-id: "..." s3-secret-key: "..." s3-bucket: "..." ``` -------------------------------- ### Phase and Progress UI Updates Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Manages the visual state of phase indicators and progress bars based on processing status. ```javascript // 缓存阶段步骤 DOM 元素 const phaseStepEls = [null, document.getElementById('phaseStep1'), document.getElementById('phaseStep2'), document.getElementById('phaseStep3')]; const phaseCountEls = [null, document.getElementById('phaseCount1'), document.getElementById('phaseCount2'), document.getElementById('phaseCount3')]; // 更新阶段步骤指示器 function updatePhaseSteps(phase, available, total, phaseResults) { // 检查是否有任何阶段结果 const hasResults = phaseResults && (phaseResults['1'] || phaseResults['2'] || phaseResults['3']); if (phase === 0 && !hasResults) { dom.phaseSteps.style.display = 'none'; return; } dom.phaseSteps.style.display = 'flex'; for (let i = 1; i <= 3; i++) { const step = phaseStepEls[i]; const count = phaseCountEls[i]; const label = PHASE_LABELS[i] ? PHASE_LABELS[i].countLabel : ''; const result = phaseResults ? phaseResults[String(i)] : null; step.classList.remove('active', 'done'); if (i < phase) { // 已完成的阶段:用后端保存的结果 step.classList.add('done'); if (result) { count.textContent = label + ': ' + result.available + '/' + result.total; } } else if (i === phase) { // 当前阶段:用实时数据 step.classList.add('active'); count.textContent = label + ': ' + available + '/' + total; } else if (phase === 0 && result) { // 空闲时:显示上次结果 step.classList.add('done'); count.textContent = label + ': ' + result.available + '/' + result.total; } else { count.textContent = ''; } } } // 更新进度条 function updateProgressBar(total, processed, available, phase, phaseResults) { const percentTotal = total > 0 ? (processed / total * 100).toFixed(1) : 0; dom.progressText.textContent = processed + '/' + total; dom.progressPercent.textContent = percentTotal + '%'; dom.progressBarTotal.style.width = percentTotal + '%'; const percentSuccess = total > 0 ? (available / total * 100).toFixed(1) : 0; dom.processPercent.textContent = percentSuccess + '%'; // 显示阶段对应的标签 const label = (phase && PHASE_LABELS[phase]) ? PHASE_LABELS[phase].countLabel : '成功节点'; if (dom.successLabel && dom.successText) { dom.successLabel.childNodes[0].textContent = label + ': '; if (available > 0) { dom.successText.innerHTML = '' + available + '/' + total; } else { dom.successText.textContent = available + '/' + total; } } dom.progressBarSuccess.style.width = percentSuccess + '%'; // 更新阶段指示器 updatePhaseSteps(phase, available, total, phaseResults); } // 重置进度条到初始状态 function resetProgress() { dom.progressText.textContent = 'N/A'; dom.progressPercent.textContent = 'N/A'; dom.processPercent.textContent = 'N/A'; dom.progressBarTotal.style.width = '0%'; dom.progressBarSuccess.style.width = '0%'; if (dom.successLabel) { dom.successLabel.childNodes[0].textContent = '成功节点: '; dom.successText.textContent = 'N/A'; } } ``` -------------------------------- ### Log Processing and Display Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Handles log retrieval, HTML escaping, and color-coding based on log levels. ```javascript dom.logs.scrollHeight - dom.logs.clientHeight <= dom.logs.scrollTop + 1; const fragment = document.createDocumentFragment(); data.logs.forEach(log => { const logLine = document.createElement('div'); logLine.innerHTML = colorizeLog(log); fragment.appendChild(logLine); }); dom.logs.innerHTML = ''; dom.logs.appendChild(fragment); if (isScrolledToBottom) { dom.logs.scrollTop = dom.logs.scrollHeight; } } return data; }) .catch(error => { if (error.message !== '未授权') { dom.logs.textContent = '加载日志失败: ' + error.message; } }); } function escapeHtml(str) { return str.replace(/&/g,'&').replace(//g,'>'); } // 为日志添加颜色 const LOG_LEVEL_CLASS = { INF: 'log-info', ERR: 'log-error', WRN: 'log-warn', DBG: 'log-debug' }; function colorizeLog(log) { log = escapeHtml(log); log = log.replace(/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/, '$1'); log = log.replace(/\b(INF|ERR|WRN|DBG)\b/, (m) => '' + m + ''); return log; } // 刷新日志 document.getElementById('refreshLogs').addEventListener('click', function() { const button = this; const icon = button.querySelector('i'); icon.classList.add('rotate-animation'); button.disabled = true; loadLogs().finally(() => { setTimeout(() => { icon.classList.remove('rotate-animation'); button.disabled = false; }, 500); }); }); ``` -------------------------------- ### Set Custom Speed Test URL Source: https://github.com/beck-8/subs-check/blob/master/README.md Define the speed test endpoint in the configuration file. ```yaml # 100MB speed-test-url: https://custom-domain/speedtest?bytes=104857600 # 1GB speed-test-url: https://custom-domain/speedtest?bytes=1073741824 ``` -------------------------------- ### CSS Variables and Base Styles Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Defines the color palette, spacing, and typography used throughout the admin interface. ```css :root { --bg-primary: #f0f2f5; --bg-card: #ffffff; --border-color: #e8ecf1; --text-primary: #1a1a2e; --text-secondary: #6c757d; --accent-blue: #4361ee; --accent-green: #2ec4b6; --accent-red: #e63946; --accent-yellow: #f4a261; --accent-cyan: #4cc9f0; --shadow-sm: 0 1px 3px rgba(0,0,0,0.08); --shadow-md: 0 4px 12px rgba(0,0,0,0.1); --radius: 10px; } body { padding: 20px; background-color: var(--bg-primary); color: var(--text-primary); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } ``` -------------------------------- ### Admin Interface CSS Styles Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html CSS definitions for UI components including editor overlays, phase indicators, progress bars, and scrollbar customization. ```css spin 1s linear infinite; display: inline-block; } /* 配置编辑器蒙版 */ .editor-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(240, 242, 245, 0.92); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); z-index: 10; display: flex; justify-content: center; align-items: center; cursor: pointer; } .editor-overlay-message { background-color: var(--bg-card); color: var(--text-secondary); padding: 16px 24px; border-radius: var(--radius); box-shadow: var(--shadow-md); text-align: center; } /* 阶段步骤指示器 */ .phase-steps { display: flex; gap: 8px; margin-bottom: 10px; } .phase-step { flex: 1; text-align: center; padding: 6px 8px; border-radius: 8px; font-size: 0.78rem; font-weight: 600; background-color: #f0f2f5; color: var(--text-secondary); border: 1px solid var(--border-color); transition: all 0.3s ease; } .phase-step.active { background-color: var(--accent-blue); color: white; border-color: var(--accent-blue); box-shadow: 0 2px 8px rgba(67, 97, 238, 0.3); } .phase-step.done { background-color: #d1fae5; color: #065f46; border-color: #a7f3d0; } .phase-step .phase-icon { margin-right: 4px; } .phase-step .phase-count { display: block; font-size: 0.7rem; font-weight: 400; opacity: 0.85; margin-top: 2px; } /* 进度条样式 */ .progress-section { padding: 14px; background-color: var(--bg-card); border-top: 1px solid var(--border-color); } .progress-info { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 13px; color: var(--text-secondary); } .progress { height: 12px; position: relative; background-color: #e9ecef; border-radius: 6px; overflow: hidden; } .progress-bar { transition: width 0.5s ease; border-radius: 6px; } .progress-bar-success { background: linear-gradient(90deg, #2ec4b6, #28a745); position: absolute; height: 100%; left: 0; top: 0; z-index: 10; box-shadow: 0 0 10px rgba(46, 196, 182, 0.4); } .progress-bar-total { background-color: rgba(67, 97, 238, 0.25); position: absolute; height: 100%; left: 0; top: 0; z-index: 5; } /* 闪烁效果 */ @keyframes pulse { 0% { opacity: 0.7; } 50% { opacity: 1; } 100% { opacity: 0.7; } } .success-highlight { animation: pulse 1.5s infinite; font-weight: bold; color: var(--accent-green); padding: 1px 4px; border-radius: 4px; background-color: rgba(46, 196, 182, 0.1); } /* 版本标签 */ .version-badge { display: inline-block; padding: 2px 10px; border-radius: 20px; background-color: #eef2ff; color: var(--accent-blue); font-size: 0.78rem; font-weight: 500; } /* 日志容器滚动条 */ .logs-container::-webkit-scrollbar { width: 6px; } .logs-container::-webkit-scrollbar-track { background: transparent; } .logs-container::-webkit-scrollbar-thumb { background: #45475a; border-radius: 3px; } .logs-container::-webkit-scrollbar-thumb:hover { background: #585b70; } ``` -------------------------------- ### Log Container Styling Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Specific styling for the log output area, including font and color schemes. ```css .logs-container { flex: 1 1 auto; min-height: 0; overflow-y: auto; background-color: #1e1e2e; padding: 14px; font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; font-size: 12px; white-space: pre-wrap; border: none; margin-bottom: 0; color: #cdd6f4; border-radius: 0; } ``` -------------------------------- ### 获取订阅文件 URL Source: https://github.com/beck-8/subs-check/blob/master/doc/r2.md 通过 Worker URL 获取存储在 R2 中的订阅文件,需提供文件名和访问令牌。 ```text https://your-worker-url/storage?filename=all.yaml&token=AUTH_TOKEN ``` ```text https://your-worker-url/storage?filename=base64.txt&token=AUTH_TOKEN ``` ```text https://your-worker-url/storage?filename=mihomo.yaml&token=AUTH_TOKEN ``` -------------------------------- ### Update Pipeline UI Components Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Functions to update the visual pipeline steps and progress bars based on current processing data. ```javascript function updatePipelineSteps(p, hasSpeed) { dom.phaseSteps.style.display = 'flex'; const rows = [ { idx: 1, label: '存活', pass: p.alivePass, total: p.total, active: true }, { idx: 2, label: '通过', pass: p.filterPass, total: p.alivePass, active: true }, { idx: 3, label: '通过', pass: p.speedPass, total: p.filterPass, active: hasSpeed }, ]; for (const r of rows) { const step = phaseStepEls[r.idx]; const count = phaseCountEls[r.idx]; step.classList.remove('active', 'done'); if (r.active) { step.classList.add('active'); count.textContent = r.label + ': ' + r.pass + '/' + r.total; } else { count.textContent = ''; } } } function updatePipelineBar(p, hasSpeed) { const total = p.total; const processed = p.aliveDone; // how far through input const success = hasSpeed ? p.speedPass : p.filterPass; // end-to-end pass count const pctProcessed = total > 0 ? (processed / total * 100).toFixed(1) : 0; dom.progressText.textContent = processed + '/' + total; dom.progressPercent.textContent = pctProcessed + '%'; dom.progressBarTotal.style.width = pctProcessed + '%'; const pctSuccess = total > 0 ? (success / total * 100).toFixed(1) : 0; dom.processPercent.textContent = pctSuccess + '%'; dom.progressBarSuccess.style.width = pctSuccess + '%'; if (dom.successLabel && dom.successText) { dom.successLabel.childNodes[0].textContent = '通过: '; dom.successText.innerHTML = success > 0 ? '' + success + '/' + total : success + '/' + total; } } ``` -------------------------------- ### API Key Management and Request Headers Source: https://github.com/beck-8/subs-check/blob/master/app/templates/admin.html Utility functions to retrieve the stored API key and inject it into request headers for authenticated API calls. ```javascript // 获取API密钥 function getApiKey() { return localStorage.getItem('apiKey') || ''; } // 添加API密钥到请求头 function addApiKeyHeader(headers = {}) { const apiKey = getApiKey(); return { ...headers, 'X-API-Key': apiKey }; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.