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
+28
-9
No files matched your search
@@ -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 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 label TEXT NOT NULL DEFAULT ''`,
|
||||
} {
|
||||
if _, err := db.sql.Exec(alter); err != nil && !strings.Contains(err.Error(), "duplicate column") {
|
||||
return err
|
||||
@@ -330,6 +331,7 @@ type ForwardRule struct {
|
||||
ListenAddr string
|
||||
Proto string
|
||||
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) {
|
||||
@@ -337,9 +339,9 @@ func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
||||
r.Proto = "http"
|
||||
}
|
||||
res, err := db.sql.Exec(
|
||||
`INSERT INTO forward_rules(agent_id, client_port, host_port, listen_addr, proto, no_ufw, created_at)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
||||
r.AgentID, r.ClientPort, r.HostPort, r.ListenAddr, r.Proto, r.NoUfw, time.Now().Unix())
|
||||
`INSERT INTO forward_rules(agent_id, client_port, host_port, listen_addr, proto, no_ufw, label, created_at)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
r.AgentID, r.ClientPort, r.HostPort, r.ListenAddr, r.Proto, r.NoUfw, r.Label, time.Now().Unix())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -348,7 +350,7 @@ func (db *DB) CreateForwardRule(r ForwardRule) (int64, error) {
|
||||
|
||||
func (db *DB) ListForwardRules() ([]ForwardRule, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -356,7 +358,7 @@ func (db *DB) ListForwardRules() ([]ForwardRule, error) {
|
||||
var out []ForwardRule
|
||||
for rows.Next() {
|
||||
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
|
||||
}
|
||||
out = append(out, r)
|
||||
@@ -381,8 +383,8 @@ func (db *DB) ListForwardRulesByAgent(agentID string) ([]ForwardRule, error) {
|
||||
func (db *DB) GetForwardRule(id int64) (*ForwardRule, error) {
|
||||
r := &ForwardRule{}
|
||||
err := db.sql.QueryRow(
|
||||
`SELECT id, agent_id, client_port, host_port, listen_addr, proto, no_ufw FROM forward_rules WHERE id = ?`, id).
|
||||
Scan(&r.ID, &r.AgentID, &r.ClientPort, &r.HostPort, &r.ListenAddr, &r.Proto, &r.NoUfw)
|
||||
`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, &r.Label)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (s *Server) handleForwardAdd(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
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.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>
|
||||
{{if .Rules}}
|
||||
<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>
|
||||
{{range .Rules}}
|
||||
<tr>
|
||||
@@ -48,6 +48,7 @@
|
||||
<td class="muted">:{{.HostPort}}</td>
|
||||
<td class="muted">127.0.0.1:{{.ClientPort}}</td>
|
||||
<td class="muted">{{.ListenAddr}}{{if and .NoUfw $.UfwEnabled}} · без ufw{{end}}</td>
|
||||
<td>{{.Label}}</td>
|
||||
<td style="text-align:right">
|
||||
<form method="post" action="/forwards/{{.ID}}/delete" style="display:inline"
|
||||
onsubmit="return confirm('Удалить правило?')">
|
||||
@@ -89,6 +90,10 @@
|
||||
<label>Слушать на хосте</label>
|
||||
<input type="text" name="listen_addr" value="0.0.0.0" style="max-width:160px">
|
||||
</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>
|
||||
</div>
|
||||
{{if .UfwEnabled}}
|
||||
|
||||
Reference in new issue
Block a user