feat: optional label on a forward rule
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
201d63ee1e
commit
b51363bf72
3 files changed
+29
-10
No files matched your search
+10
-8
@@ -106,6 +106,7 @@ CREATE TABLE IF NOT EXISTS forward_rules (
|
|||||||
`ALTER TABLE agents ADD COLUMN run_user TEXT NOT NULL DEFAULT ''`,
|
`ALTER TABLE agents ADD COLUMN run_user TEXT NOT NULL DEFAULT ''`,
|
||||||
`ALTER TABLE agents ADD COLUMN login_user TEXT NOT NULL DEFAULT ''`,
|
`ALTER TABLE agents ADD COLUMN login_user TEXT NOT NULL DEFAULT ''`,
|
||||||
`ALTER TABLE forward_rules ADD COLUMN no_ufw INTEGER NOT NULL DEFAULT 0`,
|
`ALTER TABLE forward_rules ADD COLUMN no_ufw INTEGER NOT NULL DEFAULT 0`,
|
||||||
|
`ALTER TABLE forward_rules ADD COLUMN label TEXT NOT NULL DEFAULT ''`,
|
||||||
} {
|
} {
|
||||||
if _, err := db.sql.Exec(alter); err != nil && !strings.Contains(err.Error(), "duplicate column") {
|
if _, err := db.sql.Exec(alter); err != nil && !strings.Contains(err.Error(), "duplicate column") {
|
||||||
return err
|
return err
|
||||||
@@ -329,7 +330,8 @@ type ForwardRule struct {
|
|||||||
HostPort int
|
HostPort int
|
||||||
ListenAddr string
|
ListenAddr string
|
||||||
Proto string
|
Proto string
|
||||||
NoUfw bool // the panel opens no ufw rule for this forward
|
NoUfw bool // the panel opens no ufw rule for this forward
|
||||||
|
Label string // optional note shown next to the rule
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
||||||
@@ -337,9 +339,9 @@ func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
|||||||
r.Proto = "http"
|
r.Proto = "http"
|
||||||
}
|
}
|
||||||
res, err := db.sql.Exec(
|
res, err := db.sql.Exec(
|
||||||
`INSERT INTO forward_rules(agent_id, client_port, host_port, listen_addr, proto, no_ufw, created_at)
|
`INSERT INTO forward_rules(agent_id, client_port, host_port, listen_addr, proto, no_ufw, label, created_at)
|
||||||
VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
VALUES(?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
r.AgentID, r.ClientPort, r.HostPort, r.ListenAddr, r.Proto, r.NoUfw, time.Now().Unix())
|
r.AgentID, r.ClientPort, r.HostPort, r.ListenAddr, r.Proto, r.NoUfw, r.Label, time.Now().Unix())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -348,7 +350,7 @@ func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
|||||||
|
|
||||||
func (db *DB) ListForwardRules() ([]ForwardRule, error) {
|
func (db *DB) ListForwardRules() ([]ForwardRule, error) {
|
||||||
rows, err := db.sql.Query(
|
rows, err := db.sql.Query(
|
||||||
`SELECT id, agent_id, client_port, host_port, listen_addr, proto, no_ufw FROM forward_rules ORDER BY id`)
|
`SELECT id, agent_id, client_port, host_port, listen_addr, proto, no_ufw, label FROM forward_rules ORDER BY id`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -356,7 +358,7 @@ func (db *DB) ListForwardRules() ([]ForwardRule, error) {
|
|||||||
var out []ForwardRule
|
var out []ForwardRule
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var r ForwardRule
|
var r ForwardRule
|
||||||
if err := rows.Scan(&r.ID, &r.AgentID, &r.ClientPort, &r.HostPort, &r.ListenAddr, &r.Proto, &r.NoUfw); err != nil {
|
if err := rows.Scan(&r.ID, &r.AgentID, &r.ClientPort, &r.HostPort, &r.ListenAddr, &r.Proto, &r.NoUfw, &r.Label); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out = append(out, r)
|
out = append(out, r)
|
||||||
@@ -381,8 +383,8 @@ func (db *DB) ListForwardRulesByAgent(agentID string) ([]ForwardRule, error) {
|
|||||||
func (db *DB) GetForwardRule(id int64) (*ForwardRule, error) {
|
func (db *DB) GetForwardRule(id int64) (*ForwardRule, error) {
|
||||||
r := &ForwardRule{}
|
r := &ForwardRule{}
|
||||||
err := db.sql.QueryRow(
|
err := db.sql.QueryRow(
|
||||||
`SELECT id, agent_id, client_port, host_port, listen_addr, proto, no_ufw FROM forward_rules WHERE id = ?`, id).
|
`SELECT id, agent_id, client_port, host_port, listen_addr, proto, no_ufw, label FROM forward_rules WHERE id = ?`, id).
|
||||||
Scan(&r.ID, &r.AgentID, &r.ClientPort, &r.HostPort, &r.ListenAddr, &r.Proto, &r.NoUfw)
|
Scan(&r.ID, &r.AgentID, &r.ClientPort, &r.HostPort, &r.ListenAddr, &r.Proto, &r.NoUfw, &r.Label)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (s *Server) handleForwardAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
rule := ForwardRule{AgentID: id, ClientPort: clientPort, HostPort: hostPort, ListenAddr: listenAddr, Proto: protoKind,
|
rule := ForwardRule{AgentID: id, ClientPort: clientPort, HostPort: hostPort, ListenAddr: listenAddr, Proto: protoKind,
|
||||||
NoUfw: r.FormValue("no_ufw") != ""}
|
NoUfw: r.FormValue("no_ufw") != "", Label: forwardLabel(r.FormValue("label"))}
|
||||||
ruleID, err := s.db.CreateForwardRule(rule)
|
ruleID, err := s.db.CreateForwardRule(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Redirect(w, r, "/agents/"+id+"/forwards?err=db", http.StatusSeeOther)
|
http.Redirect(w, r, "/agents/"+id+"/forwards?err=db", http.StatusSeeOther)
|
||||||
@@ -130,3 +130,15 @@ func (s *Server) removeForward(rid int64) {
|
|||||||
_ = s.db.DeleteForwardRule(rid)
|
_ = s.db.DeleteForwardRule(rid)
|
||||||
s.ufw.revokeForRule(rid)
|
s.ufw.revokeForRule(rid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forwardLabelMax bounds a rule's label, in characters.
|
||||||
|
const forwardLabelMax = 80
|
||||||
|
|
||||||
|
// forwardLabel tidies the optional note of a rule: one line, bounded length.
|
||||||
|
func forwardLabel(v string) string {
|
||||||
|
v = strings.Join(strings.Fields(v), " ")
|
||||||
|
if r := []rune(v); len(r) > forwardLabelMax {
|
||||||
|
v = string(r[:forwardLabelMax])
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<h2>Правила</h2>
|
<h2>Правила</h2>
|
||||||
{{if .Rules}}
|
{{if .Rules}}
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th>Тип</th><th>Хост (порт)</th><th>→ Клиент (порт)</th><th>Слушать на</th><th></th></tr></thead>
|
<thead><tr><th>Тип</th><th>Хост (порт)</th><th>→ Клиент (порт)</th><th>Слушать на</th><th>Метка</th><th></th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range .Rules}}
|
{{range .Rules}}
|
||||||
<tr>
|
<tr>
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
<td class="muted">:{{.HostPort}}</td>
|
<td class="muted">:{{.HostPort}}</td>
|
||||||
<td class="muted">127.0.0.1:{{.ClientPort}}</td>
|
<td class="muted">127.0.0.1:{{.ClientPort}}</td>
|
||||||
<td class="muted">{{.ListenAddr}}{{if and .NoUfw $.UfwEnabled}} · без ufw{{end}}</td>
|
<td class="muted">{{.ListenAddr}}{{if and .NoUfw $.UfwEnabled}} · без ufw{{end}}</td>
|
||||||
|
<td>{{.Label}}</td>
|
||||||
<td style="text-align:right">
|
<td style="text-align:right">
|
||||||
<form method="post" action="/forwards/{{.ID}}/delete" style="display:inline"
|
<form method="post" action="/forwards/{{.ID}}/delete" style="display:inline"
|
||||||
onsubmit="return confirm('Удалить правило?')">
|
onsubmit="return confirm('Удалить правило?')">
|
||||||
@@ -89,6 +90,10 @@
|
|||||||
<label>Слушать на хосте</label>
|
<label>Слушать на хосте</label>
|
||||||
<input type="text" name="listen_addr" value="0.0.0.0" style="max-width:160px">
|
<input type="text" name="listen_addr" value="0.0.0.0" style="max-width:160px">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="fld" style="flex:1; min-width:200px">
|
||||||
|
<label>Метка (необязательно)</label>
|
||||||
|
<input type="text" name="label" maxlength="80" placeholder="для чего это правило">
|
||||||
|
</div>
|
||||||
<button type="submit">Создать</button>
|
<button type="submit">Создать</button>
|
||||||
</div>
|
</div>
|
||||||
{{if .UfwEnabled}}
|
{{if .UfwEnabled}}
|
||||||
|
|||||||
Reference in new issue
Block a user