diff --git a/DreamLife_easytier/app.js b/DreamLife_easytier/app.js
new file mode 100644
index 0000000..f6689f9
--- /dev/null
+++ b/DreamLife_easytier/app.js
@@ -0,0 +1,264 @@
+class SystemMonitor {
+ constructor() {
+ this.cpuValueEl = document.getElementById('cpu-value');
+ this.cpuBarEl = document.getElementById('cpu-bar');
+ this.memoryValueEl = document.getElementById('memory-value');
+ this.memoryBarEl = document.getElementById('memory-bar');
+ this.serverStatusEl = document.getElementById('server-status');
+ this.lastUpdateEl = document.getElementById('last-update');
+
+ this.updateInterval = 1000;
+ this.isOnline = false;
+
+ this.cpuApiUrl = 'https://dreamlife.indevs.in:61207/api/4/cpu';
+ this.memoryApiUrl = 'https://dreamlife.indevs.in:61207/api/4/mem';
+
+ this.init();
+ }
+
+ init() {
+ this.fetchSystemInfo();
+ setInterval(() => this.fetchSystemInfo(), this.updateInterval);
+ }
+
+ async fetchSystemInfo() {
+ try {
+ const [cpuResponse, memoryResponse] = await Promise.all([
+ fetch(this.cpuApiUrl, {
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json'
+ },
+ signal: AbortSignal.timeout(5000)
+ }),
+
+ fetch(this.memoryApiUrl, {
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json'
+ },
+ signal: AbortSignal.timeout(5000)
+ })
+ ]);
+
+ if (!cpuResponse.ok || !memoryResponse.ok) {
+ throw new Error('API 请求失败');
+ }
+
+ const cpuData = await cpuResponse.json();
+ const memoryData = await memoryResponse.json();
+
+ this.updateUI(cpuData, memoryData);
+ this.setOnlineStatus(true);
+
+ } catch (error) {
+ console.error('获取系统信息失败:', error);
+ this.setOnlineStatus(false);
+ this.showMockData();
+ }
+ }
+
+ updateUI(cpuData, memoryData) {
+
+ // Glances CPU API:
+ // { "total": 12.3, ... }
+
+ const cpuPercent = Number(cpuData.total || 0).toFixed(2);
+
+ // Glances MEM API:
+ // { "percent": 45.6, ... }
+
+ const memoryPercent = Number(memoryData.percent || 0).toFixed(2);
+
+ this.cpuValueEl.textContent = `${cpuPercent}%`;
+ this.cpuBarEl.style.width = `${Math.round(cpuPercent)}%`;
+
+ this.memoryValueEl.textContent = `${memoryPercent}%`;
+ this.memoryBarEl.style.width = `${Math.round(memoryPercent)}%`;
+
+ const now = new Date();
+
+ const timeString = now.toLocaleString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ timeZoneName: 'short'
+ });
+
+ this.lastUpdateEl.textContent = timeString;
+ }
+
+ setOnlineStatus(online) {
+ if (this.isOnline !== online) {
+ this.isOnline = online;
+
+ if (online) {
+ this.serverStatusEl.textContent = '在线';
+ this.serverStatusEl.className = 'info-value status-online';
+ } else {
+ this.serverStatusEl.textContent = '离线';
+ this.serverStatusEl.className = 'info-value status-offline';
+ }
+ }
+ }
+
+ showMockData() {
+ const mockCpu = Math.floor(Math.random() * 40) + 20;
+ const mockMemory = Math.floor(Math.random() * 30) + 40;
+
+ this.cpuValueEl.textContent = `${mockCpu}%`;
+ this.cpuBarEl.style.width = `${mockCpu}%`;
+
+ this.memoryValueEl.textContent = `${mockMemory}%`;
+ this.memoryBarEl.style.width = `${mockMemory}%`;
+
+ const now = new Date();
+
+ this.lastUpdateEl.textContent = now.toLocaleString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ timeZoneName: 'short'
+ });
+ }
+}
+
+let isProtocolMenuOpen = false;
+
+function toggleProtocolMenu() {
+ const selector = document.getElementById('protocol-selector');
+ isProtocolMenuOpen = !isProtocolMenuOpen;
+
+ if (isProtocolMenuOpen) {
+ selector.classList.add('open');
+ } else {
+ selector.classList.remove('open');
+ }
+}
+
+function selectProtocol(protocol) {
+ const ports = {
+ 'tcp': 11010,
+ 'udp': 11010,
+ 'wg': 11011,
+ 'ws':11011,
+ 'wss':11012,
+ 'quic':11012,
+ 'faketcp':11013,
+ 'http':80,
+ 'https':443,
+ 'txt':null,
+ 'srv':null
+ };
+
+ const protocolNames = {
+ 'tcp': 'TCP',
+ 'udp': 'UDP',
+ 'wg': 'WG',
+ 'ws': 'WS',
+ 'wss': 'WSS',
+ 'quic': 'QUIC',
+ 'faketcp': 'FakeTCP',
+ 'http': 'HTTP',
+ 'https': 'HTTPS',
+ 'txt': 'TXT',
+ 'srv': 'SRV'
+ };
+
+ document.getElementById('current-protocol').textContent = protocolNames[protocol];
+
+ const addressCode = document.getElementById('node-address');
+ if (ports[protocol] === null) {
+ addressCode.firstChild.textContent = `${protocol}://dreamlife.indevs.in\n`;
+ } else {
+ addressCode.firstChild.textContent = `${protocol}://dreamlife.indevs.in:${ports[protocol]}\n`;
+ }
+
+ document.querySelectorAll('.protocol-option').forEach(opt => {
+ opt.classList.remove('active');
+ });
+ document.querySelector(`.protocol-option[data-protocol="${protocol}"]`).classList.add('active');
+
+ toggleProtocolMenu();
+}
+
+function copyAddress() {
+ const address = document.getElementById('node-address').textContent;
+ navigator.clipboard.writeText(address).then(() => {
+ const copyIcon = document.getElementById('copy-icon');
+ const checkIcon = document.getElementById('check-icon');
+
+ copyIcon.style.display = 'none';
+ checkIcon.style.display = 'block';
+
+ setTimeout(() => {
+ copyIcon.style.display = 'block';
+ checkIcon.style.display = 'none';
+ }, 2000);
+ }).catch(err => {
+ console.error('复制失败:', err);
+ alert('复制失败,请手动复制');
+ });
+}
+
+document.addEventListener('click', function(event) {
+ const selector = document.getElementById('protocol-selector');
+ if (!selector.contains(event.target) && isProtocolMenuOpen) {
+ selector.classList.remove('open');
+ isProtocolMenuOpen = false;
+ }
+});
+
+async function testLatency() {
+ const latencyText = document.querySelector('.latency-text');
+ const testBtn = document.getElementById('latency-test-btn');
+
+ testBtn.disabled = true;
+ testBtn.textContent = '测试中...';
+ latencyText.textContent = '--';
+ latencyText.className = 'latency-text';
+
+ try {
+ const startTime = performance.now();
+
+ await fetch('https://dreamlife.indevs.in:61207/api/4/cpu', {
+ method: 'GET',
+ signal: AbortSignal.timeout(5000)
+ });
+
+ const endTime = performance.now();
+ const latency = Math.round(endTime - startTime);
+
+ latencyText.textContent = `${latency}ms`;
+
+ if (latency < 100) {
+ latencyText.classList.add('latency-good');
+ } else if (latency < 300) {
+ latencyText.classList.add('latency-medium');
+ } else {
+ latencyText.classList.add('latency-bad');
+ }
+
+ testBtn.textContent = '重测';
+ } catch (error) {
+ console.error('延迟测试失败:', error);
+ latencyText.textContent = '超时';
+ latencyText.classList.add('latency-bad');
+ testBtn.textContent = '重测';
+ } finally {
+ testBtn.disabled = false;
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ new SystemMonitor();
+ setTimeout(() => {
+ testLatency();
+ }, 500);
+});
\ No newline at end of file
diff --git a/DreamLife_easytier/dl.png b/DreamLife_easytier/dl.png
new file mode 100644
index 0000000..6cb2d08
Binary files /dev/null and b/DreamLife_easytier/dl.png differ
diff --git a/DreamLife_easytier/dreamlife.png b/DreamLife_easytier/dreamlife.png
new file mode 100644
index 0000000..1101431
Binary files /dev/null and b/DreamLife_easytier/dreamlife.png differ
diff --git a/DreamLife_easytier/index.html b/DreamLife_easytier/index.html
new file mode 100644
index 0000000..725d93c
--- /dev/null
+++ b/DreamLife_easytier/index.html
@@ -0,0 +1,133 @@
+
+
+
+
+
+ DreamLife - EasyTier公共节点
+
+
+
+
+
+
+
+
+
+ 连接信息
+
+
使用 EasyTier 客户端初始节点配置:
+
+
+ 节点地址:
+
+
+
+
tcp://dreamlife.indevs.in:11010
+
+
+
+
+
+
+
+
+ 公共服务器状态
+
+
+
+
+ 节点状态:
+ 在线
+
+
+ 最后更新:
+ --
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DreamLife_easytier/styles.css b/DreamLife_easytier/styles.css
new file mode 100644
index 0000000..45d3e16
--- /dev/null
+++ b/DreamLife_easytier/styles.css
@@ -0,0 +1,480 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-color: #3b82f6;
+ --bg-color: #f8fafc;
+ --card-bg: #ffffff;
+ --text-primary: #1e293b;
+ --text-secondary: #64748b;
+ --border-color: #e2e8f0;
+ --success-color: #10b981;
+ --hover-color: #2563eb;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ background-color: var(--bg-color);
+ color: var(--text-primary);
+ line-height: 1.6;
+}
+
+.container {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 40px 20px;
+}
+
+.header {
+ text-align: center;
+ margin-bottom: 60px;
+}
+
+.logo-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
+ margin-bottom: 16px;
+}
+
+.icon-logo {
+ width: 100px;
+ height: auto;
+}
+
+.text-logo {
+ height: 60px;
+ width: auto;
+}
+
+.subtitle {
+ font-size: 18px;
+ color: var(--text-secondary);
+ font-weight: 300;
+}
+
+.main-content {
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+}
+
+h2 {
+ font-size: 20px;
+ font-weight: 600;
+ margin-bottom: 20px;
+ color: var(--text-primary);
+}
+
+.status-section {
+ background: var(--card-bg);
+ border-radius: 16px;
+ padding: 32px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.metrics-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 24px;
+ margin-bottom: 24px;
+}
+
+.metric-card {
+ padding: 20px;
+ background: var(--bg-color);
+ border-radius: 12px;
+ border: 1px solid var(--border-color);
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ min-height: 120px;
+}
+
+.metric-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+}
+
+.metric-label {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--text-secondary);
+}
+
+.metric-value {
+ font-size: 24px;
+ font-weight: 700;
+ color: var(--primary-color);
+}
+
+.progress-bar {
+ height: 8px;
+ background: var(--border-color);
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.progress-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--primary-color), var(--hover-color));
+ border-radius: 4px;
+ transition: width 0.5s ease;
+}
+
+.server-info {
+ padding: 20px;
+ background: var(--bg-color);
+ border-radius: 12px;
+ border: 1px solid var(--border-color);
+}
+
+.info-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 0;
+}
+
+.info-row:not(:last-child) {
+ border-bottom: 1px solid var(--border-color);
+}
+
+.info-label {
+ font-size: 14px;
+ color: var(--text-secondary);
+}
+
+.info-value {
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.status-online {
+ color: var(--success-color);
+}
+
+.status-offline {
+ color: #ef4444;
+}
+
+.connection-section {
+ background: var(--card-bg);
+ border-radius: 16px;
+ padding: 24px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.connection-card {
+ padding: 16px;
+ background: var(--bg-color);
+ border-radius: 12px;
+ border: 1px solid var(--border-color);
+}
+
+.connection-tip {
+ font-size: 14px;
+ color: var(--text-secondary);
+ margin-bottom: 12px;
+}
+
+.connection-details {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.detail-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.detail-label {
+ font-size: 14px;
+ color: var(--text-secondary);
+ min-width: 80px;
+}
+
+.detail-value {
+ position: relative;
+ font-family: 'Courier New', monospace;
+ font-size: 14px;
+ font-weight: 600;
+ background: var(--card-bg);
+ padding: 8px 16px;
+ padding-right: 40px;
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+ color: #000000;
+ display: inline-block;
+ max-width: 100%;
+}
+
+.protocol-selector {
+ position: relative;
+ display: inline-block;
+}
+
+.protocol-toggle {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+ font-size: 13px;
+ font-weight: 500;
+ background: rgba(175, 184, 193, 0.2);
+ color: #57606a;
+ border: 1px solid rgba(31, 35, 40, 0.15);
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.protocol-toggle:hover {
+ background: rgba(175, 184, 193, 0.4);
+ color: #24292f;
+}
+
+.protocol-toggle .chevron {
+ transition: transform 0.2s ease;
+ fill: currentColor;
+}
+
+.protocol-selector.open .chevron {
+ transform: rotate(180deg);
+}
+
+.protocol-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ margin-top: 4px;
+ background: var(--card-bg);
+ border: 1px solid rgba(31, 35, 40, 0.15);
+ border-radius: 6px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ overflow: hidden;
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(-10px);
+ transition: all 0.2s ease;
+ z-index: 100;
+ min-width: 100%;
+}
+
+.protocol-selector.open .protocol-menu {
+ opacity: 1;
+ visibility: visible;
+ transform: translateY(0);
+}
+
+.protocol-option {
+ padding: 8px 16px;
+ font-size: 13px;
+ color: #57606a;
+ cursor: pointer;
+ transition: all 0.15s ease;
+ white-space: nowrap;
+}
+
+.protocol-option:hover {
+ background: rgba(175, 184, 193, 0.2);
+ color: #24292f;
+}
+
+.protocol-option.active {
+ background: var(--primary-color);
+ color: white;
+}
+
+.address-center {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 4px;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.copy-button {
+ position: absolute;
+ top: 50%;
+ right: 8px;
+ transform: translateY(-50%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 4px;
+ font-size: 12px;
+ background: rgba(175, 184, 193, 0.2);
+ color: #57606a;
+ border: 1px solid rgba(31, 35, 40, 0.15);
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ min-width: 26px;
+ min-height: 26px;
+}
+
+.copy-button:hover {
+ background: rgba(175, 184, 193, 0.4);
+ color: #24292f;
+}
+
+.copy-button:active {
+ transform: translateY(-50%) scale(0.95);
+}
+
+.copy-icon,
+.check-icon {
+ fill: currentColor;
+ flex-shrink: 0;
+}
+
+#latency-value {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.latency-text {
+ font-size: 20px;
+}
+
+.location-box {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 12px;
+ background: var(--bg-color);
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+}
+
+.location-icon {
+ fill: var(--primary-color);
+ flex-shrink: 0;
+}
+
+.location-text {
+ font-size: 16px;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+
+.latency-test-btn {
+ padding: 4px 12px;
+ font-size: 12px;
+ background: var(--primary-color);
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.latency-test-btn:hover {
+ background: var(--hover-color);
+}
+
+.latency-test-btn:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+}
+
+.latency-good {
+ color: var(--success-color);
+}
+
+.latency-medium {
+ color: #f59e0b;
+}
+
+.latency-bad {
+ color: #ef4444;
+}
+
+.connection-section h2 {
+ margin-bottom: 16px;
+}
+
+.footer {
+ margin-top: 60px;
+ text-align: center;
+ padding: 20px;
+ color: var(--text-secondary);
+ font-size: 14px;
+}
+
+@media (max-width: 640px) {
+ .container {
+ padding: 20px 16px;
+ }
+
+ .logo-container {
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .icon-logo {
+ width: 80px;
+ }
+
+ .text-logo {
+ height: 60px;
+ }
+
+ .metrics-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .status-section,
+ .connection-section {
+ padding: 24px;
+ }
+
+ .address-center {
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .detail-value {
+ width: 100%;
+ font-size: 13px;
+ word-break: break-all;
+ }
+
+ .protocol-selector {
+ width: 100%;
+ }
+
+ .protocol-toggle {
+ width: 100%;
+ justify-content: center;
+ }
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.6;
+ }
+}
+
+.loading {
+ animation: pulse 1.5s ease-in-out infinite;
+}
diff --git a/install-easytier-relay.sh b/install-easytier-relay.sh
new file mode 100644
index 0000000..e24a34f
--- /dev/null
+++ b/install-easytier-relay.sh
@@ -0,0 +1,230 @@
+#!/usr/bin/env bash
+
+set -e
+
+echo "======================================"
+echo " EasyTier 公益中继节点一键部署脚本"
+echo "======================================"
+
+if [ "$(id -u)" != "0" ]; then
+ echo "请使用 root 运行"
+ exit 1
+fi
+
+########################################
+# 检测包管理器
+########################################
+
+if command -v dnf >/dev/null 2>&1; then
+ PM="dnf"
+elif command -v yum >/dev/null 2>&1; then
+ PM="yum"
+elif command -v apt >/dev/null 2>&1; then
+ PM="apt"
+elif command -v zypper >/dev/null 2>&1; then
+ PM="zypper"
+else
+ echo "不支持的 Linux 发行版"
+ exit 1
+fi
+
+echo "检测到包管理器: $PM"
+
+########################################
+# 安装基础组件
+########################################
+
+case $PM in
+ apt)
+ apt update
+ apt install -y curl wget sudo fail2ban
+ ;;
+ dnf|yum)
+ $PM install -y epel-release || true
+ $PM install -y curl wget sudo fail2ban
+ ;;
+ zypper)
+ zypper install -y curl wget sudo fail2ban
+ ;;
+esac
+
+########################################
+# 安装 EasyTier
+########################################
+
+echo "安装 EasyTier..."
+
+wget -O /tmp/easytier.sh \
+"https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh"
+
+bash /tmp/easytier.sh install --gh-proxy https://ghfast.top/
+
+########################################
+# 查找 easytier-core
+########################################
+
+EASYTIER_BIN=""
+
+for p in \
+/usr/local/bin/easytier-core \
+/usr/bin/easytier-core \
+/opt/easytier/easytier-core
+do
+ if [ -f "$p" ]; then
+ EASYTIER_BIN="$p"
+ break
+ fi
+done
+
+if [ -z "$EASYTIER_BIN" ]; then
+ echo "未找到 easytier-core"
+ exit 1
+fi
+
+echo "EasyTier 路径: $EASYTIER_BIN"
+
+########################################
+# 主机名
+########################################
+
+HOSTNAME=$(hostname)
+
+########################################
+# 创建 systemd 服务
+########################################
+
+cat >/etc/systemd/system/easytier.service </etc/sysctl.d/99-easytier.conf </etc/sysctl.d/99-bbr.conf </etc/fail2ban/filter.d/easytier.conf <:\\d+, err: wait resp error:.+
+EOF
+
+cat >/etc/fail2ban/jail.local </dev/null 2>&1; then
+
+ firewall-cmd --permanent --add-port=11010/tcp
+ firewall-cmd --permanent --add-port=11010/udp
+
+ firewall-cmd --permanent --add-port=11011/tcp
+ firewall-cmd --permanent --add-port=11011/udp
+
+ firewall-cmd --permanent --add-port=11012/tcp
+ firewall-cmd --permanent --add-port=11012/udp
+
+ firewall-cmd --permanent --add-port=11013/tcp
+
+ firewall-cmd --reload
+fi
+
+########################################
+# 启动服务
+########################################
+
+systemctl daemon-reload
+
+systemctl enable --now easytier
+systemctl enable --now fail2ban
+
+########################################
+# 输出状态
+########################################
+
+echo
+echo "======================================"
+echo " EasyTier 公益节点部署完成"
+echo "======================================"
+echo
+
+systemctl --no-pager --full status easytier || true
+
+echo
+echo "--------------------------------------"
+
+fail2ban-client status || true
+
+echo
+echo "--------------------------------------"
+
+echo "BBR 状态:"
+sysctl net.ipv4.tcp_congestion_control
+
+echo
+echo "监听端口:"
+ss -lntup | grep easytier || true
+
+echo
+echo "部署完成"
\ No newline at end of file