From 171e14176ad6c7ca745ead3062580dc643587184 Mon Sep 17 00:00:00 2001
From: iRedTea
Date: Tue, 13 Oct 2026 19:00:50 +0300
Subject: [PATCH] fix: small fixes
---
README.md | 28 ++++++++++----------
cmd/agent/main.go | 6 +++--
cmd/server/main.go | 4 ++-
deploy/lpra-server.service | 10 +++----
internal/agent/client.go | 2 +-
internal/agent/terminal.go | 2 +-
internal/agent/update.go | 2 +-
internal/server/db.go | 2 +-
internal/server/forward.go | 8 +++---
internal/server/handlers_agent.go | 14 +++++-----
internal/server/handlers_panel.go | 10 +++----
internal/server/handlers_panel_fs.go | 4 +--
internal/server/handlers_panel_stat.go | 10 +++----
internal/server/handlers_panel_term.go | 8 +++---
internal/server/hub.go | 2 +-
internal/server/server.go | 4 +--
internal/server/udp.go | 4 +--
internal/server/ufw.go | 2 +-
internal/server/web/templates/dashboard.html | 6 ++---
internal/server/web/templates/edit.html | 8 +++---
internal/server/web/templates/files.html | 6 ++---
internal/server/web/templates/forwards.html | 12 ++++-----
internal/server/web/templates/login.html | 2 +-
internal/server/web/templates/settings.html | 8 +++---
internal/server/web/templates/setup.html | 2 +-
internal/server/web/templates/status.html | 12 ++++-----
internal/server/web/templates/terminal.html | 6 ++---
internal/tunnel/frame.go | 2 +-
28 files changed, 95 insertions(+), 91 deletions(-)
diff --git a/README.md b/README.md
index d7b72df..4787a64 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# LPRA — LongPolling Remote Access
+# LPRA - LongPolling Remote Access
Лёгкий self-hosted сервис для доступа к Linux-машинам **без белого IP** (за NAT).
Агент на клиенте сам исходяще подключается к серверу по HTTP long-polling; админ
@@ -11,9 +11,9 @@
```
## Компоненты
-- **Сервер** (`cmd/server`) — админ-панель + точка встречи для агентов. За nginx (TLS).
-- **Агент** (`cmd/agent`) — Linux-бинарь, systemd-сервис от root, PTY по запросу.
-- **install.sh** — интерактивный установщик, отдаётся сервером как статика.
+- **Сервер** (`cmd/server`) - админ-панель + точка встречи для агентов. За nginx (TLS).
+- **Агент** (`cmd/agent`) - Linux-бинарь, systemd-сервис от root, PTY по запросу.
+- **install.sh** - интерактивный установщик, отдаётся сервером как статика.
## Сборка
```sh
@@ -28,7 +28,7 @@
Поставьте перед ним nginx по `deploy/nginx.conf.example` (TLS, long-poll/WS).
Локально без TLS можно добавить `-insecure-cookies`.
-Первый запуск: откройте `/setup` — мастер создаст администратора. Дальше `/settings`:
+Первый запуск: откройте `/setup` - мастер создаст администратора. Дальше `/settings`:
адрес хоста (по умолчанию `https://localhost:8445`), enroll-секрет и готовая
команда установки.
@@ -37,36 +37,36 @@
```sh
curl -fsSL https://localhost:8445/install.sh | sudo HOST=https://localhost:8445 ENROLL_SECRET=<секрет> sh
```
-Или интерактивно (спросит секрет, а если агент уже стоит — предложит удалить):
+Или интерактивно (спросит секрет, а если агент уже стоит - предложит удалить):
```sh
curl -fsSL https://localhost:8445/install.sh | sudo sh
```
## Проброс портов и прямой туннель
Правила проброса создаются на странице машины. Режим `http` идёт через адрес панели (long-poll),
-режимы `tcp`/`udp` — через прямой туннель: агент держит исходящее соединение к порту туннеля хоста
-(по умолчанию `8446`, флаг `-tunnel` / `LPRA_TUNNEL_ADDR`).
+режимы `tcp`/`udp` - через прямой туннель: агент держит исходящее соединение к порту туннеля хоста
+(флаг `-tunnel` / `LPRA_TUNNEL_ADDR`).
Порт туннеля и хост, к которому для него подключаются агенты (например, IP вместо домена панели),
-меняются в `/settings` → «Прямой туннель». Агенты забирают адрес с панели сами — при подключении и
+меняются в `/settings` → «Прямой туннель». Агенты забирают адрес с панели сами - при подключении и
при каждом опросе, переустановка не нужна. Локальные `TUNNEL_ADDR` / `TUNNEL_PORT` в окружении агента
имеют приоритет над настройкой панели (`TUNNEL_ADDR=off` отключает туннель).
Если на хосте активен ufw и сервер запущен от root, панель сама открывает порт хоста правила
-(tcp для `http`/`tcp`, udp для `udp`) и порт туннеля для прямых режимов, а при удалении правила — закрывает.
+(tcp для `http`/`tcp`, udp для `udp`) и порт туннеля для прямых режимов, а при удалении правила - закрывает.
При `ProtectSystem=strict` в systemd-юните ufw нужны права на запись: `ReadWritePaths=/etc/ufw /run`.
## Версия
-Версия задаётся в одном месте — `VERSION.txt`; файл вшивается в сервер и агент при сборке.
+Версия задаётся в одном месте - `VERSION.txt`; файл вшивается в сервер и агент при сборке.
## Пользователь терминала
-По умолчанию терминал открывается от пользователя, под которым запущен агент (на Linux — root).
+По умолчанию терминал открывается от пользователя, под которым запущен агент (на Linux - root).
В окне «Изменить машину» можно выбрать другого пользователя машины или создать нового
-(имя, пароль, опционально — добавить в sudo). Смена и создание работают, только если агент запущен от root.
+(имя, пароль, опционально - добавить в sudo). Смена и создание работают, только если агент запущен от root.
Файловый менеджер по-прежнему работает от пользователя агента.
## Безопасность
-- Пароли админов — bcrypt; сессии в HttpOnly/Secure/SameSite куках; CSRF на формах; rate-limit логина.
+- Пароли админов - bcrypt; сессии в HttpOnly/Secure/SameSite куках; CSRF на формах; rate-limit логина.
- Агенты: общий enroll-секрет → индивидуальный токен (хранится хешем, сверка constant-time).
- Открытие терминала пишется в таблицу `audit` (терминал даёт root-доступ), как и смена/создание пользователя.
diff --git a/cmd/agent/main.go b/cmd/agent/main.go
index 0bcce03..2334ec7 100644
--- a/cmd/agent/main.go
+++ b/cmd/agent/main.go
@@ -10,9 +10,11 @@ import (
"net/url"
"os"
"os/signal"
+ "strconv"
"syscall"
"github.com/x-grk/longpolling-remote-access/internal/agent"
+ "github.com/x-grk/longpolling-remote-access/internal/tunnel"
)
func main() {
@@ -48,7 +50,7 @@ func envOr(key, def string) string {
return def
}
-// tunnelAddr derives the direct-tunnel address (host:8446) from the server URL.
+// tunnelAddr derives the direct-tunnel address (host:port) from the server URL.
// It is only the starting point: the agent follows the host and port set in
// the panel settings, unless TUNNEL_ADDR (the whole address) or TUNNEL_PORT
// (just the port) pins it here. Set TUNNEL_ADDR=off to disable the tunnel.
@@ -63,5 +65,5 @@ func tunnelAddr(hostURL string) string {
if err != nil || u.Hostname() == "" {
return ""
}
- return net.JoinHostPort(u.Hostname(), envOr("TUNNEL_PORT", "8446"))
+ return net.JoinHostPort(u.Hostname(), envOr("TUNNEL_PORT", strconv.Itoa(tunnel.DefaultPort)))
}
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 85a9958..33b1959 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -10,16 +10,18 @@ import (
"log"
"net/http"
"os"
+ "strconv"
"time"
"github.com/x-grk/longpolling-remote-access/internal/server"
+ "github.com/x-grk/longpolling-remote-access/internal/tunnel"
)
func main() {
addr := flag.String("addr", envOr("LPRA_ADDR", "127.0.0.1:8091"), "listen address (behind nginx)")
dbPath := flag.String("db", envOr("LPRA_DB", "lpra.db"), "path to SQLite database")
distDir := flag.String("dist", envOr("LPRA_DIST", "dist"), "directory with agent binaries served at /download/")
- tunnelAddr := flag.String("tunnel", envOr("LPRA_TUNNEL_ADDR", ":8446"), "listen address for the direct TCP+UDP forward tunnel (empty disables)")
+ tunnelAddr := flag.String("tunnel", envOr("LPRA_TUNNEL_ADDR", ":"+strconv.Itoa(tunnel.DefaultPort)), "listen address for the direct TCP+UDP forward tunnel (empty disables)")
insecureCookies := flag.Bool("insecure-cookies", envOr("LPRA_INSECURE_COOKIES", "") == "1",
"do not mark cookies Secure (plain-HTTP local testing only)")
flag.Parse()
diff --git a/deploy/lpra-server.service b/deploy/lpra-server.service
index 1c2c83e..685fe42 100644
--- a/deploy/lpra-server.service
+++ b/deploy/lpra-server.service
@@ -10,15 +10,15 @@
# sudo cp deploy/lpra-server.service /etc/systemd/system/
# sudo systemctl daemon-reload && sudo systemctl enable --now lpra-server
#
-# Direct port forwarding (tcp/udp modes) uses port 8446 on the host — open it in
-# the firewall: ufw allow 8446/tcp ; ufw allow 8446/udp
-# (The "http" forward mode and terminal/files go through nginx :8445 and need
-# nothing extra.) Override with LPRA_TUNNEL_ADDR, or set it empty to disable.
+# Direct port forwarding (tcp/udp modes) uses the tunnel port on the host, which
+# has to be open in the firewall for tcp and udp. (The "http" forward mode and
+# terminal/files go through nginx :8445 and need nothing extra.) Set the tunnel
+# address with LPRA_TUNNEL_ADDR, or set it empty to disable.
# The port, and the host agents dial for it, can also be changed in the panel
# settings; agents pick the new address up from the panel by themselves.
#
# ufw integration (the panel opening forwarded ports itself) needs root and a
-# writable /etc/ufw and /run — see ReadWritePaths below.
+# writable /etc/ufw and /run - see ReadWritePaths below.
[Unit]
Description=LPRA remote access host (admin panel + agent rendezvous)
diff --git a/internal/agent/client.go b/internal/agent/client.go
index 6b6805a..5577a32 100644
--- a/internal/agent/client.go
+++ b/internal/agent/client.go
@@ -33,7 +33,7 @@ type Config struct {
EnrollSecret string
Name string // optional friendly name
StatePath string // where the enrolled id+token are persisted
- TunnelAddr string // host:port for the direct forward tunnel (e.g. REDACTED:8446); empty disables
+ TunnelAddr string // host:port for the direct forward tunnel (e.g. panel.example:port); empty disables
// TunnelPinned keeps TunnelAddr as configured locally (TUNNEL_ADDR /
// TUNNEL_PORT) instead of following the address set in the panel.
TunnelPinned bool
diff --git a/internal/agent/terminal.go b/internal/agent/terminal.go
index a43cbca..488589f 100644
--- a/internal/agent/terminal.go
+++ b/internal/agent/terminal.go
@@ -15,7 +15,7 @@ type PTY struct {
}
// StartPTY launches a login shell attached to a new PTY. With user empty (or
-// equal to the agent's own user) it is the agent's shell — root by default,
+// equal to the agent's own user) it is the agent's shell - root by default,
// since the agent runs as a root systemd service. Any other user is entered via
// `su -l`, which only a root agent can do without a password.
func StartPTY(user string) (*PTY, error) {
diff --git a/internal/agent/update.go b/internal/agent/update.go
index 2e29c6e..c1c3a91 100644
--- a/internal/agent/update.go
+++ b/internal/agent/update.go
@@ -17,7 +17,7 @@ import (
// doUpdate downloads the latest agent binary for this platform, atomically
// replaces the running executable, and schedules a service restart. It never
-// uninstalls — only updates.
+// uninstalls - only updates.
func (a *Agent) doUpdate() proto.TaskResult {
exe, err := os.Executable()
if err != nil {
diff --git a/internal/server/db.go b/internal/server/db.go
index cc076dd..b1f4607 100644
--- a/internal/server/db.go
+++ b/internal/server/db.go
@@ -400,7 +400,7 @@ type UfwRule struct {
ID int64
Port int
Proto string
- RuleID int64 // bound forward rule id, or 0 for shared/manual (e.g. tunnel 8446)
+ RuleID int64 // bound forward rule id, or 0 for shared/manual (e.g. the tunnel port)
}
func (db *DB) AddUfwRule(port int, proto string, ruleID int64) error {
diff --git a/internal/server/forward.go b/internal/server/forward.go
index cc0670e..c16e291 100644
--- a/internal/server/forward.go
+++ b/internal/server/forward.go
@@ -14,8 +14,8 @@ import (
// supports three transports:
// - "http": tunneled over the HTTP long-poll channel (works anywhere nginx
// reaches, highest latency);
-// - "tcp": direct TCP multiplexed over the 8446 tunnel (low latency);
-// - "udp": direct UDP over the 8446 channel.
+// - "tcp": direct TCP multiplexed over the tunnel port (low latency);
+// - "udp": direct UDP over the tunnel port.
type forwardManager struct {
hub *Hub
tun *tunnelHub
@@ -92,11 +92,11 @@ func (m *forwardManager) acceptTCP(rule ForwardRule, ln net.Listener) {
}
}
-// bridgeDirect wires a connection to the agent's low-latency 8446 tunnel.
+// bridgeDirect wires a connection to the agent's low-latency direct tunnel.
func (m *forwardManager) bridgeDirect(rule ForwardRule, conn net.Conn) {
at := m.tun.get(rule.AgentID)
if at == nil {
- // No direct tunnel (agent offline or 8446 unreachable).
+ // No direct tunnel (agent offline or the tunnel port unreachable).
conn.Close()
return
}
diff --git a/internal/server/handlers_agent.go b/internal/server/handlers_agent.go
index 03bfd35..cee1d74 100644
--- a/internal/server/handlers_agent.go
+++ b/internal/server/handlers_agent.go
@@ -16,7 +16,7 @@ func writeJSON(w http.ResponseWriter, v any) {
_ = json.NewEncoder(w).Encode(v)
}
-// POST /api/agent/enroll — exchange the shared enroll secret for a per-agent id+token.
+// POST /api/agent/enroll - exchange the shared enroll secret for a per-agent id+token.
func (s *Server) handleEnroll(w http.ResponseWriter, r *http.Request) {
secret := r.Header.Get("X-Enroll-Secret")
if !constantTimeEqual(secret, s.db.EnrollSecret()) {
@@ -36,7 +36,7 @@ func (s *Server) handleEnroll(w http.ResponseWriter, r *http.Request) {
writeJSON(w, proto.EnrollResponse{AgentID: id, Token: token})
}
-// GET /api/agent/poll — control channel. Held until a job is queued or timeout.
+// GET /api/agent/poll - control channel. Held until a job is queued or timeout.
func (s *Server) handlePoll(w http.ResponseWriter, r *http.Request) {
a := s.authenticateAgent(r)
if a == nil {
@@ -68,7 +68,7 @@ func (s *Server) setTunnelHeader(w http.ResponseWriter) {
}
}
-// GET /api/agent/config — settings an agent picks up when it connects, without
+// GET /api/agent/config - settings an agent picks up when it connects, without
// waiting out a poll hold.
func (s *Server) handleAgentConfig(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
@@ -79,7 +79,7 @@ func (s *Server) handleAgentConfig(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
-// GET /api/agent/task/{id} — the agent fetches the task to run.
+// GET /api/agent/task/{id} - the agent fetches the task to run.
func (s *Server) handleTaskFetch(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
@@ -93,7 +93,7 @@ func (s *Server) handleTaskFetch(w http.ResponseWriter, r *http.Request) {
writeJSON(w, env)
}
-// POST /api/agent/task/{id}/result — the agent returns the task result.
+// POST /api/agent/task/{id}/result - the agent returns the task result.
func (s *Server) handleTaskResult(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
@@ -108,7 +108,7 @@ func (s *Server) handleTaskResult(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
-// GET /api/agent/term/{sid}/in — downstream (panel -> agent) long-poll.
+// GET /api/agent/term/{sid}/in - downstream (panel -> agent) long-poll.
func (s *Server) handleTermIn(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
@@ -127,7 +127,7 @@ func (s *Server) handleTermIn(w http.ResponseWriter, r *http.Request) {
writeJSON(w, proto.InBatch{Frames: frames})
}
-// POST /api/agent/term/{sid}/out — upstream (agent PTY -> panel).
+// POST /api/agent/term/{sid}/out - upstream (agent PTY -> panel).
func (s *Server) handleTermOut(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
diff --git a/internal/server/handlers_panel.go b/internal/server/handlers_panel.go
index e95e43c..0d84280 100644
--- a/internal/server/handlers_panel.go
+++ b/internal/server/handlers_panel.go
@@ -137,7 +137,7 @@ func seenText(online bool, lastSeen time.Time) string {
return humanSince(lastSeen)
}
-// GET /api/panel/agents/presence — who is online, for the dashboard to keep
+// GET /api/panel/agents/presence - who is online, for the dashboard to keep
// its status column live without reloading.
func (s *Server) handlePresence(w http.ResponseWriter, r *http.Request) {
agents, err := s.db.ListAgents()
@@ -227,7 +227,7 @@ func (s *Server) agentUsers(agentID string) (*proto.SysUsersResult, error) {
case res := <-ch:
if !res.OK {
// Agents older than 0.0.2 don't know this task.
- return nil, errors.New("клиент не поддерживает выбор пользователя — обновите его")
+ return nil, errors.New("клиент не поддерживает выбор пользователя - обновите его")
}
var out proto.SysUsersResult
if err := json.Unmarshal(res.Data, &out); err != nil {
@@ -258,7 +258,7 @@ func (s *Server) handleAgentSetUser(w http.ResponseWriter, r *http.Request) {
}
name := strings.TrimSpace(r.FormValue("user"))
if !s.online(a) {
- editUserRedirect(w, r, id, "uerr", "Машина офлайн — сменить пользователя нельзя.")
+ editUserRedirect(w, r, id, "uerr", "Машина офлайн - сменить пользователя нельзя.")
return
}
// Only accept a user the machine itself reports, so a typo (or an agent too
@@ -299,7 +299,7 @@ func (s *Server) handleAgentAddUser(w http.ResponseWriter, r *http.Request) {
return
}
if !s.online(a) {
- editUserRedirect(w, r, id, "uerr", "Машина офлайн — создать пользователя нельзя.")
+ editUserRedirect(w, r, id, "uerr", "Машина офлайн - создать пользователя нельзя.")
return
}
name := strings.TrimSpace(r.FormValue("name"))
@@ -402,7 +402,7 @@ func (s *Server) handleSettings(w http.ResponseWriter, r *http.Request) {
})
}
-// POST /settings/tunnel — host and port agents use for the direct tunnel.
+// POST /settings/tunnel - host and port agents use for the direct tunnel.
func (s *Server) handleSetTunnel(w http.ResponseWriter, r *http.Request) {
if !checkCSRF(r) {
http.Error(w, "bad csrf", http.StatusForbidden)
diff --git a/internal/server/handlers_panel_fs.go b/internal/server/handlers_panel_fs.go
index 736a0ec..868cd98 100644
--- a/internal/server/handlers_panel_fs.go
+++ b/internal/server/handlers_panel_fs.go
@@ -37,7 +37,7 @@ func (s *Server) handleFilesPage(w http.ResponseWriter, r *http.Request) {
s.render(w, "files.html", map[string]any{"Agent": a, "EditLimit": proto.FSReadCap})
}
-// POST /api/panel/agents/{id}/fs/{op} — run a file-manager operation on the
+// POST /api/panel/agents/{id}/fs/{op} - run a file-manager operation on the
// agent and return its result. The request body is the op's JSON args and is
// forwarded verbatim to the agent.
func (s *Server) handleFSOp(w http.ResponseWriter, r *http.Request) {
@@ -74,7 +74,7 @@ func (s *Server) handleFSOp(w http.ResponseWriter, r *http.Request) {
}
}
-// GET /api/panel/agents/{id}/fs/download?path=... — stream a file as a download.
+// GET /api/panel/agents/{id}/fs/download?path=... - stream a file as a download.
func (s *Server) handleFSDownload(w http.ResponseWriter, r *http.Request) {
agentID := r.PathValue("id")
p := r.URL.Query().Get("path")
diff --git a/internal/server/handlers_panel_stat.go b/internal/server/handlers_panel_stat.go
index 73c90e9..fac0f84 100644
--- a/internal/server/handlers_panel_stat.go
+++ b/internal/server/handlers_panel_stat.go
@@ -103,12 +103,12 @@ func (s *Server) onlineAgent(w http.ResponseWriter, r *http.Request) (string, bo
// the task.
func oldAgentError(res proto.TaskResult) proto.TaskResult {
if !res.OK && strings.Contains(res.Error, "неизвестная операция") {
- res.Error = "клиент не поддерживает эту функцию — обновите его (нужен 0.0.5+)"
+ res.Error = "клиент не поддерживает эту функцию - обновите его (нужен 0.0.5+)"
}
return res
}
-// GET /api/panel/agents/{id}/stat — CPU, RAM and disk of the machine right now.
+// GET /api/panel/agents/{id}/stat - CPU, RAM and disk of the machine right now.
func (s *Server) handleAgentStat(w http.ResponseWriter, r *http.Request) {
id, ok := s.onlineAgent(w, r)
if !ok {
@@ -128,7 +128,7 @@ func (s *Server) handleAgentStat(w http.ResponseWriter, r *http.Request) {
writeJSON(w, e.res)
}
-// POST /api/panel/agents/{id}/speed — have the agent measure its link to us.
+// POST /api/panel/agents/{id}/speed - have the agent measure its link to us.
func (s *Server) handleAgentSpeed(w http.ResponseWriter, r *http.Request) {
id, ok := s.onlineAgent(w, r)
if !ok {
@@ -150,7 +150,7 @@ var speedBlock = sync.OnceValue(func() []byte {
return b
})
-// GET /api/agent/speed/down?bytes=N — stream N bytes (default: as much as fits
+// GET /api/agent/speed/down?bytes=N - stream N bytes (default: as much as fits
// in the time window) for the agent to time. bytes=0 is the ping probe.
func (s *Server) handleSpeedDown(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
@@ -182,7 +182,7 @@ func (s *Server) handleSpeedDown(w http.ResponseWriter, r *http.Request) {
}
}
-// POST /api/agent/speed/up — swallow the agent's upload.
+// POST /api/agent/speed/up - swallow the agent's upload.
func (s *Server) handleSpeedUp(w http.ResponseWriter, r *http.Request) {
if s.authenticateAgent(r) == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
diff --git a/internal/server/handlers_panel_term.go b/internal/server/handlers_panel_term.go
index 7a8128b..ee5e2e4 100644
--- a/internal/server/handlers_panel_term.go
+++ b/internal/server/handlers_panel_term.go
@@ -12,7 +12,7 @@ import (
// same transport the agent uses), so it works through any reverse proxy without
// WebSocket upgrade support.
-// POST /api/panel/term/{agentID}/open — create a terminal session.
+// POST /api/panel/term/{agentID}/open - create a terminal session.
func (s *Server) handlePanelOpen(w http.ResponseWriter, r *http.Request) {
agentID := r.PathValue("agentID")
agent, err := s.db.GetAgent(agentID)
@@ -28,7 +28,7 @@ func (s *Server) handlePanelOpen(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]string{"session_id": sess.ID})
}
-// GET /api/panel/term/{sid}/out — long-poll for PTY output.
+// GET /api/panel/term/{sid}/out - long-poll for PTY output.
func (s *Server) handlePanelOut(w http.ResponseWriter, r *http.Request) {
sess := s.hub.Session(r.PathValue("sid"))
if sess == nil {
@@ -51,7 +51,7 @@ type panelInMsg struct {
Rows uint16 `json:"rows,omitempty"`
}
-// POST /api/panel/term/{sid}/in — send keystrokes/resize to the agent PTY.
+// POST /api/panel/term/{sid}/in - send keystrokes/resize to the agent PTY.
func (s *Server) handlePanelIn(w http.ResponseWriter, r *http.Request) {
sess := s.hub.Session(r.PathValue("sid"))
if sess == nil {
@@ -72,7 +72,7 @@ func (s *Server) handlePanelIn(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
-// POST /api/panel/term/{sid}/close — tear the session down.
+// POST /api/panel/term/{sid}/close - tear the session down.
func (s *Server) handlePanelClose(w http.ResponseWriter, r *http.Request) {
s.hub.CloseSession(r.PathValue("sid"))
w.WriteHeader(http.StatusNoContent)
diff --git a/internal/server/hub.go b/internal/server/hub.go
index c0f1be5..49c2db6 100644
--- a/internal/server/hub.go
+++ b/internal/server/hub.go
@@ -37,7 +37,7 @@ type taskEntry struct {
}
// Session is one live byte stream between the panel and an agent (a PTY terminal
-// or a raw TCP forward — the hub treats both identically).
+// or a raw TCP forward - the hub treats both identically).
type Session struct {
ID string
AgentID string
diff --git a/internal/server/server.go b/internal/server/server.go
index ddfe75a..24a9475 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -26,8 +26,8 @@ type Options struct {
// nginx TLS); set false only for plain-HTTP local testing.
SecureCookies bool
// TunnelAddr is the listen address for the direct TCP+UDP forward tunnel
- // (default ":8446"). Empty disables direct forwarding. The port can be
- // overridden from the settings page.
+ // Empty disables direct forwarding. The port can be overridden from the
+ // settings page.
TunnelAddr string
}
diff --git a/internal/server/udp.go b/internal/server/udp.go
index b49277c..3771313 100644
--- a/internal/server/udp.go
+++ b/internal/server/udp.go
@@ -11,11 +11,11 @@ import (
"github.com/x-grk/longpolling-remote-access/internal/tunnel"
)
-// udpRelay carries UDP forwards over the direct 8446 UDP channel. The agent
+// udpRelay carries UDP forwards over the direct UDP channel. The agent
// registers its UDP return address; end-user datagrams arriving on a rule's
// host port are relayed to the agent and replies are sent back.
//
-// Datagram framing on the 8446 control socket:
+// Datagram framing on the tunnel control socket:
//
// registration (agent->server): "R "
// data (both): 'D' | sid(4) | port(2) | payload
diff --git a/internal/server/ufw.go b/internal/server/ufw.go
index 9619fab..dcc02a0 100644
--- a/internal/server/ufw.go
+++ b/internal/server/ufw.go
@@ -172,7 +172,7 @@ func (u *ufwManager) pruneShared() {
// allowForward opens the host ports a forward rule needs: its host port (bound
// to the rule so it is revoked with it) plus the shared tunnel port for direct
-// tcp/udp modes (bound to 0 — shared across rules). A rule made "without ufw"
+// tcp/udp modes (bound to 0 - shared across rules). A rule made "without ufw"
// opens nothing, so nothing is revoked with it either.
func (u *ufwManager) allowForward(rule ForwardRule) {
if !u.available() {
diff --git a/internal/server/web/templates/dashboard.html b/internal/server/web/templates/dashboard.html
index e8f7322..fcecdb5 100644
--- a/internal/server/web/templates/dashboard.html
+++ b/internal/server/web/templates/dashboard.html
@@ -3,7 +3,7 @@
-Машины — LPRA
+Машины - LPRA
@@ -49,7 +49,7 @@