mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
de9ab51def
* feat(nodes): add renewal cycle and auto-advance scheduling - Add renewal_cycle field to nodes for tracking paid vs free cycles - Implement auto-advance scheduling when renewal is processed - Add migration test for renewal_cycle column - Update dashboard to show renewal cycle count - Add renewal status display on node detail page * fix(frontend): show rule count in compact view mode for forward pages * style(frontend): format code and improve node card layout Entire-Checkpoint: 08b760b76538 * fix: restore renewal.ts from main to resolve build errors
230 lines
12 KiB
Plaintext
230 lines
12 KiB
Plaintext
Concise audit for issue #276, focused on `vite-frontend/src/pages` and `src/api`.
|
|
|
|
a) What already exists
|
|
|
|
1. Dashboard traffic auto-refresh
|
|
- Dashboard data is loaded via async API calls in a hook, not by page reload:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/dashboard/use-dashboard-data.ts`
|
|
- `useDashboardData()` calls `getUserPackageInfo()` and `getAnnouncement()`.
|
|
- The 24h traffic chart is already rendered from `statisticsFlows`:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/dashboard.tsx`
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/dashboard/components/flow-chart-card.tsx`
|
|
|
|
2. Node status real-time updates
|
|
- This is the strongest existing area.
|
|
- WebSocket realtime hook already exists:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node/use-node-realtime.ts`
|
|
- `useNodeRealtime()`
|
|
- Offline debounce exists:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node/use-node-offline-timers.ts`
|
|
- Node page consumes websocket messages and updates local state without reload:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `handleWebSocketMessage()`
|
|
- `loadNodes()`
|
|
- Realtime also includes system info updates and upgrade progress updates.
|
|
|
|
3. Form submissions without full page reload
|
|
- Broadly already implemented across main CRUD pages using async handlers + modal close + state refresh.
|
|
- Examples:
|
|
- Nodes: `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `handleSubmit()`
|
|
- Tunnels: `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/tunnel.tsx`
|
|
- `handleSubmit()`
|
|
- Forwards: `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward.tsx`
|
|
- `handleSubmit()`
|
|
- Users: `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/user.tsx`
|
|
- `handleSubmitUser()`
|
|
- I did not find page reload patterns in these CRUD pages (`location.reload` / `window.location.reload` absent there).
|
|
|
|
4. Local/partial list refresh after CRUD/toggle
|
|
- Some local optimistic/partial updates already exist:
|
|
- Node delete updates local state directly:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `confirmDelete()`
|
|
- Node edit updates local item directly:
|
|
- `handleSubmit()` edit branch
|
|
- Forward service toggle uses optimistic local update + rollback on failure:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward.tsx`
|
|
- `handleServiceToggle()`
|
|
- Sorting persists locally and updates item `inx` in-memory after success:
|
|
- Node `handleDragEnd()`
|
|
- Tunnel `handleDragEnd()`
|
|
- Forward `handleDragEnd()`
|
|
|
|
5. Batch operation progress UI
|
|
- Node upgrade has actual per-node progress UI:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `upgradeProgress` state
|
|
- websocket `type === "upgrade_progress"` handling in `handleWebSocketMessage()`
|
|
- `<Progress />` render around lines ~1307+
|
|
- Forward batch actions are factored and return structured outcomes:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward/batch-actions.ts`
|
|
|
|
6. Search/filter instant response
|
|
- Instant client-side filtering already exists in:
|
|
- Nodes: `sortedNodes` memo filters by `searchKeyword`
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- Tunnels: `sortedTunnels`
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/tunnel.tsx`
|
|
- Forwards: `orderedForwards`
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward.tsx`
|
|
- Forward also has instant client-side filter dropdowns (`filterUserId`, `filterTunnelId`).
|
|
|
|
b) What is missing or partial
|
|
|
|
1. Dashboard traffic auto-refresh: missing
|
|
- `useDashboardData()` only fetches once on mount.
|
|
- No polling, websocket, refetch trigger, focus refresh, or manual refresh hook.
|
|
- So “AJAX no-refresh” exists only for initial load, not ongoing refresh.
|
|
|
|
2. Node realtime: partial but good
|
|
- Realtime status/system-info exists only for nodes.
|
|
- It reconnects with limited retries (5 attempts) and then stops.
|
|
- `loadNodes()` initially sets `systemInfo: null`, so a full reload can temporarily wipe metrics until WS catches up again.
|
|
- No obvious fallback polling if websocket dies permanently.
|
|
|
|
3. Form submission no full reload: mostly done
|
|
- CRUD forms are already async and modal-driven.
|
|
- But post-submit refresh style is inconsistent:
|
|
- some do local patch,
|
|
- many do full page-level list refetch (`loadData()`, `loadUsers()`, `loadNodes()`).
|
|
|
|
4. Local/partial list refresh after CRUD/toggle: partial/inconsistent
|
|
- Forward toggle is good and local.
|
|
- Node edit/delete are relatively local.
|
|
- But many operations still refetch the whole page dataset:
|
|
- Tunnel create/update/delete -> `loadData()`
|
|
- Forward create/update/delete -> `loadData()`
|
|
- User create/update/delete -> `loadUsers()`
|
|
- User tunnel assignment/update/remove -> `loadUserTunnels()`
|
|
- This is AJAX and avoids full browser reload, but not “partial/local refresh” in the stronger UX sense.
|
|
|
|
5. Batch operation progress UI: largely missing outside node upgrade
|
|
- Tunnel batch delete/redeploy: only loading spinner/toast + full reload, no per-item progress/result panel.
|
|
- Forward batch actions: summarized via toast and optional refresh, but no visible progress timeline/progress list.
|
|
- User batch tunnel assignment: no progress UI beyond loading state.
|
|
|
|
6. Search/filter instant response: mixed
|
|
- Nodes/tunnels/forwards are instant client-side.
|
|
- Users are not fully instant:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/user.tsx`
|
|
- `loadUsers()` is server-driven and tied to `useEffect([pagination.current, pagination.size, searchKeyword])`
|
|
- Enter key also manually calls `handleSearch()`
|
|
- So user search is AJAX but not purely local/instant in the same way.
|
|
|
|
c) Specific file paths and functions to touch for an implementation plan
|
|
|
|
Primary targets
|
|
|
|
1. Dashboard auto-refresh
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/dashboard/use-dashboard-data.ts`
|
|
- `useDashboardData()`
|
|
- extract `loadPackageData()` / `loadAnnouncement()` into stable callbacks
|
|
- add polling or visibility/focus-based refetch
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/dashboard.tsx`
|
|
- if adding manual refresh indicator/button, this is the page entry
|
|
- API source already exists:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/api/index.ts`
|
|
- `getUserPackageInfo()`, `getAnnouncement()`
|
|
|
|
2. Node realtime hardening / fallback
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node/use-node-realtime.ts`
|
|
- `useNodeRealtime()`
|
|
- retry policy, reconnect strategy, possible fallback refresh trigger
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `handleWebSocketMessage()`
|
|
- `loadNodes()`
|
|
- `handleSubmit()`
|
|
- `confirmDelete()`
|
|
- `handleBatchDelete()`
|
|
|
|
3. Replace whole-list reloads with local/partial updates
|
|
- Tunnels:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/tunnel.tsx`
|
|
- `loadData()`
|
|
- `handleSubmit()`
|
|
- `confirmDelete()`
|
|
- `handleBatchDelete()`
|
|
- `handleBatchRedeploy()`
|
|
- Forwards:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward.tsx`
|
|
- `loadData()`
|
|
- `handleSubmit()`
|
|
- `confirmDelete()`
|
|
- `handleServiceToggle()`
|
|
- `handleBatchDelete()`
|
|
- `handleBatchToggleService()`
|
|
- `handleBatchRedeploy()`
|
|
- `handleBatchChangeTunnel()`
|
|
- Users:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/user.tsx`
|
|
- `loadUsers()`
|
|
- `handleSubmitUser()`
|
|
- `handleConfirmDelete()`
|
|
- `loadUserTunnels()`
|
|
- `handleBatchAssignTunnel()`
|
|
- `handleUpdateTunnel()`
|
|
- `handleConfirmRemoveTunnel()`
|
|
- `handleConfirmResetFlow()`
|
|
- `handleConfirmResetTunnelFlow()`
|
|
|
|
4. Batch progress/result UX
|
|
- Nodes already have a model to copy:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx`
|
|
- `upgradeProgress`
|
|
- websocket `upgrade_progress`
|
|
- Forward batch abstraction:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/forward/batch-actions.ts`
|
|
- likely extend return shape if backend can expose richer progress/events
|
|
- API endpoints involved:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/api/index.ts`
|
|
- `batchDeleteForwards()`
|
|
- `batchPauseForwards()`
|
|
- `batchResumeForwards()`
|
|
- `batchRedeployForwards()`
|
|
- `batchChangeTunnel()`
|
|
- `batchDeleteTunnels()`
|
|
- `batchRedeployTunnels()`
|
|
- `batchDeleteNodes()`
|
|
- `batchUpgradeNodes()`
|
|
|
|
5. Search/filter behavior normalization
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/user.tsx`
|
|
- `loadUsers()`
|
|
- `handleSearch()`
|
|
- search input around lines ~718+
|
|
- Nodes/tunnels/forwards already have memo-based client filtering and are the pattern to follow.
|
|
|
|
d) Notable risks / edge cases
|
|
|
|
1. Dashboard polling could spam toasts
|
|
- `checkExpirationNotifications()` runs inside package load flow.
|
|
- If polling is added naively, expiration toasts could repeat or localStorage notification keys could churn unexpectedly.
|
|
|
|
2. Dashboard chart semantics
|
|
- `statisticsFlows` appears to be a snapshot list, not a stream API.
|
|
- Frequent polling may redraw aggressively and may need dedupe/staleness handling.
|
|
|
|
3. Node websocket auth/security
|
|
- Realtime URL sends token in query string (`secret=${getToken()}`) in:
|
|
- `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node/use-node-realtime.ts`
|
|
- Any changes should be careful not to break backend expectations.
|
|
|
|
4. Realtime/local state merge complexity
|
|
- `loadNodes()` normalizes nodes and clears `systemInfo`.
|
|
- If partial refetch is introduced, merging fetched node metadata with live websocket state must avoid wiping online metrics/progress.
|
|
|
|
5. Batch APIs may not provide granular progress
|
|
- Current batch helpers mostly return summary counts, not per-item progress.
|
|
- True progress UI may require backend support, not only frontend work.
|
|
|
|
6. Forward toggle consistency
|
|
- `handleServiceToggle()` optimistically flips `serviceRunning` and later patches `status`.
|
|
- If backend returns partial failure or delayed actual state, UI may drift until a refresh.
|
|
|
|
7. User search behavior currently depends on backend pagination/search
|
|
- Moving to fully instant local filtering may conflict with server-side pagination semantics and large datasets.
|
|
|
|
Bottom line
|
|
- Already present: async modal CRUD, no browser reloads, strong node realtime, optimistic forward toggle, instant search/filter on node/tunnel/forward pages.
|
|
- Missing/partial for #276: dashboard traffic auto-refresh, consistent partial/local state updates after CRUD, batch progress UI outside node upgrade, and more unified instant-search UX on the user page. |