\n {node.expiryTime &&\n node.expiryTime > 0 &&\n node.renewalCycle && (\n
{\n Line 112: export const formatNodeRenewalTime = (timestamp?: number): string => {",
+ "title": "formatNodeRenewalTime|getNodeRenewalCycleLabel",
+ "metadata": {
+ "matches": 7,
+ "truncated": false
+ },
+ "time": {
+ "start": 1773025465051,
+ "end": 1773025465075
+ }
+ },
+ "id": "prt_cd08d92d100115XxWcwAxZnk7F",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d652f001t28GCHihEM8wQb"
+ },
+ {
+ "type": "step-finish",
+ "reason": "tool-calls",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "cost": 0,
+ "tokens": {
+ "total": 27143,
+ "input": 1109,
+ "output": 242,
+ "reasoning": 0,
+ "cache": {
+ "read": 25792,
+ "write": 0
+ }
+ },
+ "id": "prt_cd08d93260015nDMM3wGHB9q3r",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d652f001t28GCHihEM8wQb"
+ }
+ ]
+ },
+ {
+ "info": {
+ "role": "assistant",
+ "time": {
+ "created": 1773025465369,
+ "completed": 1773025480260
+ },
+ "parentID": "msg_cd08cf2e5001UoNixpQwGQiGYH",
+ "modelID": "glm-5",
+ "providerID": "zai-coding-plan",
+ "mode": "build",
+ "agent": "build",
+ "path": {
+ "cwd": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon",
+ "root": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon"
+ },
+ "cost": 0,
+ "tokens": {
+ "total": 28285,
+ "input": 1216,
+ "output": 189,
+ "reasoning": 0,
+ "cache": {
+ "read": 26880,
+ "write": 0
+ }
+ },
+ "finish": "tool-calls",
+ "id": "msg_cd08d9419001W1KMqd4fxHLjwr",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO"
+ },
+ "parts": [
+ {
+ "type": "step-start",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "id": "prt_cd08db8c9001HAo9TiY2OkrFUG",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d9419001W1KMqd4fxHLjwr"
+ },
+ {
+ "type": "reasoning",
+ "text": "现在我看到问题了。这些函数定义在 `src/pages/node/renewal.ts` 中,在 `dashboard.tsx` 中被导入了,但在 `node.tsx` 中没有导入。\n\n让我查看 node.tsx 的导入部分,看看是否需要添加这些导入。",
+ "time": {
+ "start": 1773025474762,
+ "end": 1773025480025
+ },
+ "id": "prt_cd08db8ca0013Q73bTEstsiLfx",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d9419001W1KMqd4fxHLjwr"
+ },
+ {
+ "type": "tool",
+ "callID": "call_6d4a266590e848ee98342331",
+ "tool": "grep",
+ "state": {
+ "status": "completed",
+ "input": {
+ "path": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx",
+ "pattern": "^import.*renewal"
+ },
+ "output": "No files found",
+ "title": "^import.*renewal",
+ "metadata": {
+ "matches": 0,
+ "truncated": false
+ },
+ "time": {
+ "start": 1773025478488,
+ "end": 1773025478501
+ }
+ },
+ "id": "prt_cd08dc74f0015IGG02WAO3X5xP",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d9419001W1KMqd4fxHLjwr"
+ },
+ {
+ "type": "tool",
+ "callID": "call_dad61f576a1747c78910d119",
+ "tool": "read",
+ "state": {
+ "status": "completed",
+ "input": {
+ "filePath": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx",
+ "limit": 50
+ },
+ "output": "
/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx\n
file\n
1: import { useState, useEffect, useMemo, useCallback } from \"react\";\n2: import toast from \"react-hot-toast\";\n3: import {\n4: DndContext,\n5: KeyboardSensor,\n6: MouseSensor,\n7: TouchSensor,\n8: type DragEndEvent,\n9: useSensor,\n10: useSensors,\n11: } from \"@dnd-kit/core\";\n12: import {\n13: SortableContext,\n14: arrayMove,\n15: rectSortingStrategy,\n16: sortableKeyboardCoordinates,\n17: useSortable,\n18: } from \"@dnd-kit/sortable\";\n19: import { CSS } from \"@dnd-kit/utilities\";\n20: \n21: import { SearchBar } from \"@/components/search-bar\";\n22: import { AnimatedPage } from \"@/components/animated-page\";\n23: import { Card, CardBody, CardHeader } from \"@/shadcn-bridge/heroui/card\";\n24: import { Button } from \"@/shadcn-bridge/heroui/button\";\n25: import { Input } from \"@/shadcn-bridge/heroui/input\";\n26: import { Textarea } from \"@/shadcn-bridge/heroui/input\";\n27: import {\n28: Modal,\n29: ModalContent,\n30: ModalHeader,\n31: ModalBody,\n32: ModalFooter,\n33: } from \"@/shadcn-bridge/heroui/modal\";\n34: import { Chip } from \"@/shadcn-bridge/heroui/chip\";\n35: import { Switch } from \"@/shadcn-bridge/heroui/switch\";\n36: import { Spinner } from \"@/shadcn-bridge/heroui/spinner\";\n37: import { Alert } from \"@/shadcn-bridge/heroui/alert\";\n38: import { Progress } from \"@/shadcn-bridge/heroui/progress\";\n39: import { Accordion, AccordionItem } from \"@/shadcn-bridge/heroui/accordion\";\n40: import { Select, SelectItem } from \"@/shadcn-bridge/heroui/select\";\n41: import { Checkbox } from \"@/shadcn-bridge/heroui/checkbox\";\n42: import {\n43: createNode,\n44: getNodeList,\n45: updateNode,\n46: deleteNode,\n47: getNodeInstallCommand,\n48: updateNodeOrder,\n49: batchDeleteNodes,\n50: upgradeNode,\n\n(Showing lines 1-50 of 2562. Use offset=51 to continue.)\n",
+ "title": "vite-frontend/src/pages/node.tsx",
+ "metadata": {
+ "preview": "import { useState, useEffect, useMemo, useCallback } from \"react\";\nimport toast from \"react-hot-toast\";\nimport {\n DndContext,\n KeyboardSensor,\n MouseSensor,\n TouchSensor,\n type DragEndEvent,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n arrayMove,\n rectSortingStrategy,\n sortableKeyboardCoordinates,\n useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\n",
+ "truncated": true,
+ "loaded": []
+ },
+ "time": {
+ "start": 1773025479979,
+ "end": 1773025479993
+ }
+ },
+ "id": "prt_cd08dcd26001v98AsU5mk5vFIQ",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d9419001W1KMqd4fxHLjwr"
+ },
+ {
+ "type": "step-finish",
+ "reason": "tool-calls",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "cost": 0,
+ "tokens": {
+ "total": 28285,
+ "input": 1216,
+ "output": 189,
+ "reasoning": 0,
+ "cache": {
+ "read": 26880,
+ "write": 0
+ }
+ },
+ "id": "prt_cd08dcd5b0016QnDhsaeG5207I",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08d9419001W1KMqd4fxHLjwr"
+ }
+ ]
+ },
+ {
+ "info": {
+ "role": "assistant",
+ "time": {
+ "created": 1773025480262,
+ "completed": 1773025489386
+ },
+ "parentID": "msg_cd08cf2e5001UoNixpQwGQiGYH",
+ "modelID": "glm-5",
+ "providerID": "zai-coding-plan",
+ "mode": "build",
+ "agent": "build",
+ "path": {
+ "cwd": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon",
+ "root": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon"
+ },
+ "cost": 0,
+ "tokens": {
+ "total": 28983,
+ "input": 854,
+ "output": 97,
+ "reasoning": 0,
+ "cache": {
+ "read": 28032,
+ "write": 0
+ }
+ },
+ "finish": "tool-calls",
+ "id": "msg_cd08dce46001PNlok5YsEpEpN3",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO"
+ },
+ "parts": [
+ {
+ "type": "step-start",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "id": "prt_cd08de304001hbv952WnFPc2qU",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08dce46001PNlok5YsEpEpN3"
+ },
+ {
+ "type": "reasoning",
+ "text": "我需要查看更多的 import 部分,看看 renewal 相关的函数是否已经从别处导入。让我继续看更多的导入部分。",
+ "time": {
+ "start": 1773025485572,
+ "end": 1773025489133
+ },
+ "id": "prt_cd08de304002nO5Vzi6wUyzjDU",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08dce46001PNlok5YsEpEpN3"
+ },
+ {
+ "type": "tool",
+ "callID": "call_96761b75543745dcadd29398",
+ "tool": "read",
+ "state": {
+ "status": "completed",
+ "input": {
+ "filePath": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx",
+ "limit": 80,
+ "offset": 50
+ },
+ "output": "
/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx\n
file\n
50: upgradeNode,\n51: batchUpgradeNodes,\n52: getNodeReleases,\n53: rollbackNode,\n54: type ReleaseChannel,\n55: } from \"@/api\";\n56: import { PageEmptyState, PageLoadingState } from \"@/components/page-state\";\n57: import {\n58: getConnectionStatusMeta,\n59: getRemoteSyncErrorMessage,\n60: } from \"@/pages/node/display\";\n61: import { tryCopyInstallCommand } from \"@/pages/node/install-command\";\n62: import {\n63: getNodeRenewalSnapshot,\n64: type NodeRenewalCycle,\n65: } from \"@/pages/node/renewal\";\n66: import { buildNodeSystemInfo } from \"@/pages/node/system-info\";\n67: import { useNodeOfflineTimers } from \"@/pages/node/use-node-offline-timers\";\n68: import { useNodeRealtime } from \"@/pages/node/use-node-realtime\";\n69: import { useLocalStorageState } from \"@/hooks/use-local-storage-state\";\n70: import { loadStoredOrder, saveOrder } from \"@/utils/order-storage\";\n71: \n72: const NODE_FALLBACK_REFRESH_INTERVAL_MS = 15000;\n73: \n74: interface Node {\n75: id: number;\n76: inx?: number;\n77: name: string;\n78: remark?: string;\n79: tags?: string;\n80: expiryTime?: number;\n81: renewalCycle?: NodeRenewalCycle;\n82: ip: string;\n83: serverIp: string;\n84: serverIpV4?: string;\n85: serverIpV6?: string;\n86: port: string;\n87: tcpListenAddr?: string;\n88: udpListenAddr?: string;\n89: extraIPs?: string;\n90: version?: string;\n91: http?: number; // 0 关 1 开\n92: tls?: number; // 0 关 1 开\n93: socks?: number; // 0 关 1 开\n94: status: number;\n95: isRemote?: number;\n96: remoteUrl?: string;\n97: syncError?: string;\n98: connectionStatus: \"online\" | \"offline\";\n99: systemInfo?: {\n100: cpuUsage: number;\n101: memoryUsage: number;\n102: uploadTraffic: number;\n103: downloadTraffic: number;\n104: uploadSpeed: number;\n105: downloadSpeed: number;\n106: uptime: number;\n107: } | null;\n108: copyLoading?: boolean;\n109: upgradeLoading?: boolean;\n110: rollbackLoading?: boolean;\n111: }\n112: \n113: interface NodeForm {\n114: id: number | null;\n115: name: string;\n116: remark: string;\n117: tags: string;\n118: expiryTime: number;\n119: renewalCycle: NodeRenewalCycle;\n120: serverHost: string;\n121: serverIpV4: string;\n122: serverIpV6: string;\n123: port: string;\n124: tcpListenAddr: string;\n125: udpListenAddr: string;\n126: interfaceName: string;\n127: extraIPs: string;\n128: http: number; // 0 关 1 开\n129: tls: number; // 0 关 1 开\n\n(Showing lines 50-129 of 2562. Use offset=130 to continue.)\n",
+ "title": "vite-frontend/src/pages/node.tsx",
+ "metadata": {
+ "preview": " upgradeNode,\n batchUpgradeNodes,\n getNodeReleases,\n rollbackNode,\n type ReleaseChannel,\n} from \"@/api\";\nimport { PageEmptyState, PageLoadingState } from \"@/components/page-state\";\nimport {\n getConnectionStatusMeta,\n getRemoteSyncErrorMessage,\n} from \"@/pages/node/display\";\nimport { tryCopyInstallCommand } from \"@/pages/node/install-command\";\nimport {\n getNodeRenewalSnapshot,\n type NodeRenewalCycle,\n} from \"@/pages/node/renewal\";\nimport { buildNodeSystemInfo } from \"@/pages/node/system-info\";\nimport { useNodeOfflineTimers } from \"@/pages/node/use-node-offline-timers\";\nimport { useNodeRealtime } from \"@/pages/node/use-node-realtime\";\nimport { useLocalStorageState } from \"@/hooks/use-local-storage-state\";",
+ "truncated": true,
+ "loaded": []
+ },
+ "time": {
+ "start": 1773025489051,
+ "end": 1773025489057
+ }
+ },
+ "id": "prt_cd08df096001WGhezq15rE7jyG",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08dce46001PNlok5YsEpEpN3"
+ },
+ {
+ "type": "step-finish",
+ "reason": "tool-calls",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "cost": 0,
+ "tokens": {
+ "total": 28983,
+ "input": 854,
+ "output": 97,
+ "reasoning": 0,
+ "cache": {
+ "read": 28032,
+ "write": 0
+ }
+ },
+ "id": "prt_cd08df0ef001SBUPLu9UUbilho",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08dce46001PNlok5YsEpEpN3"
+ }
+ ]
+ },
+ {
+ "info": {
+ "role": "assistant",
+ "time": {
+ "created": 1773025489388,
+ "completed": 1773025500253
+ },
+ "parentID": "msg_cd08cf2e5001UoNixpQwGQiGYH",
+ "modelID": "glm-5",
+ "providerID": "zai-coding-plan",
+ "mode": "build",
+ "agent": "build",
+ "path": {
+ "cwd": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon",
+ "root": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon"
+ },
+ "cost": 0,
+ "tokens": {
+ "total": 30011,
+ "input": 934,
+ "output": 213,
+ "reasoning": 0,
+ "cache": {
+ "read": 28864,
+ "write": 0
+ }
+ },
+ "finish": "tool-calls",
+ "id": "msg_cd08df1ec0016p8ElQV7HboEkQ",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO"
+ },
+ "parts": [
+ {
+ "type": "step-start",
+ "snapshot": "0ac8bcf194b1d53f2d11f5b7c097711d68a623bc",
+ "id": "prt_cd08e057b001QV4ndtT3PWa23R",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08df1ec0016p8ElQV7HboEkQ"
+ },
+ {
+ "type": "reasoning",
+ "text": "好的,我看到了。在第 62-65 行,node.tsx 从 `@/pages/node/renewal` 导入了 `getNodeRenewalSnapshot` 和 `type NodeRenewalCycle`,但没有导入 `formatNodeRenewalTime` 和 `getNodeRenewalCycleLabel`。\n\n我需要修改这个导入,添加这两个函数。",
+ "time": {
+ "start": 1773025494395,
+ "end": 1773025499976
+ },
+ "id": "prt_cd08e057b002Di46cCubns5awm",
+ "sessionID": "ses_32f7e3b05ffeK8IxNvZAZISGWO",
+ "messageID": "msg_cd08df1ec0016p8ElQV7HboEkQ"
+ },
+ {
+ "type": "tool",
+ "callID": "call_d4633a42dfa7434aa777f2cf",
+ "tool": "edit",
+ "state": {
+ "status": "completed",
+ "input": {
+ "filePath": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx",
+ "oldString": "import {\n getNodeRenewalSnapshot,\n type NodeRenewalCycle,\n} from \"@/pages/node/renewal\";",
+ "newString": "import {\n getNodeRenewalSnapshot,\n formatNodeRenewalTime,\n getNodeRenewalCycleLabel,\n type NodeRenewalCycle,\n} from \"@/pages/node/renewal\";"
+ },
+ "output": "Edit applied successfully.",
+ "title": "vite-frontend/src/pages/node.tsx",
+ "metadata": {
+ "diagnostics": {
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/store/repo/repository.go": [
+ {
+ "range": {
+ "start": {
+ "line": 161,
+ "character": 13
+ },
+ "end": {
+ "line": 161,
+ "character": 24
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 356,
+ "character": 77
+ },
+ "end": {
+ "line": 356,
+ "character": 88
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 436,
+ "character": 74
+ },
+ "end": {
+ "line": 436,
+ "character": 85
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 573,
+ "character": 77
+ },
+ "end": {
+ "line": 573,
+ "character": 88
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 583,
+ "character": 77
+ },
+ "end": {
+ "line": 583,
+ "character": 88
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 596,
+ "character": 28
+ },
+ "end": {
+ "line": 596,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 603,
+ "character": 28
+ },
+ "end": {
+ "line": 603,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 611,
+ "character": 29
+ },
+ "end": {
+ "line": 611,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 624,
+ "character": 47
+ },
+ "end": {
+ "line": 624,
+ "character": 58
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 632,
+ "character": 28
+ },
+ "end": {
+ "line": 632,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 634,
+ "character": 35
+ },
+ "end": {
+ "line": 634,
+ "character": 46
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 658,
+ "character": 47
+ },
+ "end": {
+ "line": 658,
+ "character": 58
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 666,
+ "character": 28
+ },
+ "end": {
+ "line": 666,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 668,
+ "character": 35
+ },
+ "end": {
+ "line": 668,
+ "character": 46
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 680,
+ "character": 53
+ },
+ "end": {
+ "line": 680,
+ "character": 64
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 688,
+ "character": 28
+ },
+ "end": {
+ "line": 688,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 690,
+ "character": 21
+ },
+ "end": {
+ "line": 690,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 700,
+ "character": 50
+ },
+ "end": {
+ "line": 700,
+ "character": 61
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 733,
+ "character": 28
+ },
+ "end": {
+ "line": 733,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 739,
+ "character": 21
+ },
+ "end": {
+ "line": 739,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 756,
+ "character": 75
+ },
+ "end": {
+ "line": 756,
+ "character": 86
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 782,
+ "character": 28
+ },
+ "end": {
+ "line": 782,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 784,
+ "character": 21
+ },
+ "end": {
+ "line": 784,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 794,
+ "character": 64
+ },
+ "end": {
+ "line": 794,
+ "character": 75
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 815,
+ "character": 28
+ },
+ "end": {
+ "line": 815,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 817,
+ "character": 21
+ },
+ "end": {
+ "line": 817,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 902,
+ "character": 10
+ },
+ "end": {
+ "line": 902,
+ "character": 23
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringsseq"
+ },
+ "source": "stringsseq",
+ "message": "Ranging over SplitSeq is more efficient"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 948,
+ "character": 49
+ },
+ "end": {
+ "line": 948,
+ "character": 60
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 958,
+ "character": 40
+ },
+ "end": {
+ "line": 958,
+ "character": 51
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 962,
+ "character": 31
+ },
+ "end": {
+ "line": 962,
+ "character": 42
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 968,
+ "character": 37
+ },
+ "end": {
+ "line": 968,
+ "character": 48
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 969,
+ "character": 37
+ },
+ "end": {
+ "line": 969,
+ "character": 48
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 970,
+ "character": 39
+ },
+ "end": {
+ "line": 970,
+ "character": 50
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 990,
+ "character": 47
+ },
+ "end": {
+ "line": 990,
+ "character": 58
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1007,
+ "character": 24
+ },
+ "end": {
+ "line": 1007,
+ "character": 35
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1024,
+ "character": 54
+ },
+ "end": {
+ "line": 1024,
+ "character": 65
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1030,
+ "character": 51
+ },
+ "end": {
+ "line": 1030,
+ "character": 62
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1034,
+ "character": 56
+ },
+ "end": {
+ "line": 1034,
+ "character": 67
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1048,
+ "character": 33
+ },
+ "end": {
+ "line": 1048,
+ "character": 44
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1061,
+ "character": 29
+ },
+ "end": {
+ "line": 1061,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1072,
+ "character": 54
+ },
+ "end": {
+ "line": 1072,
+ "character": 65
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1080,
+ "character": 29
+ },
+ "end": {
+ "line": 1080,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1086,
+ "character": 37
+ },
+ "end": {
+ "line": 1086,
+ "character": 48
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1095,
+ "character": 52
+ },
+ "end": {
+ "line": 1095,
+ "character": 63
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1103,
+ "character": 29
+ },
+ "end": {
+ "line": 1103,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1109,
+ "character": 37
+ },
+ "end": {
+ "line": 1109,
+ "character": 48
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1118,
+ "character": 58
+ },
+ "end": {
+ "line": 1118,
+ "character": 69
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1141,
+ "character": 29
+ },
+ "end": {
+ "line": 1141,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1143,
+ "character": 37
+ },
+ "end": {
+ "line": 1143,
+ "character": 48
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1215,
+ "character": 84
+ },
+ "end": {
+ "line": 1215,
+ "character": 95
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1281,
+ "character": 27
+ },
+ "end": {
+ "line": 1281,
+ "character": 38
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1297,
+ "character": 83
+ },
+ "end": {
+ "line": 1297,
+ "character": 94
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1321,
+ "character": 90
+ },
+ "end": {
+ "line": 1321,
+ "character": 101
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1335,
+ "character": 85
+ },
+ "end": {
+ "line": 1335,
+ "character": 96
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1555,
+ "character": 85
+ },
+ "end": {
+ "line": 1555,
+ "character": 96
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1572,
+ "character": 130
+ },
+ "end": {
+ "line": 1572,
+ "character": 141
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1593,
+ "character": 21
+ },
+ "end": {
+ "line": 1593,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2541,
+ "character": 23
+ },
+ "end": {
+ "line": 2541,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2556,
+ "character": 23
+ },
+ "end": {
+ "line": 2556,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2663,
+ "character": 36
+ },
+ "end": {
+ "line": 2663,
+ "character": 47
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2757,
+ "character": 27
+ },
+ "end": {
+ "line": 2757,
+ "character": 38
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2960,
+ "character": 16
+ },
+ "end": {
+ "line": 2960,
+ "character": 29
+ }
+ },
+ "severity": 4,
+ "code": "stringscut",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize#stringscut"
+ },
+ "source": "stringscut",
+ "message": "strings.Index can be simplified using strings.Cut"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3036,
+ "character": 38
+ },
+ "end": {
+ "line": 3036,
+ "character": 49
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3043,
+ "character": 38
+ },
+ "end": {
+ "line": 3043,
+ "character": 49
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3051,
+ "character": 36
+ },
+ "end": {
+ "line": 3051,
+ "character": 47
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ }
+ ],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/store/repo/repository_migrate_test.go": [],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/store/model/model.go": [
+ {
+ "range": {
+ "start": {
+ "line": 234,
+ "character": 27
+ },
+ "end": {
+ "line": 234,
+ "character": 85
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#omitzero"
+ },
+ "source": "omitzero",
+ "message": "Omitempty has no effect on nested struct fields"
+ }
+ ],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/store/repo/repository_mutations.go": [
+ {
+ "range": {
+ "start": {
+ "line": 79,
+ "character": 21
+ },
+ "end": {
+ "line": 79,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 97,
+ "character": 21
+ },
+ "end": {
+ "line": 97,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 114,
+ "character": 21
+ },
+ "end": {
+ "line": 114,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 157,
+ "character": 21
+ },
+ "end": {
+ "line": 157,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 164,
+ "character": 21
+ },
+ "end": {
+ "line": 164,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 173,
+ "character": 21
+ },
+ "end": {
+ "line": 173,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 199,
+ "character": 156
+ },
+ "end": {
+ "line": 199,
+ "character": 167
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 199,
+ "character": 317
+ },
+ "end": {
+ "line": 199,
+ "character": 328
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 246,
+ "character": 159
+ },
+ "end": {
+ "line": 246,
+ "character": 170
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 252,
+ "character": 21
+ },
+ "end": {
+ "line": 252,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 303,
+ "character": 21
+ },
+ "end": {
+ "line": 303,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 380,
+ "character": 21
+ },
+ "end": {
+ "line": 380,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 389,
+ "character": 21
+ },
+ "end": {
+ "line": 389,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 568,
+ "character": 107
+ },
+ "end": {
+ "line": 568,
+ "character": 118
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 574,
+ "character": 21
+ },
+ "end": {
+ "line": 574,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 610,
+ "character": 70
+ },
+ "end": {
+ "line": 610,
+ "character": 81
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 629,
+ "character": 62
+ },
+ "end": {
+ "line": 629,
+ "character": 73
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 635,
+ "character": 21
+ },
+ "end": {
+ "line": 635,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 657,
+ "character": 122
+ },
+ "end": {
+ "line": 657,
+ "character": 133
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 663,
+ "character": 21
+ },
+ "end": {
+ "line": 663,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 679,
+ "character": 21
+ },
+ "end": {
+ "line": 679,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 688,
+ "character": 21
+ },
+ "end": {
+ "line": 688,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 743,
+ "character": 149
+ },
+ "end": {
+ "line": 743,
+ "character": 160
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 749,
+ "character": 21
+ },
+ "end": {
+ "line": 749,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 807,
+ "character": 23
+ },
+ "end": {
+ "line": 807,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 858,
+ "character": 23
+ },
+ "end": {
+ "line": 858,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1200,
+ "character": 192
+ },
+ "end": {
+ "line": 1200,
+ "character": 203
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1249,
+ "character": 80
+ },
+ "end": {
+ "line": 1249,
+ "character": 91
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1258,
+ "character": 137
+ },
+ "end": {
+ "line": 1258,
+ "character": 148
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1286,
+ "character": 31
+ },
+ "end": {
+ "line": 1286,
+ "character": 42
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1312,
+ "character": 30
+ },
+ "end": {
+ "line": 1312,
+ "character": 41
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1355,
+ "character": 27
+ },
+ "end": {
+ "line": 1355,
+ "character": 38
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1384,
+ "character": 10
+ },
+ "end": {
+ "line": 1384,
+ "character": 23
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringsseq"
+ },
+ "source": "stringsseq",
+ "message": "Ranging over SplitSeq is more efficient"
+ }
+ ],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/http/handler/mutations.go": [
+ {
+ "range": {
+ "start": {
+ "line": 34,
+ "character": 20
+ },
+ "end": {
+ "line": 34,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 88,
+ "character": 20
+ },
+ "end": {
+ "line": 88,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 215,
+ "character": 20
+ },
+ "end": {
+ "line": 215,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 240,
+ "character": 20
+ },
+ "end": {
+ "line": 240,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 292,
+ "character": 20
+ },
+ "end": {
+ "line": 292,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 463,
+ "character": 20
+ },
+ "end": {
+ "line": 463,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 635,
+ "character": 2
+ },
+ "end": {
+ "line": 635,
+ "character": 23
+ }
+ },
+ "severity": 3,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://staticcheck.dev/docs/checks/#QF1003"
+ },
+ "source": "QF1003",
+ "message": "could use tagged switch on row.ChainType"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 636,
+ "character": 67
+ },
+ "end": {
+ "line": 636,
+ "character": 78
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 638,
+ "character": 67
+ },
+ "end": {
+ "line": 638,
+ "character": 78
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 639,
+ "character": 68
+ },
+ "end": {
+ "line": 639,
+ "character": 79
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 641,
+ "character": 68
+ },
+ "end": {
+ "line": 641,
+ "character": 79
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 674,
+ "character": 20
+ },
+ "end": {
+ "line": 674,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 861,
+ "character": 46
+ },
+ "end": {
+ "line": 861,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1011,
+ "character": 46
+ },
+ "end": {
+ "line": 1011,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1015,
+ "character": 20
+ },
+ "end": {
+ "line": 1015,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1040,
+ "character": 18
+ },
+ "end": {
+ "line": 1040,
+ "character": 29
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1065,
+ "character": 20
+ },
+ "end": {
+ "line": 1065,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1131,
+ "character": 20
+ },
+ "end": {
+ "line": 1131,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1233,
+ "character": 20
+ },
+ "end": {
+ "line": 1233,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1377,
+ "character": 47
+ },
+ "end": {
+ "line": 1377,
+ "character": 58
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1529,
+ "character": 46
+ },
+ "end": {
+ "line": 1529,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1560,
+ "character": 46
+ },
+ "end": {
+ "line": 1560,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1591,
+ "character": 46
+ },
+ "end": {
+ "line": 1591,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1618,
+ "character": 46
+ },
+ "end": {
+ "line": 1618,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1714,
+ "character": 46
+ },
+ "end": {
+ "line": 1714,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1718,
+ "character": 20
+ },
+ "end": {
+ "line": 1718,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1743,
+ "character": 20
+ },
+ "end": {
+ "line": 1743,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1926,
+ "character": 20
+ },
+ "end": {
+ "line": 1926,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 1945,
+ "character": 20
+ },
+ "end": {
+ "line": 1945,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2036,
+ "character": 71
+ },
+ "end": {
+ "line": 2036,
+ "character": 82
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2222,
+ "character": 55
+ },
+ "end": {
+ "line": 2222,
+ "character": 66
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2248,
+ "character": 46
+ },
+ "end": {
+ "line": 2248,
+ "character": 57
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2296,
+ "character": 20
+ },
+ "end": {
+ "line": 2296,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2637,
+ "character": 90
+ },
+ "end": {
+ "line": 2637,
+ "character": 101
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2648,
+ "character": 67
+ },
+ "end": {
+ "line": 2648,
+ "character": 78
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2667,
+ "character": 63
+ },
+ "end": {
+ "line": 2667,
+ "character": 74
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2678,
+ "character": 62
+ },
+ "end": {
+ "line": 2678,
+ "character": 73
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2699,
+ "character": 153
+ },
+ "end": {
+ "line": 2699,
+ "character": 164
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2707,
+ "character": 32
+ },
+ "end": {
+ "line": 2707,
+ "character": 43
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2722,
+ "character": 26
+ },
+ "end": {
+ "line": 2722,
+ "character": 37
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2726,
+ "character": 38
+ },
+ "end": {
+ "line": 2726,
+ "character": 49
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2728,
+ "character": 43
+ },
+ "end": {
+ "line": 2728,
+ "character": 54
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2732,
+ "character": 24
+ },
+ "end": {
+ "line": 2732,
+ "character": 35
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2739,
+ "character": 19
+ },
+ "end": {
+ "line": 2739,
+ "character": 30
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2741,
+ "character": 25
+ },
+ "end": {
+ "line": 2741,
+ "character": 36
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2752,
+ "character": 19
+ },
+ "end": {
+ "line": 2752,
+ "character": 30
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2754,
+ "character": 23
+ },
+ "end": {
+ "line": 2754,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2758,
+ "character": 111
+ },
+ "end": {
+ "line": 2758,
+ "character": 122
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2763,
+ "character": 26
+ },
+ "end": {
+ "line": 2763,
+ "character": 37
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2767,
+ "character": 38
+ },
+ "end": {
+ "line": 2767,
+ "character": 49
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2769,
+ "character": 23
+ },
+ "end": {
+ "line": 2769,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2773,
+ "character": 25
+ },
+ "end": {
+ "line": 2773,
+ "character": 36
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2778,
+ "character": 33
+ },
+ "end": {
+ "line": 2778,
+ "character": 44
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2781,
+ "character": 35
+ },
+ "end": {
+ "line": 2781,
+ "character": 46
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2783,
+ "character": 21
+ },
+ "end": {
+ "line": 2783,
+ "character": 32
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 2910,
+ "character": 84
+ },
+ "end": {
+ "line": 2910,
+ "character": 95
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3112,
+ "character": 10
+ },
+ "end": {
+ "line": 3112,
+ "character": 23
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringsseq"
+ },
+ "source": "stringsseq",
+ "message": "Ranging over SplitSeq is more efficient"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3221,
+ "character": 50
+ },
+ "end": {
+ "line": 3221,
+ "character": 61
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3398,
+ "character": 18
+ },
+ "end": {
+ "line": 3398,
+ "character": 29
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3398,
+ "character": 33
+ },
+ "end": {
+ "line": 3398,
+ "character": 44
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3402,
+ "character": 20
+ },
+ "end": {
+ "line": 3402,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3408,
+ "character": 18
+ },
+ "end": {
+ "line": 3408,
+ "character": 29
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3408,
+ "character": 44
+ },
+ "end": {
+ "line": 3408,
+ "character": 55
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3413,
+ "character": 26
+ },
+ "end": {
+ "line": 3413,
+ "character": 37
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3415,
+ "character": 29
+ },
+ "end": {
+ "line": 3415,
+ "character": 40
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3422,
+ "character": 16
+ },
+ "end": {
+ "line": 3422,
+ "character": 27
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3441,
+ "character": 13
+ },
+ "end": {
+ "line": 3441,
+ "character": 24
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3453,
+ "character": 15
+ },
+ "end": {
+ "line": 3453,
+ "character": 26
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3465,
+ "character": 15
+ },
+ "end": {
+ "line": 3465,
+ "character": 26
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3477,
+ "character": 23
+ },
+ "end": {
+ "line": 3477,
+ "character": 34
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3498,
+ "character": 20
+ },
+ "end": {
+ "line": 3498,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3516,
+ "character": 20
+ },
+ "end": {
+ "line": 3516,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3533,
+ "character": 1
+ },
+ "end": {
+ "line": 3533,
+ "character": 11
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#slicessort"
+ },
+ "source": "slicessort",
+ "message": "sort.Slice can be modernized using slices.Sort"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3537,
+ "character": 28
+ },
+ "end": {
+ "line": 3537,
+ "character": 39
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3544,
+ "character": 32
+ },
+ "end": {
+ "line": 3544,
+ "character": 43
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3560,
+ "character": 27
+ },
+ "end": {
+ "line": 3560,
+ "character": 38
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3582,
+ "character": 20
+ },
+ "end": {
+ "line": 3582,
+ "character": 31
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any"
+ },
+ "source": "any",
+ "message": "interface{} can be replaced by any"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3634,
+ "character": 10
+ },
+ "end": {
+ "line": 3634,
+ "character": 23
+ }
+ },
+ "severity": 4,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringsseq"
+ },
+ "source": "stringsseq",
+ "message": "Ranging over SplitSeq is more efficient"
+ }
+ ],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/http/handler/control_plane_test.go": [
+ {
+ "range": {
+ "start": {
+ "line": 204,
+ "character": 19
+ },
+ "end": {
+ "line": 204,
+ "character": 19
+ }
+ },
+ "severity": 3,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite"
+ },
+ "source": "unusedwrite",
+ "message": "unused write to field repo"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 205,
+ "character": 23
+ },
+ "end": {
+ "line": 205,
+ "character": 23
+ }
+ },
+ "severity": 3,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite"
+ },
+ "source": "unusedwrite",
+ "message": "unused write to field ID"
+ },
+ {
+ "range": {
+ "start": {
+ "line": 205,
+ "character": 32
+ },
+ "end": {
+ "line": 205,
+ "character": 32
+ }
+ },
+ "severity": 3,
+ "code": "default",
+ "codeDescription": {
+ "href": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite"
+ },
+ "source": "unusedwrite",
+ "message": "unused write to field Name"
+ }
+ ],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/app/app.go": [],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/cmd/paneld/main.go": [],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/http/handler/jobs.go": [],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/http/handler/jobs_renewal_test.go": [],
+ "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/go-backend/internal/http/handler/jobs_test.go": []
+ },
+ "diff": "Index: /Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx\n===================================================================\n--- /Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx\n+++ /Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx\n@@ -60,8 +60,10 @@\n } from \"@/pages/node/display\";\n import { tryCopyInstallCommand } from \"@/pages/node/install-command\";\n import {\n getNodeRenewalSnapshot,\n+ formatNodeRenewalTime,\n+ getNodeRenewalCycleLabel,\n type NodeRenewalCycle,\n } from \"@/pages/node/renewal\";\n import { buildNodeSystemInfo } from \"@/pages/node/system-info\";\n import { useNodeOfflineTimers } from \"@/pages/node/use-node-offline-timers\";\n",
+ "filediff": {
+ "file": "/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/quiet-canyon/vite-frontend/src/pages/node.tsx",
+ "before": "import { useState, useEffect, useMemo, useCallback } from \"react\";\nimport toast from \"react-hot-toast\";\nimport {\n DndContext,\n KeyboardSensor,\n MouseSensor,\n TouchSensor,\n type DragEndEvent,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n arrayMove,\n rectSortingStrategy,\n sortableKeyboardCoordinates,\n useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\nimport { SearchBar } from \"@/components/search-bar\";\nimport { AnimatedPage } from \"@/components/animated-page\";\nimport { Card, CardBody, CardHeader } from \"@/shadcn-bridge/heroui/card\";\nimport { Button } from \"@/shadcn-bridge/heroui/button\";\nimport { Input } from \"@/shadcn-bridge/heroui/input\";\nimport { Textarea } from \"@/shadcn-bridge/heroui/input\";\nimport {\n Modal,\n ModalContent,\n ModalHeader,\n ModalBody,\n ModalFooter,\n} from \"@/shadcn-bridge/heroui/modal\";\nimport { Chip } from \"@/shadcn-bridge/heroui/chip\";\nimport { Switch } from \"@/shadcn-bridge/heroui/switch\";\nimport { Spinner } from \"@/shadcn-bridge/heroui/spinner\";\nimport { Alert } from \"@/shadcn-bridge/heroui/alert\";\nimport { Progress } from \"@/shadcn-bridge/heroui/progress\";\nimport { Accordion, AccordionItem } from \"@/shadcn-bridge/heroui/accordion\";\nimport { Select, SelectItem } from \"@/shadcn-bridge/heroui/select\";\nimport { Checkbox } from \"@/shadcn-bridge/heroui/checkbox\";\nimport {\n createNode,\n getNodeList,\n updateNode,\n deleteNode,\n getNodeInstallCommand,\n updateNodeOrder,\n batchDeleteNodes,\n upgradeNode,\n batchUpgradeNodes,\n getNodeReleases,\n rollbackNode,\n type ReleaseChannel,\n} from \"@/api\";\nimport { PageEmptyState, PageLoadingState } from \"@/components/page-state\";\nimport {\n getConnectionStatusMeta,\n getRemoteSyncErrorMessage,\n} from \"@/pages/node/display\";\nimport { tryCopyInstallCommand } from \"@/pages/node/install-command\";\nimport {\n getNodeRenewalSnapshot,\n type NodeRenewalCycle,\n} from \"@/pages/node/renewal\";\nimport { buildNodeSystemInfo } from \"@/pages/node/system-info\";\nimport { useNodeOfflineTimers } from \"@/pages/node/use-node-offline-timers\";\nimport { useNodeRealtime } from \"@/pages/node/use-node-realtime\";\nimport { useLocalStorageState } from \"@/hooks/use-local-storage-state\";\nimport { loadStoredOrder, saveOrder } from \"@/utils/order-storage\";\n\nconst NODE_FALLBACK_REFRESH_INTERVAL_MS = 15000;\n\ninterface Node {\n id: number;\n inx?: number;\n name: string;\n remark?: string;\n tags?: string;\n expiryTime?: number;\n renewalCycle?: NodeRenewalCycle;\n ip: string;\n serverIp: string;\n serverIpV4?: string;\n serverIpV6?: string;\n port: string;\n tcpListenAddr?: string;\n udpListenAddr?: string;\n extraIPs?: string;\n version?: string;\n http?: number; // 0 关 1 开\n tls?: number; // 0 关 1 开\n socks?: number; // 0 关 1 开\n status: number;\n isRemote?: number;\n remoteUrl?: string;\n syncError?: string;\n connectionStatus: \"online\" | \"offline\";\n systemInfo?: {\n cpuUsage: number;\n memoryUsage: number;\n uploadTraffic: number;\n downloadTraffic: number;\n uploadSpeed: number;\n downloadSpeed: number;\n uptime: number;\n } | null;\n copyLoading?: boolean;\n upgradeLoading?: boolean;\n rollbackLoading?: boolean;\n}\n\ninterface NodeForm {\n id: number | null;\n name: string;\n remark: string;\n tags: string;\n expiryTime: number;\n renewalCycle: NodeRenewalCycle;\n serverHost: string;\n serverIpV4: string;\n serverIpV6: string;\n port: string;\n tcpListenAddr: string;\n udpListenAddr: string;\n interfaceName: string;\n extraIPs: string;\n http: number; // 0 关 1 开\n tls: number; // 0 关 1 开\n socks: number; // 0 关 1 开\n}\n\nconst EXPIRING_SOON_DAYS = 7;\n\ntype NodeExpiryState = \"permanent\" | \"healthy\" | \"expiringSoon\" | \"expired\";\n\ntype NodeFilterMode = \"all\" | \"expiringSoon\" | \"expired\" | \"withExpiry\";\n\nconst getNodeReminderEnabled = (node: Node): boolean => {\n return !!node.expiryTime && node.expiryTime > 0 && !!node.renewalCycle;\n};\n\nconst getNodeExpiryMeta = (timestamp?: number, cycle?: NodeRenewalCycle) => {\n const renewal = getNodeRenewalSnapshot(timestamp, cycle, EXPIRING_SOON_DAYS);\n\n if (renewal.state === \"unset\") {\n return {\n state: \"permanent\" as NodeExpiryState,\n label: \"未设置续费周期\",\n tone: \"default\" as const,\n accentClassName: \"\",\n bannerClassName: \"\",\n isHighlighted: false,\n sortWeight: 3,\n nextDueTime: undefined,\n };\n }\n\n if (renewal.state === \"expired\") {\n return {\n state: \"expired\" as NodeExpiryState,\n label: \"已过期\",\n tone: \"danger\" as const,\n accentClassName:\n \"border-red-300/80 bg-red-50/70 shadow-red-100 dark:border-red-500/40 dark:bg-red-950/20\",\n bannerClassName:\n \"bg-red-50 text-red-700 dark:bg-red-950/30 dark:text-red-300\",\n isHighlighted: true,\n sortWeight: 0,\n nextDueTime: renewal.nextDueTime,\n };\n }\n\n if (renewal.state === \"dueSoon\") {\n return {\n state: \"expiringSoon\" as NodeExpiryState,\n label: renewal.label,\n tone: \"warning\" as const,\n accentClassName:\n \"border-amber-300/80 bg-amber-50/80 shadow-amber-100 dark:border-amber-500/40 dark:bg-amber-950/20\",\n bannerClassName:\n \"bg-amber-50 text-amber-700 dark:bg-amber-950/30 dark:text-amber-300\",\n isHighlighted: true,\n sortWeight: 1,\n nextDueTime: renewal.nextDueTime,\n };\n }\n\n return {\n state: \"healthy\" as NodeExpiryState,\n label: renewal.label,\n tone: \"success\" as const,\n accentClassName: \"\",\n bannerClassName: \"\",\n isHighlighted: false,\n sortWeight: 2,\n nextDueTime: renewal.nextDueTime,\n };\n};\n\nconst normalizeNodeTags = (tags?: string): string[] => {\n if (!tags) return [];\n\n return tags\n .split(\",\")\n .map((tag) => tag.trim())\n .filter(Boolean);\n};\n\nconst mergeNodeRealtimeState = (\n incomingNode: Node,\n existingNode?: Node,\n): Node => {\n return {\n ...incomingNode,\n systemInfo: existingNode?.systemInfo ?? incomingNode.systemInfo ?? null,\n copyLoading: existingNode?.copyLoading ?? incomingNode.copyLoading ?? false,\n upgradeLoading:\n existingNode?.upgradeLoading ?? incomingNode.upgradeLoading ?? false,\n rollbackLoading:\n existingNode?.rollbackLoading ?? incomingNode.rollbackLoading ?? false,\n };\n};\n\nconst SortableItem = ({\n id,\n children,\n}: {\n id: number;\n children: (listeners: any) => any;\n}) => {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id });\n\n const style: React.CSSProperties = {\n transform: transform\n ? CSS.Transform.toString({\n ...transform,\n x: Math.round(transform.x),\n y: Math.round(transform.y),\n })\n : undefined,\n transition: isDragging ? undefined : transition || undefined,\n opacity: isDragging ? 0.5 : 1,\n willChange: isDragging ? \"transform\" : undefined,\n };\n\n return (\n
\n {children(listeners)}\n
\n );\n};\n\nexport default function NodePage() {\n const [nodeList, setNodeList] = useState
([]);\n const [nodeOrder, setNodeOrder] = useState([]);\n const [loading, setLoading] = useState(false);\n const [searchKeyword, setSearchKeyword] = useLocalStorageState(\n \"node-search-keyword\",\n \"\",\n );\n const [nodeFilterMode, setNodeFilterMode] =\n useLocalStorageState(\"node-expiry-filter-mode\", \"all\");\n const [isSearchVisible, setIsSearchVisible] = useState(false);\n const [dialogVisible, setDialogVisible] = useState(false);\n const [dialogTitle, setDialogTitle] = useState(\"\");\n const [isEdit, setIsEdit] = useState(false);\n const [submitLoading, setSubmitLoading] = useState(false);\n const [deleteModalOpen, setDeleteModalOpen] = useState(false);\n const [deleteLoading, setDeleteLoading] = useState(false);\n const [nodeToDelete, setNodeToDelete] = useState(null);\n const [protocolDisabled, setProtocolDisabled] = useState(false);\n const [protocolDisabledReason, setProtocolDisabledReason] = useState(\"\");\n const [form, setForm] = useState({\n id: null,\n name: \"\",\n remark: \"\",\n tags: \"\",\n expiryTime: 0,\n renewalCycle: \"\",\n serverHost: \"\",\n serverIpV4: \"\",\n serverIpV6: \"\",\n port: \"1000-65535\",\n tcpListenAddr: \"[::]\",\n udpListenAddr: \"[::]\",\n interfaceName: \"\",\n extraIPs: \"\",\n http: 0,\n tls: 0,\n socks: 0,\n });\n const [errors, setErrors] = useState>({});\n\n const [selectMode, setSelectMode] = useState(false);\n const [selectedIds, setSelectedIds] = useState>(new Set());\n const [batchDeleteModalOpen, setBatchDeleteModalOpen] = useState(false);\n const [batchLoading, setBatchLoading] = useState(false);\n\n // 安装命令相关状态\n const [installCommandModal, setInstallCommandModal] = useState(false);\n const [installCommand, setInstallCommand] = useState(\"\");\n const [currentNodeName, setCurrentNodeName] = useState(\"\");\n const [installSelectorOpen, setInstallSelectorOpen] = useState(false);\n const [installTargetNode, setInstallTargetNode] = useState(null);\n const [installChannel, setInstallChannel] =\n useState(\"stable\");\n\n // 升级相关状态\n const [upgradeModalOpen, setUpgradeModalOpen] = useState(false);\n const [upgradeTarget, setUpgradeTarget] = useState<\"single\" | \"batch\">(\n \"single\",\n );\n const [upgradeTargetNodeId, setUpgradeTargetNodeId] = useState(\n null,\n );\n const [releases, setReleases] = useState<\n Array<{\n version: string;\n name: string;\n publishedAt: string;\n prerelease: boolean;\n channel: ReleaseChannel;\n }>\n >([]);\n const [releasesLoading, setReleasesLoading] = useState(false);\n const [releaseChannel, setReleaseChannel] =\n useState(\"stable\");\n const [selectedVersion, setSelectedVersion] = useState(\"\");\n const [batchUpgradeLoading, setBatchUpgradeLoading] = useState(false);\n const [upgradeProgress, setUpgradeProgress] = useState<\n Record\n >({});\n\n const handleNodeOffline = useCallback((nodeId: number) => {\n setNodeList((prev) =>\n prev.map((node) => {\n if (node.id !== nodeId) return node;\n if (node.connectionStatus === \"offline\" && node.systemInfo === null) {\n return node;\n }\n\n return { ...node, connectionStatus: \"offline\", systemInfo: null };\n }),\n );\n }, []);\n\n const { clearOfflineTimer, scheduleNodeOffline } = useNodeOfflineTimers({\n delayMs: 3000,\n onNodeOffline: handleNodeOffline,\n });\n\n // 加载节点列表\n const loadNodes = useCallback(async (options?: { silent?: boolean }) => {\n const silent = options?.silent ?? false;\n\n if (!silent) {\n setLoading(true);\n }\n\n try {\n const res = await getNodeList();\n\n if (res.code === 0) {\n const nodesData: Node[] = (res.data || []).map((node: any) => ({\n ...node,\n inx: node.inx ?? 0,\n connectionStatus: node.syncError\n ? \"offline\"\n : node.status === 1\n ? \"online\"\n : \"offline\",\n syncError: node.syncError || undefined,\n systemInfo: null,\n copyLoading: false,\n }));\n\n setNodeList((prev) => {\n const previousById = new Map(prev.map((node) => [node.id, node]));\n\n return nodesData.map((node) =>\n mergeNodeRealtimeState(node, previousById.get(node.id)),\n );\n });\n\n // 优先使用数据库中的 inx 字段进行排序,否则回退到本地排序\n const hasDbOrdering = nodesData.some(\n (n) => n.inx !== undefined && n.inx !== 0,\n );\n\n if (hasDbOrdering) {\n const dbOrder = [...nodesData]\n .sort((a, b) => (a.inx ?? 0) - (b.inx ?? 0))\n .map((n) => n.id);\n\n setNodeOrder(dbOrder);\n } else {\n setNodeOrder(\n loadStoredOrder(\n \"node-order\",\n nodesData.map((n) => n.id),\n ),\n );\n }\n } else {\n if (!silent) {\n toast.error(res.msg || \"加载节点列表失败\");\n }\n }\n } catch {\n if (!silent) {\n toast.error(\"网络错误,请重试\");\n }\n } finally {\n if (!silent) {\n setLoading(false);\n }\n }\n }, []);\n\n // 处理WebSocket消息\n const handleWebSocketMessage = (data: any) => {\n const { id, type, data: messageData } = data;\n const nodeId = Number(id);\n\n if (Number.isNaN(nodeId)) return;\n\n if (type === \"status\") {\n if (messageData === 1) {\n clearOfflineTimer(nodeId);\n setNodeList((prev) =>\n prev.map((node) => {\n if (node.id !== nodeId) return node;\n if (node.connectionStatus === \"online\") return node;\n\n return { ...node, connectionStatus: \"online\" };\n }),\n );\n } else {\n // 离线事件做延迟处理,避免短抖动导致频繁闪烁\n scheduleNodeOffline(nodeId);\n }\n } else if (type === \"info\") {\n clearOfflineTimer(nodeId);\n setNodeList((prev) =>\n prev.map((node) => {\n if (node.id === nodeId) {\n const systemInfo = buildNodeSystemInfo(\n messageData,\n node.systemInfo,\n );\n\n if (!systemInfo) {\n return node;\n }\n\n return {\n ...node,\n connectionStatus: \"online\",\n systemInfo,\n };\n }\n\n return node;\n }),\n );\n } else if (type === \"upgrade_progress\") {\n try {\n const progressData =\n typeof messageData === \"string\"\n ? JSON.parse(messageData)\n : messageData;\n\n if (progressData?.data) {\n setUpgradeProgress((prev) => ({\n ...prev,\n [nodeId]: {\n stage: progressData.data.stage || \"\",\n percent: progressData.data.percent || 0,\n message: progressData.message || \"\",\n },\n }));\n }\n } catch {\n // ignore parse errors\n }\n }\n };\n\n const { wsConnected, wsConnecting, usingPollingFallback } = useNodeRealtime({\n onMessage: handleWebSocketMessage,\n });\n\n useEffect(() => {\n loadNodes();\n }, [loadNodes]);\n\n useEffect(() => {\n if (!usingPollingFallback) {\n return;\n }\n\n void loadNodes({ silent: true });\n\n const interval = window.setInterval(() => {\n void loadNodes({ silent: true });\n }, NODE_FALLBACK_REFRESH_INTERVAL_MS);\n\n return () => {\n window.clearInterval(interval);\n };\n }, [loadNodes, usingPollingFallback]);\n\n // 格式化速度\n const formatSpeed = (bytesPerSecond: number): string => {\n if (bytesPerSecond === 0) return \"0 B/s\";\n\n const k = 1024;\n const sizes = [\"B/s\", \"KB/s\", \"MB/s\", \"GB/s\", \"TB/s\"];\n const i = Math.floor(Math.log(bytesPerSecond) / Math.log(k));\n\n return (\n parseFloat((bytesPerSecond / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i]\n );\n };\n\n // 格式化开机时间\n const formatUptime = (seconds: number): string => {\n if (seconds === 0) return \"-\";\n\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n\n if (days > 0) {\n return `${days}天${hours}小时`;\n } else if (hours > 0) {\n return `${hours}小时${minutes}分钟`;\n } else {\n return `${minutes}分钟`;\n }\n };\n\n // 格式化流量\n const formatTraffic = (bytes: number): string => {\n if (bytes === 0) return \"0 B\";\n\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i];\n };\n\n // 获取进度条颜色\n const getProgressColor = (\n value: number,\n offline = false,\n ): \"default\" | \"primary\" | \"secondary\" | \"success\" | \"warning\" | \"danger\" => {\n if (offline) return \"default\";\n if (value <= 50) return \"success\";\n if (value <= 80) return \"warning\";\n\n return \"danger\";\n };\n\n // IPv4/IPv6 格式验证(仅用于判定地址族)\n const ipv4Regex =\n /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;\n const ipv6Regex =\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;\n\n const validateIpv4Literal = (ip: string): boolean =>\n ipv4Regex.test(ip.trim());\n const validateIpv6Literal = (ip: string): boolean =>\n ipv6Regex.test(ip.trim());\n\n // Hostname/domain validation (no scheme/port)\n const hostnameRegex =\n /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(?:\\.(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?))*$/;\n const validateHostname = (host: string): boolean => {\n const v = host.trim();\n\n if (!v) return false;\n if (v === \"localhost\") return true;\n\n return hostnameRegex.test(v);\n };\n\n // 验证端口格式:支持 80,443,100-600\n const validatePort = (\n portStr: string,\n ): { valid: boolean; error?: string } => {\n if (!portStr || !portStr.trim()) {\n return { valid: false, error: \"请输入端口\" };\n }\n\n const trimmed = portStr.trim();\n const parts = trimmed\n .split(\",\")\n .map((p) => p.trim())\n .filter((p) => p);\n\n if (parts.length === 0) {\n return { valid: false, error: \"请输入有效的端口\" };\n }\n\n for (const part of parts) {\n // 检查是否是端口范围 (如 100-600)\n if (part.includes(\"-\")) {\n const range = part.split(\"-\").map((p) => p.trim());\n\n if (range.length !== 2) {\n return { valid: false, error: `端口范围格式错误: ${part}` };\n }\n\n const start = parseInt(range[0]);\n const end = parseInt(range[1]);\n\n if (isNaN(start) || isNaN(end)) {\n return { valid: false, error: `端口必须是数字: ${part}` };\n }\n\n if (start < 1 || start > 65535 || end < 1 || end > 65535) {\n return {\n valid: false,\n error: `端口范围必须在 1-65535 之间: ${part}`,\n };\n }\n\n if (start >= end) {\n return { valid: false, error: `起始端口必须小于结束端口: ${part}` };\n }\n } else {\n // 单个端口\n const port = parseInt(part);\n\n if (isNaN(port)) {\n return { valid: false, error: `端口必须是数字: ${part}` };\n }\n\n if (port < 1 || port > 65535) {\n return { valid: false, error: `端口必须在 1-65535 之间: ${part}` };\n }\n }\n }\n\n return { valid: true };\n };\n\n // 表单验证\n const validateForm = (): boolean => {\n const newErrors: Record = {};\n\n if (!form.name.trim()) {\n newErrors.name = \"请输入节点名称\";\n } else if (form.name.trim().length < 2) {\n newErrors.name = \"节点名称长度至少2位\";\n } else if (form.name.trim().length > 50) {\n newErrors.name = \"节点名称长度不能超过50位\";\n }\n\n if (\n (form.renewalCycle && !form.expiryTime) ||\n (!form.renewalCycle && form.expiryTime)\n ) {\n newErrors.expiryTime = \"请同时设置续费周期和续费基准时间\";\n }\n\n const v4 = form.serverIpV4.trim();\n const v6 = form.serverIpV6.trim();\n const host = form.serverHost.trim();\n\n if (!v4 && !v6 && !host) {\n const msg = \"请至少填写一个 IPv4/IPv6 地址或域名\";\n\n newErrors.serverIpV4 = msg;\n newErrors.serverIpV6 = msg;\n newErrors.serverHost = msg;\n } else {\n if (v4 && !validateIpv4Literal(v4)) {\n newErrors.serverIpV4 = \"请输入有效的IPv4地址\";\n }\n if (v6 && !validateIpv6Literal(v6)) {\n newErrors.serverIpV6 = \"请输入有效的IPv6地址\";\n }\n if (host && !validateHostname(host)) {\n newErrors.serverHost = \"请输入有效的域名/主机名\";\n }\n }\n\n const portValidation = validatePort(form.port);\n\n if (!portValidation.valid) {\n newErrors.port = portValidation.error || \"端口格式错误\";\n }\n\n setErrors(newErrors);\n\n return Object.keys(newErrors).length === 0;\n };\n\n // 新增节点\n const handleAdd = () => {\n setDialogTitle(\"新增节点\");\n setIsEdit(false);\n setDialogVisible(true);\n resetForm();\n setProtocolDisabled(true);\n setProtocolDisabledReason(\"节点未在线,等待节点上线后再设置\");\n };\n\n // 编辑节点\n const handleEdit = (node: Node) => {\n setDialogTitle(\"编辑节点\");\n setIsEdit(true);\n\n const legacy = (node.serverIp || \"\").trim();\n const normalizedV4 =\n node.serverIpV4?.trim() || (validateIpv4Literal(legacy) ? legacy : \"\");\n const normalizedV6 =\n node.serverIpV6?.trim() || (validateIpv6Literal(legacy) ? legacy : \"\");\n const normalizedHost =\n !normalizedV4 && !normalizedV6 && legacy ? legacy : \"\";\n\n setForm({\n id: node.id,\n name: node.name,\n remark: node.remark || \"\",\n tags: node.tags || \"\",\n expiryTime: node.expiryTime || 0,\n renewalCycle: node.renewalCycle || \"\",\n serverHost: normalizedHost,\n serverIpV4: normalizedV4,\n serverIpV6: normalizedV6,\n port: node.port || \"1000-65535\",\n tcpListenAddr: node.tcpListenAddr || \"[::]\",\n udpListenAddr: node.udpListenAddr || \"[::]\",\n interfaceName: (node as any).interfaceName || \"\",\n extraIPs: node.extraIPs || \"\",\n http: typeof node.http === \"number\" ? node.http : 1,\n tls: typeof node.tls === \"number\" ? node.tls : 1,\n socks: typeof node.socks === \"number\" ? node.socks : 1,\n });\n const offline = node.connectionStatus !== \"online\";\n\n setProtocolDisabled(offline);\n setProtocolDisabledReason(\n offline ? \"节点未在线,等待节点上线后再设置\" : \"\",\n );\n setDialogVisible(true);\n };\n\n // 删除节点\n const handleDelete = (node: Node) => {\n setNodeToDelete(node);\n setDeleteModalOpen(true);\n };\n\n const confirmDelete = async () => {\n if (!nodeToDelete) return;\n\n setDeleteLoading(true);\n try {\n const res = await deleteNode(nodeToDelete.id);\n\n if (res.code === 0) {\n toast.success(\"删除成功\");\n setNodeList((prev) => prev.filter((n) => n.id !== nodeToDelete.id));\n setDeleteModalOpen(false);\n setNodeToDelete(null);\n } else {\n toast.error(res.msg || \"删除失败\");\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setDeleteLoading(false);\n }\n };\n\n const openInstallSelector = (node: Node) => {\n setInstallTargetNode(node);\n setInstallChannel(\"stable\");\n setInstallSelectorOpen(true);\n };\n\n // 复制安装命令\n const handleCopyInstallCommand = async (\n node: Node,\n channel: ReleaseChannel,\n ) => {\n setNodeList((prev) =>\n prev.map((n) => (n.id === node.id ? { ...n, copyLoading: true } : n)),\n );\n\n try {\n const res = await getNodeInstallCommand(node.id, channel);\n\n if (res.code === 0 && res.data) {\n const copied = await tryCopyInstallCommand(res.data);\n\n if (copied) {\n toast.success(\n `${channel === \"stable\" ? \"正式版\" : \"测试版\"}安装命令已复制到剪贴板`,\n );\n } else {\n setInstallCommand(res.data);\n setCurrentNodeName(node.name);\n setInstallCommandModal(true);\n }\n } else {\n toast.error(res.msg || \"获取安装命令失败\");\n }\n } catch {\n toast.error(\"获取安装命令失败\");\n } finally {\n setNodeList((prev) =>\n prev.map((n) => (n.id === node.id ? { ...n, copyLoading: false } : n)),\n );\n }\n };\n\n const handleConfirmInstallCommand = async () => {\n if (!installTargetNode) return;\n\n setInstallSelectorOpen(false);\n await handleCopyInstallCommand(installTargetNode, installChannel);\n };\n\n // 手动复制安装命令\n const handleManualCopy = async () => {\n try {\n await navigator.clipboard.writeText(installCommand);\n toast.success(\"安装命令已复制到剪贴板\");\n setInstallCommandModal(false);\n } catch {\n toast.error(\"复制失败,请手动选择文本复制\");\n }\n };\n\n const loadReleasesByChannel = useCallback(async (channel: ReleaseChannel) => {\n setReleasesLoading(true);\n try {\n const res = await getNodeReleases(channel);\n\n if (res.code === 0 && Array.isArray(res.data)) {\n setReleases(res.data);\n } else {\n toast.error(res.msg || \"获取版本列表失败\");\n }\n } catch {\n toast.error(\"获取版本列表失败\");\n } finally {\n setReleasesLoading(false);\n }\n }, []);\n\n // 打开版本选择弹窗\n const openUpgradeModal = async (\n target: \"single\" | \"batch\",\n nodeId?: number,\n ) => {\n const defaultChannel: ReleaseChannel = \"stable\";\n\n setUpgradeTarget(target);\n setUpgradeTargetNodeId(nodeId || null);\n setReleaseChannel(defaultChannel);\n setSelectedVersion(\"\");\n setUpgradeModalOpen(true);\n await loadReleasesByChannel(defaultChannel);\n };\n\n // 确认升级(从版本弹窗)\n const handleConfirmUpgrade = async () => {\n const version = selectedVersion || undefined;\n\n if (upgradeTarget === \"single\" && upgradeTargetNodeId) {\n setUpgradeModalOpen(false);\n // Find the node\n const node = nodeList.find((n) => n.id === upgradeTargetNodeId);\n\n if (!node) return;\n setNodeList((prev) =>\n prev.map((n) =>\n n.id === upgradeTargetNodeId ? { ...n, upgradeLoading: true } : n,\n ),\n );\n try {\n const res = await upgradeNode(\n upgradeTargetNodeId,\n version,\n releaseChannel,\n );\n\n if (res.code === 0) {\n toast.success(`节点升级命令已发送,节点将自动重启`);\n } else {\n toast.error(res.msg || \"升级失败\");\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setNodeList((prev) =>\n prev.map((n) =>\n n.id === upgradeTargetNodeId ? { ...n, upgradeLoading: false } : n,\n ),\n );\n }\n } else if (upgradeTarget === \"batch\") {\n setBatchUpgradeLoading(true);\n setUpgradeModalOpen(false);\n try {\n const res = await batchUpgradeNodes(\n Array.from(selectedIds),\n version,\n releaseChannel,\n );\n\n if (res.code === 0) {\n toast.success(`批量升级命令已发送到 ${selectedIds.size} 个节点`);\n } else {\n toast.error(res.msg || \"批量升级失败\");\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setBatchUpgradeLoading(false);\n }\n }\n };\n\n // 回退节点\n const handleRollbackNode = async (node: Node) => {\n setNodeList((prev) =>\n prev.map((n) => (n.id === node.id ? { ...n, rollbackLoading: true } : n)),\n );\n try {\n const res = await rollbackNode(node.id);\n\n if (res.code === 0) {\n toast.success(`节点 ${node.name} 回退命令已发送,节点将自动重启`);\n } else {\n toast.error(res.msg || \"回退失败\");\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setNodeList((prev) =>\n prev.map((n) =>\n n.id === node.id ? { ...n, rollbackLoading: false } : n,\n ),\n );\n }\n };\n\n // 提交表单\n const handleSubmit = async () => {\n if (!validateForm()) return;\n\n setSubmitLoading(true);\n\n try {\n const apiCall = isEdit ? updateNode : createNode;\n const { serverHost, ...rest } = form;\n const data = {\n ...rest,\n remark: form.remark.trim(),\n tags: form.tags.trim(),\n expiryTime: form.expiryTime,\n renewalCycle: form.renewalCycle,\n extraIPs: form.extraIPs,\n serverIp:\n form.serverIpV4?.trim() ||\n form.serverIpV6?.trim() ||\n serverHost?.trim() ||\n \"\",\n };\n\n const res = await apiCall(data);\n\n if (res.code === 0) {\n toast.success(isEdit ? \"更新成功\" : \"创建成功\");\n setDialogVisible(false);\n\n if (isEdit) {\n setNodeList((prev) =>\n prev.map((n) =>\n n.id === form.id\n ? {\n ...n,\n name: form.name,\n remark: form.remark.trim(),\n tags: form.tags.trim(),\n expiryTime: form.expiryTime,\n renewalCycle: form.renewalCycle,\n serverIp:\n form.serverIpV4?.trim() ||\n form.serverIpV6?.trim() ||\n form.serverHost?.trim() ||\n \"\",\n serverIpV4: form.serverIpV4,\n serverIpV6: form.serverIpV6,\n port: form.port,\n tcpListenAddr: form.tcpListenAddr,\n udpListenAddr: form.udpListenAddr,\n interfaceName: form.interfaceName,\n http: form.http,\n tls: form.tls,\n socks: form.socks,\n }\n : n,\n ),\n );\n } else {\n loadNodes();\n }\n } else {\n toast.error(res.msg || (isEdit ? \"更新失败\" : \"创建失败\"));\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setSubmitLoading(false);\n }\n };\n\n // 重置表单\n const resetForm = () => {\n setForm({\n id: null,\n name: \"\",\n remark: \"\",\n tags: \"\",\n expiryTime: 0,\n renewalCycle: \"\",\n serverHost: \"\",\n serverIpV4: \"\",\n serverIpV6: \"\",\n port: \"1000-65535\",\n tcpListenAddr: \"[::]\",\n udpListenAddr: \"[::]\",\n interfaceName: \"\",\n extraIPs: \"\",\n http: 0,\n tls: 0,\n socks: 0,\n });\n setErrors({});\n };\n\n // 处理拖拽结束\n const handleDragEnd = async (event: DragEndEvent) => {\n const { active, over } = event;\n\n if (!active || !over || active.id === over.id) return;\n if (!nodeOrder || nodeOrder.length === 0) return;\n\n const activeId = Number(active.id);\n const overId = Number(over.id);\n\n if (isNaN(activeId) || isNaN(overId)) return;\n\n const oldIndex = nodeOrder.indexOf(activeId);\n const newIndex = nodeOrder.indexOf(overId);\n\n if (oldIndex === -1 || newIndex === -1 || oldIndex === newIndex) return;\n\n const newOrder = arrayMove(nodeOrder, oldIndex, newIndex);\n\n setNodeOrder(newOrder);\n\n saveOrder(\"node-order\", newOrder);\n\n // 持久化到数据库\n try {\n const nodesToUpdate = newOrder.map((id, index) => ({ id, inx: index }));\n const response = await updateNodeOrder({ nodes: nodesToUpdate });\n\n if (response.code === 0) {\n setNodeList((prev) =>\n prev.map((node) => {\n const updated = nodesToUpdate.find((n) => n.id === node.id);\n\n return updated ? { ...node, inx: updated.inx } : node;\n }),\n );\n } else {\n toast.error(\"保存排序失败:\" + (response.msg || \"未知错误\"));\n }\n } catch {\n toast.error(\"保存排序失败,请重试\");\n }\n };\n\n // 批量操作处理函数\n const toggleSelectMode = () => {\n setSelectMode((prev) => {\n if (prev) {\n setSelectedIds(new Set());\n }\n\n return !prev;\n });\n };\n\n const toggleSelect = (id: number) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n\n return next;\n });\n };\n\n const selectAll = () => {\n setSelectedIds(new Set(sortedNodes.map((n) => n.id)));\n };\n\n const deselectAll = () => {\n setSelectedIds(new Set());\n };\n\n const handleBatchDelete = async () => {\n if (selectedIds.size === 0) return;\n setBatchLoading(true);\n try {\n const res = await batchDeleteNodes(Array.from(selectedIds));\n\n if (res.code === 0) {\n toast.success(`成功删除 ${selectedIds.size} 个节点`);\n setNodeList((prev) => prev.filter((n) => !selectedIds.has(n.id)));\n setSelectedIds(new Set());\n setBatchDeleteModalOpen(false);\n setSelectMode(false);\n } else {\n toast.error(res.msg || \"删除失败\");\n }\n } catch {\n toast.error(\"网络错误,请重试\");\n } finally {\n setBatchLoading(false);\n }\n };\n\n // 传感器配置\n const sensors = useSensors(\n useSensor(MouseSensor, {\n activationConstraint: {\n distance: 8,\n },\n }),\n useSensor(TouchSensor, {\n activationConstraint: {\n delay: 250,\n tolerance: 8,\n },\n }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n }),\n );\n\n const nodeExpiryStats = useMemo(() => {\n return nodeList.reduce(\n (acc, node) => {\n const meta = getNodeExpiryMeta(node.expiryTime, node.renewalCycle);\n\n if (meta.state === \"expired\") acc.expired += 1;\n if (meta.state === \"expiringSoon\") acc.expiringSoon += 1;\n if (getNodeReminderEnabled(node)) {\n acc.withExpiry += 1;\n }\n\n return acc;\n },\n { expired: 0, expiringSoon: 0, withExpiry: 0 },\n );\n }, [nodeList]);\n\n // 根据排序顺序获取节点列表\n const sortedNodes = useMemo((): Node[] => {\n if (!nodeList || nodeList.length === 0) return [];\n\n let filteredNodes = nodeList;\n\n if (searchKeyword.trim()) {\n const lowerKeyword = searchKeyword.toLowerCase();\n\n filteredNodes = filteredNodes.filter(\n (n) =>\n (n.name && n.name.toLowerCase().includes(lowerKeyword)) ||\n (n.remark && n.remark.toLowerCase().includes(lowerKeyword)) ||\n (n.tags && n.tags.toLowerCase().includes(lowerKeyword)) ||\n (n.serverIp && n.serverIp.toLowerCase().includes(lowerKeyword)) ||\n (n.serverIpV4 && n.serverIpV4.toLowerCase().includes(lowerKeyword)) ||\n (n.serverIpV6 && n.serverIpV6.toLowerCase().includes(lowerKeyword)),\n );\n }\n\n if (nodeFilterMode !== \"all\") {\n filteredNodes = filteredNodes.filter((node) => {\n const expiryMeta = getNodeExpiryMeta(\n node.expiryTime,\n node.renewalCycle,\n );\n\n switch (nodeFilterMode) {\n case \"expiringSoon\":\n return expiryMeta.state === \"expiringSoon\";\n case \"expired\":\n return expiryMeta.state === \"expired\";\n case \"withExpiry\":\n return getNodeReminderEnabled(node);\n default:\n return true;\n }\n });\n }\n\n const sortedByDb = [...filteredNodes].sort((a, b) => {\n const expiryDiff =\n getNodeExpiryMeta(a.expiryTime, a.renewalCycle).sortWeight -\n getNodeExpiryMeta(b.expiryTime, b.renewalCycle).sortWeight;\n\n if (expiryDiff !== 0) {\n return expiryDiff;\n }\n\n const aInx = a.inx ?? 0;\n const bInx = b.inx ?? 0;\n\n return aInx - bInx;\n });\n\n // 如果数据库中没有排序信息,则使用本地存储的顺序\n if (\n nodeOrder &&\n nodeOrder.length > 0 &&\n sortedByDb.every((n) => n.inx === undefined || n.inx === 0)\n ) {\n const nodeMap = new Map(filteredNodes.map((n) => [n.id, n] as const));\n const localSorted: Node[] = [];\n\n nodeOrder.forEach((id) => {\n const node = nodeMap.get(id);\n\n if (node) localSorted.push(node);\n });\n\n filteredNodes.forEach((node) => {\n if (!nodeOrder.includes(node.id)) {\n localSorted.push(node);\n }\n });\n\n return localSorted;\n }\n\n return sortedByDb;\n }, [nodeFilterMode, nodeList, nodeOrder, searchKeyword]);\n\n const sortableNodeIds = useMemo(\n () => sortedNodes.map((n) => n.id),\n [sortedNodes],\n );\n\n return (\n \n \n
\n setIsSearchVisible(false)}\n onOpen={() => setIsSearchVisible(true)}\n />\n\n \n
\n\n
\n
*]:shrink-0\">\n {selectMode ? (\n <>\n \n 已选 {selectedIds.size} 项\n \n \n \n \n \n \n >\n ) : (\n <>\n \n \n >\n )}\n
\n
\n
\n\n {!wsConnected && (\n \n )}\n\n {/* 节点列表 */}\n {loading ? (\n \n ) : nodeList.length === 0 ? (\n \n ) : (\n \n \n \n {sortedNodes.map((node) => {\n const isRemoteNode = node.isRemote === 1;\n const expiryMeta = getNodeExpiryMeta(\n node.expiryTime,\n node.renewalCycle,\n );\n\n return (\n
\n {(listeners) => (\n \n \n \n
\n {selectMode && (\n toggleSelect(node.id)}\n />\n )}\n \n {node.name}\n
\n
\n
\n {node.expiryTime &&\n node.expiryTime > 0 &&\n node.renewalCycle && (\n
\n {expiryMeta.label}\n \n )}\n
\n {isRemoteNode && (\n
\n 远程\n \n )}\n {(() => {\n const connectionStatusMeta =\n getConnectionStatusMeta(\n node.connectionStatus,\n );\n\n return (\n
\n {connectionStatusMeta.text}\n \n );\n })()}\n
\n
\n \n\n \n {isRemoteNode && node.syncError && (\n \n {getRemoteSyncErrorMessage(node.syncError)}\n
\n )}\n {expiryMeta.isHighlighted && (\n \n 节点到期提醒\n {expiryMeta.label}\n
\n )}\n {/* 基础信息 */}\n \n {(node.remark?.trim() || node.tags?.trim()) && (\n
\n {node.remark?.trim() && (\n
\n
\n {node.remark.trim()}\n
\n
\n )}\n {normalizeNodeTags(node.tags).length > 0 && (\n
\n {normalizeNodeTags(node.tags).map((tag) => (\n \n #{tag}\n \n ))}\n
\n )}\n
\n )}\n {node.expiryTime &&\n node.expiryTime > 0 &&\n node.renewalCycle &&
}\n
\n
\n IP\n \n
\n {node.serverIpV4?.trim() ||\n node.serverIpV6?.trim() ? (\n
\n {node.serverIpV4?.trim() && (\n \n {node.serverIpV4.trim()}\n \n )}\n {node.serverIpV6?.trim() && (\n \n {node.serverIpV6.trim()}\n \n )}\n
\n ) : (\n
\n {node.serverIp.trim()}\n \n )}\n
\n
\n {!isRemoteNode && (\n <>\n
\n 版本\n \n {node.version || \"未知\"}\n \n
\n {upgradeProgress[node.id] &&\n upgradeProgress[node.id].percent < 100 && (\n
\n )}\n
\n \n 开机时间\n \n \n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? formatUptime(node.systemInfo.uptime)\n : \"-\"}\n \n
\n >\n )}\n
\n\n {!isRemoteNode && (\n <>\n {/* 系统监控 */}\n \n
\n
\n
\n CPU\n \n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? `${node.systemInfo.cpuUsage.toFixed(1)}%`\n : \"-\"}\n \n
\n
\n
\n
\n
\n 内存\n \n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? `${node.systemInfo.memoryUsage.toFixed(1)}%`\n : \"-\"}\n \n
\n
\n
\n
\n\n
\n
\n
\n 上传\n
\n
\n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? formatSpeed(\n node.systemInfo.uploadSpeed,\n )\n : \"-\"}\n
\n
\n
\n
\n 下载\n
\n
\n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? formatSpeed(\n node.systemInfo.downloadSpeed,\n )\n : \"-\"}\n
\n
\n
\n\n {/* 流量统计 */}\n
\n
\n
\n ↑ 上行流量\n
\n
\n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? formatTraffic(\n node.systemInfo.uploadTraffic,\n )\n : \"-\"}\n
\n
\n
\n
\n ↓ 下行流量\n
\n
\n {node.connectionStatus === \"online\" &&\n node.systemInfo\n ? formatTraffic(\n node.systemInfo.downloadTraffic,\n )\n : \"-\"}\n
\n
\n
\n
\n >\n )}\n\n {/* 操作按钮 */}\n \n {!isRemoteNode && (\n
\n \n \n \n
\n )}\n
\n {!isRemoteNode && (\n \n )}\n \n
\n
\n \n \n )}\n \n );\n })}\n
\n \n \n )}\n\n {/* 新增/编辑节点对话框 */}\n setDialogVisible(false)}\n >\n \n {dialogTitle}\n \n \n {/* 屏蔽协议 */}\n \n
\n 屏蔽协议\n
\n
\n 开启开关以屏蔽对应协议\n
\n {protocolDisabled && (\n
\n )}\n
\n {/* HTTP tile */}\n
\n
\n
\n
\n HTTP\n
\n
\n
\n
\n 禁用/启用\n
\n
\n setForm((prev) => ({\n ...prev,\n http: v ? 1 : 0,\n }))\n }\n />\n \n
\n {form.http === 1 ? \"已开启\" : \"已关闭\"}\n
\n
\n\n {/* TLS tile */}\n
\n
\n
\n
\n TLS\n
\n
\n
\n
\n 禁用/启用\n
\n
\n setForm((prev) => ({ ...prev, tls: v ? 1 : 0 }))\n }\n />\n \n
\n {form.tls === 1 ? \"已开启\" : \"已关闭\"}\n
\n
\n\n {/* SOCKS tile */}\n
\n
\n
\n
\n SOCKS\n
\n
\n
\n
\n 禁用/启用\n
\n
\n setForm((prev) => ({\n ...prev,\n socks: v ? 1 : 0,\n }))\n }\n />\n \n
\n {form.socks === 1 ? \"已开启\" : \"已关闭\"}\n
\n
\n
\n
\n\n \n