From 3954954f860e9f330d70557c16443bdf73f28bb2 Mon Sep 17 00:00:00 2001 From: almostm4 Date: Sun, 9 Nov 2025 14:45:01 +0100 Subject: [PATCH 1/2] Fix tor configuration append / graph visualization --- install.sh | 44 ++++++++++++++++++++++++++++++++++++++------ web/api/stats.php | 17 +++++++++++++---- web/setup.php | 2 +- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/install.sh b/install.sh index 6c46b17..6ea7377 100644 --- a/install.sh +++ b/install.sh @@ -32,6 +32,25 @@ TIMER="/etc/systemd/system/torpanel-collector.timer" export DEBIAN_FRONTEND=noninteractive +ensure_torrc_d_include() { + local main="$TOR_ETC/torrc" + install -d -m 755 "$TOR_TORRC_D" + + if [[ ! -f "$main" ]]; then + echo "# Created by TorPanel installer" > "$main" + fi + + if grep -Eq '^[[:space:]]*%include[[:space:]]+/etc/tor/torrc\.d/\*\.conf' "$main"; then + return 0 + fi + + if sed -n 's/^[[:space:]]*#\s*%include[[:space:]]\+\/etc\/tor\/torrc\.d\/\*\.conf/%include \/etc\/tor\/torrc.d\/\*\.conf/p' "$main" | grep -q .; then + sed -i 's/^[[:space:]]*#\s*%include[[:space:]]\+\/etc\/tor\/torrc\.d\/\*\.conf/%include \/etc\/tor\/torrc.d\/\*\.conf/' "$main" + else + printf '\n%%include /etc/tor/torrc.d/*.conf\n' >> "$main" + fi +} + echo -e "${C_BOLD}Installing TorPanel...${C_RESET}" info "Updating apt and installing packages" @@ -49,7 +68,7 @@ ln -sf "$PHP_SOCK" /run/php/php-fpm.sock || true info "Preparing directories" install -d "$PANEL_PUBLIC" "$STATE_DIR" "$LOG_DIR" "$ETC_APP" -touch "$STATE_DIR/stats.json" +echo -n '{"data":[]}' > "$STATE_DIR/stats.json" rsync -a --delete "$SCRIPT_DIR/web/" "$PANEL_PUBLIC/" chown -R www-data:www-data "$PANEL_ROOT" "$STATE_DIR" "$LOG_DIR" chmod 750 "$PANEL_ROOT" "$STATE_DIR" "$LOG_DIR" @@ -105,6 +124,10 @@ CookieAuthFileGroupReadable 1 TORRC ok "torrc written" +info "Ensuring main torrc includes torrc.d/*.conf" +ensure_torrc_d_include +ok "torrc.d include active" + info "Setting permissions for Tor managed config" chown root:www-data "$TOR_TORRC_D"; chmod 775 "$TOR_TORRC_D" chown root:www-data "$TOR_PANEL_CONF"; chmod 664 "$TOR_PANEL_CONF" @@ -129,6 +152,7 @@ cat > "$SVC" <<'UNIT' Description=TorPanel minute collector After=tor.service Wants=tor.service +ConditionPathExists=/run/tor/control.authcookie [Service] Type=oneshot @@ -146,17 +170,22 @@ ProtectKernelModules=yes ProtectKernelTunables=yes PrivateTmp=yes PrivateDevices=yes -PrivateUsers=yes +PrivateUsers=no LockPersonality=yes MemoryDenyWriteExecute=yes RestrictRealtime=yes RestrictSUIDSGID=yes UMask=0077 -ReadWriteDirectories=/var/lib/torpanel +ReadWritePaths=/var/lib/torpanel ReadOnlyPaths=/run/tor /etc/tor RestrictAddressFamilies=AF_UNIX SystemCallFilter=@system-service CapabilityBoundingSet= +StandardOutput=journal +StandardError=journal +Environment=PYTHONUNBUFFERED=1 +Restart=on-failure +RestartSec=15s [Install] WantedBy=timers.target @@ -165,11 +194,13 @@ UNIT cat > "$TIMER" <<'TIMER' [Unit] Description=Run TorPanel collector every minute + [Timer] -OnBootSec=30sec -OnUnitActiveSec=60sec -AccuracySec=15sec +OnCalendar=*-*-* *:*:00 +AccuracySec=15s Persistent=true +Unit=torpanel-collector.service + [Install] WantedBy=timers.target TIMER @@ -181,6 +212,7 @@ systemctl enable --now tor systemctl enable "$PHP_FPM_SVC" nginx >/dev/null systemctl restart "$PHP_FPM_SVC" systemctl restart nginx +systemctl start torpanel-collector.service systemctl enable --now torpanel-collector.timer ok "Services running" diff --git a/web/api/stats.php b/web/api/stats.php index bbb3eb4..d404794 100644 --- a/web/api/stats.php +++ b/web/api/stats.php @@ -1,7 +1,16 @@ []]); exit; } +header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); +header('Pragma: no-cache'); + +$path = '/var/lib/torpanel/stats.json'; +if (!is_readable($path)) { + echo json_encode(['data' => []]); + exit; +} + $raw = file_get_contents($path); -echo $raw ?: json_encode(["data"=>[]]); +echo $raw !== false && $raw !== '' ? $raw : json_encode(['data' => []]); \ No newline at end of file diff --git a/web/setup.php b/web/setup.php index 8de5f66..181657b 100644 --- a/web/setup.php +++ b/web/setup.php @@ -125,7 +125,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { -
+
Date: Sun, 9 Nov 2025 16:05:02 +0100 Subject: [PATCH 2/2] Naming adjustments --- web/api/stats_rates.php | 51 +++++++++++++++++++++++++++++++++++++++++ web/index.php | 8 +++---- 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 web/api/stats_rates.php diff --git a/web/api/stats_rates.php b/web/api/stats_rates.php new file mode 100644 index 0000000..d0801de --- /dev/null +++ b/web/api/stats_rates.php @@ -0,0 +1,51 @@ +true,'data'=>[]]); exit; } + +$raw = file_get_contents($path); +$state = json_decode($raw, true); +if (!is_array($state) || !is_array($state['data'] ?? null)) { + echo json_encode(['ok'=>true,'data'=>[]]); exit; +} + +usort($state['data'], function($a,$b){ + return (int)($a['t'] ?? 0) <=> (int)($b['t'] ?? 0); +}); + +$out = []; +$prev = null; +foreach ($state['data'] as $s) { + $t = (int)($s['t'] ?? 0); + if ($t <= 0) continue; + + $rx_total = (int)($s['read'] ?? ($s['bytes_read'] ?? ($s['rx'] ?? 0))); + $tx_total = (int)($s['written'] ?? ($s['bytes_written'] ?? ($s['tx'] ?? 0))); + + $rx_mbps = 0.0; $tx_mbps = 0.0; + if ($prev) { + $dt = max(1, $t - $prev['t']); + $drx = max(0, $rx_total - $prev['rx_total']); + $dtx = max(0, $tx_total - $prev['tx_total']); + $rx_mbps = ($drx * 8) / $dt / 1_000_000.0; + $tx_mbps = ($dtx * 8) / $dt / 1_000_000.0; + } + + $out[] = [ + 'ts' => $t * 1000, + 'rx_mbps' => round($rx_mbps, 3), + 'tx_mbps' => round($tx_mbps, 3), + 'rx_total' => $rx_total, + 'tx_total' => $tx_total, + ]; + $prev = ['t'=>$t, 'rx_total'=>$rx_total, 'tx_total'=>$tx_total]; +} + +if (count($out) > 1440) $out = array_slice($out, -1440); +echo json_encode(['ok'=>true,'data'=>$out]); \ No newline at end of file diff --git a/web/index.php b/web/index.php index afb3cc2..a12638f 100644 --- a/web/index.php +++ b/web/index.php @@ -87,14 +87,14 @@ auth_require();
-
Read (total)
+
RX (total)
-
Written (total)
+
TX (total)
@@ -326,8 +326,8 @@ function setChartData(labels, rx, tx){ chart = new Chart(ctx, { type: 'line', data: { labels, datasets: [ - {label:'Read/min', data: rx, tension:.3}, - {label:'Written/min', data: tx, tension:.3} + {label:'RX/min', data: rx, tension:.3}, + {label:'TX/min', data: tx, tension:.3} ]}, options: { responsive:true,