mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 08:16:37 +08:00
fix: resolve merge conflicts and fix monitoring bugs
- Add missing 'uptime' field to NodeMetricApiItem type definition - Fix WS message handling: non-UpgradeProgress typed messages now broadcast via broadcastInfo instead of being silently dropped - Strengthen looksLikeSystemInfoMessage heuristic to require ≥3 matching keys to avoid false positives - Fix tab/space indentation inconsistency in admin.tsx useEffect - Remove duplicate method declarations from merge (repository_control, mutations) - Update tunnel_entry_sqlite_test to use renamed Tx suffix function
This commit is contained in:
@@ -4165,20 +4165,6 @@ func (h *Handler) validateForwardPortAvailability(node *nodeRecord, port int, cu
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) validateForwardPortAvailabilityTx(tx *gorm.DB, node *nodeRecord, port int, currentForwardID int64) error {
|
||||
if h == nil || h.repo == nil || tx == nil || node == nil || port <= 0 {
|
||||
return nil
|
||||
}
|
||||
occupied, err := h.repo.HasOtherForwardOnNodePortTx(tx, node.ID, port, currentForwardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if occupied {
|
||||
return fmt.Errorf("节点 %s 端口 %d 已被其他转发占用", node.Name, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePortRangeMinMax(input string) (int, int) {
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestValidateTunnelEntryPortConflictsForNewEntriesDoesNotBlockOnSQLiteTx(t *
|
||||
doneCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
errCh <- h.validateTunnelEntryPortConflictsForNewEntries(tx, tunnelID, []int64{oldEntryID}, []int64{oldEntryID, newEntryID})
|
||||
errCh <- h.validateTunnelEntryPortConflictsForNewEntriesTx(tx, tunnelID, []int64{oldEntryID}, []int64{oldEntryID, newEntryID})
|
||||
}()
|
||||
|
||||
select {
|
||||
|
||||
@@ -64,36 +64,6 @@ func (r *Repository) ListForwardsByTunnelTx(tx *gorm.DB, tunnelID int64) ([]mode
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListForwardsByTunnelTx(tx *gorm.DB, tunnelID int64) ([]model.ForwardRecord, error) {
|
||||
if tx == nil {
|
||||
return nil, errors.New("database unavailable")
|
||||
}
|
||||
var forwards []model.Forward
|
||||
err := tx.Where("tunnel_id = ?", tunnelID).Order("id ASC").Find(&forwards).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := make([]model.ForwardRecord, 0, len(forwards))
|
||||
for _, f := range forwards {
|
||||
rows = append(rows, model.ForwardRecord{
|
||||
ID: f.ID,
|
||||
UserID: f.UserID,
|
||||
UserName: f.UserName,
|
||||
Name: f.Name,
|
||||
TunnelID: f.TunnelID,
|
||||
RemoteAddr: f.RemoteAddr,
|
||||
Strategy: f.Strategy,
|
||||
Status: f.Status,
|
||||
SpeedID: f.SpeedID,
|
||||
})
|
||||
}
|
||||
for i := range rows {
|
||||
if strings.TrimSpace(rows[i].Strategy) == "" {
|
||||
rows[i].Strategy = "fifo"
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListActiveTunnelIDsByNode(nodeID int64) ([]int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
@@ -156,25 +126,6 @@ func (r *Repository) ListForwardPortsTx(tx *gorm.DB, forwardID int64) ([]model.F
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListForwardPortsTx(tx *gorm.DB, forwardID int64) ([]model.ForwardPortRecord, error) {
|
||||
if tx == nil {
|
||||
return nil, errors.New("database unavailable")
|
||||
}
|
||||
var ports []model.ForwardPort
|
||||
err := tx.Where("forward_id = ?", forwardID).Order("id ASC").Find(&ports).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := make([]model.ForwardPortRecord, 0, len(ports))
|
||||
for _, p := range ports {
|
||||
inIP := ""
|
||||
if p.InIP.Valid {
|
||||
inIP = p.InIP.String
|
||||
}
|
||||
rows = append(rows, model.ForwardPortRecord{NodeID: p.NodeID, Port: p.Port, InIP: inIP})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *Repository) HasOtherForwardOnNodePort(nodeID int64, port int, currentForwardID int64) (bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
@@ -202,24 +153,6 @@ func (r *Repository) HasOtherForwardOnNodePortTx(tx *gorm.DB, nodeID int64, port
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *Repository) HasOtherForwardOnNodePortTx(tx *gorm.DB, nodeID int64, port int, currentForwardID int64) (bool, error) {
|
||||
if tx == nil {
|
||||
return false, errors.New("database unavailable")
|
||||
}
|
||||
if nodeID <= 0 || port <= 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := tx.Model(&model.ForwardPort{}).
|
||||
Where("node_id = ? AND port = ? AND forward_id <> ?", nodeID, port, currentForwardID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetTunnelOutProtocol(tunnelID int64) (string, error) {
|
||||
if r == nil || r.db == nil {
|
||||
|
||||
@@ -258,10 +258,16 @@ func (s *Server) handleNode(w http.ResponseWriter, r *http.Request, nodeID int64
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if json.Unmarshal([]byte(msg), &parsed) == nil && parsed.Type != "" {
|
||||
if parsed.Type == "UpgradeProgress" {
|
||||
switch parsed.Type {
|
||||
case "UpgradeProgress":
|
||||
s.broadcastTyped(nodeID, "upgrade_progress", msg)
|
||||
continue
|
||||
default:
|
||||
// Unknown typed messages still get broadcast so future
|
||||
// agent message types are not silently lost.
|
||||
s.broadcastInfo(nodeID, msg)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if looksLikeSystemInfoMessage(msg) {
|
||||
@@ -308,9 +314,13 @@ func looksLikeSystemInfoMessage(msg string) bool {
|
||||
"\"load5\"",
|
||||
"\"load15\"",
|
||||
}
|
||||
matched := 0
|
||||
for _, k := range keys {
|
||||
if strings.Contains(msg, k) {
|
||||
return true
|
||||
matched++
|
||||
if matched >= 3 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# PLAN: Release 2.1.8
|
||||
|
||||
## Overview
|
||||
Synchronize with the main branch and release a new tag `2.1.8`.
|
||||
|
||||
## Tasks
|
||||
- [x] Pull latest changes from `main`
|
||||
- [x] Update `AGENTS.md` with new tag and current commit hash (PR #337 created and set to auto-merge)
|
||||
- [x] Create git tag `2.1.8`
|
||||
- [x] Push git tag `2.1.8` to origin
|
||||
@@ -402,6 +402,7 @@ export interface NodeMetricApiItem {
|
||||
load15: number;
|
||||
tcpConns: number;
|
||||
udpConns: number;
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
export interface TunnelMetricApiItem {
|
||||
|
||||
@@ -202,40 +202,40 @@ export default function AdminLayout({
|
||||
setUsername(name);
|
||||
setIsAdmin(adminFlag);
|
||||
|
||||
// Monitor permission is not strictly role-based; non-admin users may be
|
||||
// granted access explicitly. Fetch a lightweight capability flag so we can
|
||||
// avoid a confusing 403 navigation.
|
||||
if (adminFlag) {
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
return;
|
||||
}
|
||||
// Monitor permission is not strictly role-based; non-admin users may be
|
||||
// granted access explicitly. Fetch a lightweight capability flag so we can
|
||||
// avoid a confusing 403 navigation.
|
||||
if (adminFlag) {
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const res = await getMonitorAccess();
|
||||
if (cancelled) return;
|
||||
if (res.code === 0 && res.data) {
|
||||
setMonitorAllowed(Boolean(res.data.allowed));
|
||||
setMonitorAccessReason(
|
||||
res.data.allowed ? null : (res.data.reason || null),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Fail open to preserve legacy navigation behavior.
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
} catch {
|
||||
if (cancelled) return;
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
}
|
||||
})();
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const res = await getMonitorAccess();
|
||||
if (cancelled) return;
|
||||
if (res.code === 0 && res.data) {
|
||||
setMonitorAllowed(Boolean(res.data.allowed));
|
||||
setMonitorAccessReason(
|
||||
res.data.allowed ? null : (res.data.reason || null),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Fail open to preserve legacy navigation behavior.
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
} catch {
|
||||
if (cancelled) return;
|
||||
setMonitorAllowed(true);
|
||||
setMonitorAccessReason(null);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user