mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-10-31 22:37:20 +00:00
fix(render,store): frpc user 支持 + 修正 useEncryption/useCompression 渲染位置
- store.Remote 新增 user 字段(DB 列名 usr,避开保留字;migrate() 自动加列), 渲染为 frpc 全局 user。面板型 frps(如 ChhChongGou 实例)强制要求 user 登录, 缺失时服务端返回 panel authentication unavailable: panel HTTP status 403 - ★ 修复渲染 bug:useEncryption/useCompression/bandwidthLimit 必须内嵌在 proxy 的 transport 对象(frpc ProxyTransport)。此前渲染在 proxy 顶层,frpc 默认 --strict-config 直接拒载:unknown field "useEncryption"。任何启用加密/压缩 的转发都无法启动。poolCount 无 proxy 级表达(官方 ProxyTransport 无此字段), 保留在 store 但不再渲染 - 前端:RemoteNode 画布节点与 StatusView 表单加 user 输入(画布此前会静默 清掉未声明的字段),带『面板型 frps 必填』提示 - 测试:user 往返(含迁移路径)、渲染含/不含 user、transport 内嵌与顶层 非法键回归守卫
This commit is contained in:
@ -73,6 +73,12 @@ type Remote struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// User is frpc's global `user` field: the proxy names reported to frps become
|
||||
// spellings. Panel-managed frps installations require it for login (without
|
||||
// it the server answers `panel authentication unavailable: panel HTTP status
|
||||
// 403`), while plain frps ignores it. Empty = omitted from the rendered config.
|
||||
User string `json:"user,omitempty"`
|
||||
|
||||
// Transport section (M1). Protocol: tcp | quic | kcp | websocket.
|
||||
TransportProtocol string `json:"transportProtocol,omitempty"`
|
||||
TransportTLS bool `json:"transportTls,omitempty"`
|
||||
@ -322,6 +328,7 @@ func (s *Store) migrate() error {
|
||||
"transport_tls INTEGER NOT NULL DEFAULT 0",
|
||||
"transport_pool INTEGER NOT NULL DEFAULT 0",
|
||||
"transport_tls_server_name TEXT NOT NULL DEFAULT ''",
|
||||
"usr TEXT NOT NULL DEFAULT ''",
|
||||
"vhost_http_port INTEGER NOT NULL DEFAULT 0",
|
||||
"admin_addr TEXT NOT NULL DEFAULT ''",
|
||||
"admin_port INTEGER NOT NULL DEFAULT 0",
|
||||
@ -476,7 +483,7 @@ func (s *Store) DeleteLocal(name string) error {
|
||||
// ---- Remotes ----
|
||||
|
||||
func (s *Store) ListRemotes() ([]Remote, error) {
|
||||
rows, err := s.db.Query("SELECT name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, vhost_http_port, admin_addr, admin_port, admin_user, admin_password FROM remotes ORDER BY name")
|
||||
rows, err := s.db.Query("SELECT name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, usr, vhost_http_port, admin_addr, admin_port, admin_user, admin_password FROM remotes ORDER BY name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -485,7 +492,7 @@ func (s *Store) ListRemotes() ([]Remote, error) {
|
||||
for rows.Next() {
|
||||
var r Remote
|
||||
var en, tls int
|
||||
if err := rows.Scan(&r.Name, &r.IP, &r.Port, &r.Token, &r.URL, &en, &r.TransportProtocol, &tls, &r.TransportPool, &r.TransportTLSServerName, &r.VhostHTTPPort, &r.AdminAddr, &r.AdminPort, &r.AdminUser, &r.AdminPassword); err != nil {
|
||||
if err := rows.Scan(&r.Name, &r.IP, &r.Port, &r.Token, &r.URL, &en, &r.TransportProtocol, &tls, &r.TransportPool, &r.TransportTLSServerName, &r.User, &r.VhostHTTPPort, &r.AdminAddr, &r.AdminPort, &r.AdminUser, &r.AdminPassword); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Enabled = en != 0
|
||||
@ -498,8 +505,8 @@ func (s *Store) ListRemotes() ([]Remote, error) {
|
||||
func (s *Store) GetRemote(name string) (Remote, bool) {
|
||||
var r Remote
|
||||
var en, tls int
|
||||
row := s.db.QueryRow("SELECT name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, vhost_http_port, admin_addr, admin_port, admin_user, admin_password FROM remotes WHERE name = ?", name)
|
||||
if err := row.Scan(&r.Name, &r.IP, &r.Port, &r.Token, &r.URL, &en, &r.TransportProtocol, &tls, &r.TransportPool, &r.TransportTLSServerName, &r.VhostHTTPPort, &r.AdminAddr, &r.AdminPort, &r.AdminUser, &r.AdminPassword); err != nil {
|
||||
row := s.db.QueryRow("SELECT name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, usr, vhost_http_port, admin_addr, admin_port, admin_user, admin_password FROM remotes WHERE name = ?", name)
|
||||
if err := row.Scan(&r.Name, &r.IP, &r.Port, &r.Token, &r.URL, &en, &r.TransportProtocol, &tls, &r.TransportPool, &r.TransportTLSServerName, &r.User, &r.VhostHTTPPort, &r.AdminAddr, &r.AdminPort, &r.AdminUser, &r.AdminPassword); err != nil {
|
||||
return Remote{}, false
|
||||
}
|
||||
r.Enabled = en != 0
|
||||
@ -509,9 +516,9 @@ func (s *Store) GetRemote(name string) (Remote, bool) {
|
||||
|
||||
func (s *Store) UpsertRemote(r Remote) error {
|
||||
_, err := s.db.Exec(
|
||||
"INSERT INTO remotes(name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, vhost_http_port, admin_addr, admin_port, admin_user, admin_password) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "+
|
||||
"ON CONFLICT(name) DO UPDATE SET ip=excluded.ip, port=excluded.port, token=excluded.token, url=excluded.url, enabled=excluded.enabled, transport_protocol=excluded.transport_protocol, transport_tls=excluded.transport_tls, transport_pool=excluded.transport_pool, transport_tls_server_name=excluded.transport_tls_server_name, vhost_http_port=excluded.vhost_http_port, admin_addr=excluded.admin_addr, admin_port=excluded.admin_port, admin_user=excluded.admin_user, admin_password=excluded.admin_password",
|
||||
r.Name, r.IP, r.Port, r.Token, r.URL, boolToInt(r.Enabled), r.TransportProtocol, boolToInt(r.TransportTLS), r.TransportPool, r.TransportTLSServerName, r.VhostHTTPPort, r.AdminAddr, r.AdminPort, r.AdminUser, r.AdminPassword,
|
||||
"INSERT INTO remotes(name, ip, port, token, url, enabled, transport_protocol, transport_tls, transport_pool, transport_tls_server_name, usr, vhost_http_port, admin_addr, admin_port, admin_user, admin_password) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "+
|
||||
"ON CONFLICT(name) DO UPDATE SET ip=excluded.ip, port=excluded.port, token=excluded.token, url=excluded.url, enabled=excluded.enabled, transport_protocol=excluded.transport_protocol, transport_tls=excluded.transport_tls, transport_pool=excluded.transport_pool, transport_tls_server_name=excluded.transport_tls_server_name, usr=excluded.usr, vhost_http_port=excluded.vhost_http_port, admin_addr=excluded.admin_addr, admin_port=excluded.admin_port, admin_user=excluded.admin_user, admin_password=excluded.admin_password",
|
||||
r.Name, r.IP, r.Port, r.Token, r.URL, boolToInt(r.Enabled), r.TransportProtocol, boolToInt(r.TransportTLS), r.TransportPool, r.TransportTLSServerName, r.User, r.VhostHTTPPort, r.AdminAddr, r.AdminPort, r.AdminUser, r.AdminPassword,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -58,3 +58,63 @@ func TestReplaceLinksClearsOld(t *testing.T) {
|
||||
t.Fatalf("links = %+v", links)
|
||||
}
|
||||
}
|
||||
|
||||
// `usr` is a reserved SQL keyword in several dialects, so the remotes column is
|
||||
// named `usr`. This guards the mapping in both directions, including the
|
||||
// migrate() path that adds the column to pre-existing databases.
|
||||
func TestRemoteUserRoundTrip(t *testing.T) {
|
||||
st, err := New(filepath.Join(t.TempDir(), "test.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
// A remote written without user (existing installs) must read back empty.
|
||||
if err := st.UpsertRemote(Remote{Name: "plain", IP: "1.2.3.4", Port: 7000, Enabled: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, ok := st.GetRemote("plain"); !ok || got.User != "" {
|
||||
t.Fatalf("plain remote user = %q ok=%v, want empty", got.User, ok)
|
||||
}
|
||||
|
||||
want := Remote{Name: "panel", IP: "103.40.13.126", Port: 59999, Token: "tk", User: "e8f52adfaf0e5c95", Enabled: true}
|
||||
if err := st.UpsertRemote(want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, ok := st.GetRemote("panel")
|
||||
if !ok || got.User != want.User {
|
||||
t.Fatalf("user not persisted: %+v", got)
|
||||
}
|
||||
list, err := st.ListRemotes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var found bool
|
||||
for _, r := range list {
|
||||
if r.Name == "panel" {
|
||||
found = true
|
||||
if r.User != want.User {
|
||||
t.Fatalf("ListRemotes user = %q, want %q", r.User, want.User)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("panel remote missing from ListRemotes")
|
||||
}
|
||||
|
||||
// Updating in place must not drop the column, and clearing it must work.
|
||||
want.Token = "tk2"
|
||||
if err := st.UpsertRemote(want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, _ := st.GetRemote("panel"); got.User != want.User || got.Token != "tk2" {
|
||||
t.Fatalf("upsert lost fields: %+v", got)
|
||||
}
|
||||
want.User = ""
|
||||
if err := st.UpsertRemote(want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, _ := st.GetRemote("panel"); got.User != "" {
|
||||
t.Fatalf("user not cleared: %q", got.User)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user