mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
chore: release 2.1.9-beta7
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# PROJECT KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Thu Mar 19 2026
|
||||
**Commit:** 6458b5a
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-alpha5
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
FLVX (formerly Flux Panel) is a traffic forwarding management system built on a forked GOST v3 stack. It ships as a Go-based admin API (SQLite/PostgreSQL) + Vite/React UI + Go forwarding agent, with optional mobile WebView wrappers.
|
||||
|
||||
@@ -33,13 +33,13 @@ curl -L https://raw.githubusercontent.com/Sagit-chu/flux-panel/main/install.sh -
|
||||
#### 安装特定版本
|
||||
从 [Releases](https://github.com/Sagit-chu/flux-panel/releases) 页面复制对应版本的安装命令,脚本会自动安装该版本而非最新版。
|
||||
|
||||
面板端(以 2.1.0 为例):
|
||||
面板端(以 2.1.9-beta6 为例):
|
||||
```bash
|
||||
curl -L https://github.com/Sagit-chu/flux-panel/releases/download/2.1.0/panel_install.sh -o panel_install.sh && chmod +x panel_install.sh && ./panel_install.sh
|
||||
curl -L https://github.com/Sagit-chu/flux-panel/releases/download/2.1.9-beta6/panel_install.sh -o panel_install.sh && chmod +x panel_install.sh && ./panel_install.sh
|
||||
```
|
||||
节点端(以 2.1.0 为例):
|
||||
节点端(以 2.1.9-beta6 为例):
|
||||
```bash
|
||||
curl -L https://github.com/Sagit-chu/flux-panel/releases/download/2.1.0/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
curl -L https://github.com/Sagit-chu/flux-panel/releases/download/2.1.9-beta6/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
#### PostgreSQL 部署(Docker Compose)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO BACKEND KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Go-based Admin API for FLVX. Replaced legacy Spring Boot backend.
|
||||
**Stack:** Go 1.24, net/http (std lib), GORM + SQLite/PostgreSQL (glebarez/sqlite - CGO-free).
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# BACKEND HTTP HANDLER KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Thu Feb 26 2026
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
HTTP request handlers for FLVX Admin API. Core business logic layer.
|
||||
|
||||
@@ -695,6 +695,30 @@ func (h *Handler) monitorServiceResultsHandler(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
// If start/end time range is provided, use time-based query (mirrors node metrics / tunnel quality pattern).
|
||||
startStr := r.URL.Query().Get("start")
|
||||
endStr := r.URL.Query().Get("end")
|
||||
if startStr != "" && endStr != "" {
|
||||
startMs, err1 := strconv.ParseInt(startStr, 10, 64)
|
||||
endMs, err2 := strconv.ParseInt(endStr, 10, 64)
|
||||
if err1 != nil || err2 != nil || startMs <= 0 || endMs <= 0 || endMs < startMs {
|
||||
response.WriteJSON(w, response.ErrDefault("无效的时间范围"))
|
||||
return
|
||||
}
|
||||
if endMs-startMs > maxMetricsRangeMs {
|
||||
response.WriteJSON(w, response.ErrDefault("时间范围过大"))
|
||||
return
|
||||
}
|
||||
results, err := h.repo.GetServiceMonitorResultsByTimeRange(monitorID, startMs, endMs)
|
||||
if err != nil {
|
||||
response.WriteJSON(w, response.Err(-2, err.Error()))
|
||||
return
|
||||
}
|
||||
response.WriteJSON(w, response.OK(results))
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback: count-based limit query (backward compat).
|
||||
limit := 100
|
||||
if l := r.URL.Query().Get("limit"); l != "" {
|
||||
if v, err := strconv.Atoi(l); err == nil && v > 0 && v <= 1000 {
|
||||
|
||||
@@ -3568,6 +3568,25 @@ func (r *Repository) GetServiceMonitorResults(monitorID int64, limit int) ([]mod
|
||||
return results, err
|
||||
}
|
||||
|
||||
// GetServiceMonitorResultsByTimeRange returns results for a monitor within [startMs, endMs].
|
||||
// Mirrors GetNodeMetrics / GetTunnelMetrics pattern for time-range based charting.
|
||||
func (r *Repository) GetServiceMonitorResultsByTimeRange(monitorID int64, startMs, endMs int64) ([]model.ServiceMonitorResult, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var results []model.ServiceMonitorResult
|
||||
err := r.db.Where("monitor_id = ? AND timestamp >= ? AND timestamp <= ?", monitorID, startMs, endMs).
|
||||
Order("timestamp DESC").
|
||||
Limit(5000).
|
||||
Find(&results).Error
|
||||
if len(results) > 1 {
|
||||
for i, j := 0, len(results)-1; i < j; i, j = i+1, j-1 {
|
||||
results[i], results[j] = results[j], results[i]
|
||||
}
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
|
||||
// GetLatestServiceMonitorResults returns the newest result per monitor_id.
|
||||
// This is intended for list rendering (avoid N+1 queries).
|
||||
func (r *Repository) GetLatestServiceMonitorResults() ([]model.ServiceMonitorResult, error) {
|
||||
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
# GO-GOST SERVICE KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Thu Feb 26 2026
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Forwarding agent built on GOST v3 with a local fork of `github.com/go-gost/x` under `x/`.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Local fork of `github.com/go-gost/x` used by `go-gost/` via `replace github.com/go-gost/x => ./x`. Most protocol/runtime behavior changes happen here. 30+ top-level packages - framework-style layout.
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X API KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Gin-based management API for reading/writing config and controlling services at runtime.
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X CONFIG KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Config model + parsing/loading pipeline for the `go-gost/x` runtime. This is the bridge between `gost.json`/`gost.yaml` and in-memory registries/services.
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# GOST CONNECTOR KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Feb 13 2026
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Connection initiators (clients) for various protocols in GOST forwarding.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X DIALERS KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Outbound dialers (client-side connection establishment) used by connectors/handlers.
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X HANDLERS KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Protocol handlers (server-side request handling) used by services defined in the GOST config.
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# GO-GOST/X LISTENERS KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Inbound listeners (transport-level accept loops) used by services defined in the GOST config.
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# GO-GOST REGISTRY KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Wed Feb 04 2026
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Central registration point for all pluggable GOST components (handlers, listeners, dialers, etc.).
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# GOST SOCKET KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Sun Feb 15 2026
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
WebSocket reporter and socket utilities for panel integration.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Plan 058: Sync Main and Push Tag
|
||||
|
||||
Sync current changes to `main`, merge to `master` (if needed) or just PR/merge on `main` (if that's the flow), and push a new tag `2.1.9-beta7`.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] Determine the next tag version (`2.1.9-beta7`).
|
||||
- [ ] Create a feature branch `release/2.1.9-beta7`.
|
||||
- [ ] Commit all current changes to the feature branch.
|
||||
- [ ] Push the feature branch to remote.
|
||||
- [ ] Create a PR to `main`.
|
||||
- [ ] Merge the PR to `main`.
|
||||
- [ ] Create and push tag `2.1.9-beta7`.
|
||||
@@ -1,9 +1,9 @@
|
||||
# VITE FRONTEND KNOWLEDGE BASE
|
||||
|
||||
**Generated:** Thu Feb 26 2026
|
||||
**Commit:** 21008cc
|
||||
**Generated:** Fri Mar 20 2026
|
||||
**Commit:** f45f960
|
||||
**Branch:** main
|
||||
**Tag:** 2.1.5-rc15
|
||||
**Tag:** 2.1.9-beta6
|
||||
|
||||
## OVERVIEW
|
||||
Web management console for FLVX.
|
||||
|
||||
@@ -486,11 +486,22 @@ export const updateServiceMonitor = (data: ServiceMonitorMutationPayload) =>
|
||||
export const deleteServiceMonitor = (id: number) =>
|
||||
Network.post("/monitor/services/delete", { id });
|
||||
|
||||
export const getServiceMonitorResults = (monitorId: number, limit = 100) =>
|
||||
Network.get<ServiceMonitorResultApiItem[]>(
|
||||
export const getServiceMonitorResults = (
|
||||
monitorId: number,
|
||||
options?: { limit?: number; start?: number; end?: number },
|
||||
) => {
|
||||
const params: Record<string, string> = {};
|
||||
if (options?.start != null && options?.end != null) {
|
||||
params.start = String(options.start);
|
||||
params.end = String(options.end);
|
||||
} else if (options?.limit != null) {
|
||||
params.limit = String(options.limit);
|
||||
}
|
||||
return Network.get<ServiceMonitorResultApiItem[]>(
|
||||
`/monitor/services/${monitorId}/results`,
|
||||
{ limit: String(limit) },
|
||||
params,
|
||||
);
|
||||
};
|
||||
|
||||
export const getServiceMonitorLatestResults = () =>
|
||||
Network.get<ServiceMonitorResultApiItem[]>(
|
||||
|
||||
@@ -314,7 +314,7 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
});
|
||||
const [submitLoading, setSubmitLoading] = useState(false);
|
||||
const [activeServiceMonitorId, setActiveServiceMonitorId] = useState<number | null>(null);
|
||||
const [serviceMonitorRangeLimit, setServiceMonitorRangeLimit] = useState(50);
|
||||
const [serviceMonitorRangeMs, setServiceMonitorRangeMs] = useState(60 * 60 * 1000);
|
||||
|
||||
const [accessDenied, setAccessDenied] = useState<string | null>(null);
|
||||
const [resultsModalOpen, setResultsModalOpen] = useState(false);
|
||||
@@ -526,9 +526,12 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
}, []);
|
||||
|
||||
const loadMonitorResults = useCallback(
|
||||
async (monitorId: number, limit = 20) => {
|
||||
async (monitorId: number, options?: { rangeMs?: number; limit?: number }) => {
|
||||
try {
|
||||
const response = await getServiceMonitorResults(monitorId, limit);
|
||||
const apiOptions = options?.rangeMs != null
|
||||
? { start: Date.now() - options.rangeMs, end: Date.now() }
|
||||
: { limit: options?.limit ?? 100 };
|
||||
const response = await getServiceMonitorResults(monitorId, apiOptions);
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
setMonitorResults((prev) => ({
|
||||
@@ -600,7 +603,7 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
if (!resultsMonitorId) return;
|
||||
setResultsLoading(true);
|
||||
try {
|
||||
await loadMonitorResults(resultsMonitorId, resultsLimit);
|
||||
await loadMonitorResults(resultsMonitorId, { limit: resultsLimit });
|
||||
} finally {
|
||||
setResultsLoading(false);
|
||||
}
|
||||
@@ -641,11 +644,11 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
void loadResultsForModal();
|
||||
}, [resultsModalOpen, resultsMonitorId, resultsLimit, loadResultsForModal]);
|
||||
|
||||
// Reload results for the active service monitor chart when range limit changes
|
||||
// Reload results for the active service monitor chart when time range changes
|
||||
useEffect(() => {
|
||||
if (!activeServiceMonitorId) return;
|
||||
void loadMonitorResults(activeServiceMonitorId, serviceMonitorRangeLimit);
|
||||
}, [activeServiceMonitorId, serviceMonitorRangeLimit, loadMonitorResults]);
|
||||
void loadMonitorResults(activeServiceMonitorId, { rangeMs: serviceMonitorRangeMs });
|
||||
}, [activeServiceMonitorId, serviceMonitorRangeMs, loadMonitorResults]);
|
||||
|
||||
const chartData = metrics.map((m) => ({
|
||||
time: formatTimestamp(m.timestamp, metricsRangeMs),
|
||||
@@ -1322,10 +1325,8 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
const activeStale = resolvedActiveMonitor ? isResultStale(resolvedActiveMonitor, activeLatestResult) : false;
|
||||
const activeResults = resolvedActiveMonitorId != null ? (monitorResults[resolvedActiveMonitorId] || []) : [];
|
||||
const activeLatencyData = [...activeResults]
|
||||
.slice(0, serviceMonitorRangeLimit)
|
||||
.reverse()
|
||||
.map((r) => ({
|
||||
time: formatTimestamp(r.timestamp),
|
||||
time: formatTimestamp(r.timestamp, serviceMonitorRangeMs),
|
||||
latency: r.success === 1 ? r.latencyMs : null,
|
||||
success: r.success,
|
||||
}));
|
||||
@@ -1336,24 +1337,23 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
<h3 className="text-lg font-semibold">服务监控图表</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
className="w-28"
|
||||
selectedKeys={[String(serviceMonitorRangeLimit)]}
|
||||
className="w-36"
|
||||
selectedKeys={[String(serviceMonitorRangeMs)]}
|
||||
onSelectionChange={(keys) => {
|
||||
const v = Number(Array.from(keys)[0]);
|
||||
if (v > 0) setServiceMonitorRangeLimit(v);
|
||||
if (v > 0) setServiceMonitorRangeMs(v);
|
||||
}}
|
||||
>
|
||||
<SelectItem key="20">20条</SelectItem>
|
||||
<SelectItem key="50">50条</SelectItem>
|
||||
<SelectItem key="100">100条</SelectItem>
|
||||
<SelectItem key="200">200条</SelectItem>
|
||||
<SelectItem key={String(60 * 60 * 1000)}>1小时</SelectItem>
|
||||
<SelectItem key={String(6 * 60 * 60 * 1000)}>6小时</SelectItem>
|
||||
<SelectItem key={String(24 * 60 * 60 * 1000)}>24小时</SelectItem>
|
||||
</Select>
|
||||
{resolvedActiveMonitorId != null && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="flat"
|
||||
onPress={() => {
|
||||
void loadMonitorResults(resolvedActiveMonitorId, serviceMonitorRangeLimit);
|
||||
void loadMonitorResults(resolvedActiveMonitorId, { rangeMs: serviceMonitorRangeMs });
|
||||
}}
|
||||
>
|
||||
<RefreshCw className="w-4 h-4 mr-1" />
|
||||
@@ -1391,7 +1391,7 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
|
||||
setActiveServiceMonitorId(monitor.id);
|
||||
// Load results for this monitor if not loaded
|
||||
if (!monitorResults[monitor.id] || monitorResults[monitor.id].length <= 1) {
|
||||
void loadMonitorResults(monitor.id, serviceMonitorRangeLimit);
|
||||
void loadMonitorResults(monitor.id, { rangeMs: serviceMonitorRangeMs });
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user