mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-10 21:30:59 +00:00
fix(nodes): make node API tokens write-only (#5613)
* fix(nodes): make node API tokens write-only * fix(nodes): keep token optional on edit for write-only API tokens NodeView no longer returns apiToken, so the edit form must consume hasApiToken and not require re-entering the token. Relaxes the form validation on edit, adds a keep-current placeholder, and adds the i18n key to all 13 locales.
This commit is contained in:
@@ -494,7 +494,6 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"activeCount": 23,
|
||||
"address": "node1.example.com",
|
||||
"allowPrivateAddress": false,
|
||||
"apiToken": "abcdef0123456789",
|
||||
"basePath": "/",
|
||||
"clientCount": 27,
|
||||
"configDirty": false,
|
||||
@@ -535,6 +534,71 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"xrayState": "",
|
||||
"xrayVersion": "25.10.31"
|
||||
},
|
||||
"NodeMutationRequest": {
|
||||
"address": "",
|
||||
"allowPrivateAddress": false,
|
||||
"apiToken": null,
|
||||
"basePath": "",
|
||||
"clearApiToken": false,
|
||||
"enable": false,
|
||||
"id": 0,
|
||||
"inboundSyncMode": "all",
|
||||
"inboundTags": [
|
||||
""
|
||||
],
|
||||
"name": "",
|
||||
"outboundTag": "",
|
||||
"pinnedCertSha256": "",
|
||||
"port": 1,
|
||||
"remark": "",
|
||||
"scheme": "http",
|
||||
"tlsVerifyMode": "verify"
|
||||
},
|
||||
"NodeView": {
|
||||
"activeCount": 20,
|
||||
"address": "node.example.com",
|
||||
"allowPrivateAddress": false,
|
||||
"basePath": "/",
|
||||
"clientCount": 25,
|
||||
"configDirty": false,
|
||||
"configDirtyAt": 0,
|
||||
"cpuPct": 12.5,
|
||||
"createdAt": 1700000000,
|
||||
"depletedCount": 1,
|
||||
"disabledCount": 2,
|
||||
"enable": true,
|
||||
"guid": "node-guid",
|
||||
"hasApiToken": true,
|
||||
"id": 1,
|
||||
"inboundCount": 3,
|
||||
"inboundSyncMode": "all",
|
||||
"inboundTags": [
|
||||
"in-443-tcp"
|
||||
],
|
||||
"lastError": "",
|
||||
"lastHeartbeat": 1700000000,
|
||||
"latencyMs": 42,
|
||||
"memPct": 45.2,
|
||||
"name": "edge-1",
|
||||
"netDown": 1048576,
|
||||
"netUp": 2097152,
|
||||
"onlineCount": 5,
|
||||
"outboundTag": "direct",
|
||||
"panelVersion": "v3.x.x",
|
||||
"parentGuid": "",
|
||||
"pinnedCertSha256": "",
|
||||
"port": 2053,
|
||||
"remark": "Primary edge",
|
||||
"scheme": "https",
|
||||
"status": "online",
|
||||
"tlsVerifyMode": "verify",
|
||||
"transitive": false,
|
||||
"updatedAt": 1700003600,
|
||||
"uptimeSecs": 86400,
|
||||
"xrayError": "",
|
||||
"xrayState": "running",
|
||||
"xrayVersion": "25.10.31"
|
||||
},
|
||||
"OutboundTraffics": {
|
||||
"down": 0,
|
||||
"id": 0,
|
||||
|
||||
@@ -2032,10 +2032,6 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"allowPrivateAddress": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"apiToken": {
|
||||
"example": "abcdef0123456789",
|
||||
"type": "string"
|
||||
},
|
||||
"basePath": {
|
||||
"example": "/",
|
||||
"type": "string"
|
||||
@@ -2206,7 +2202,6 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"activeCount",
|
||||
"address",
|
||||
"allowPrivateAddress",
|
||||
"apiToken",
|
||||
"basePath",
|
||||
"clientCount",
|
||||
"configDirty",
|
||||
@@ -2245,6 +2240,308 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"NodeMutationRequest": {
|
||||
"description": "NodeMutationRequest is the node write/probe contract. ApiToken is accepted\nonly as input. On update, nil means keep the stored token; replacement and\nclearing are explicit and mutually exclusive.",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"allowPrivateAddress": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"apiToken": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"basePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"clearApiToken": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"inboundSyncMode": {
|
||||
"enum": [
|
||||
"all",
|
||||
"selected"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"inboundTags": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"outboundTag": {
|
||||
"type": "string"
|
||||
},
|
||||
"pinnedCertSha256": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"maximum": 65535,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"remark": {
|
||||
"type": "string"
|
||||
},
|
||||
"scheme": {
|
||||
"enum": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"tlsVerifyMode": {
|
||||
"enum": [
|
||||
"verify",
|
||||
"skip",
|
||||
"pin",
|
||||
"mtls"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"address",
|
||||
"allowPrivateAddress",
|
||||
"basePath",
|
||||
"enable",
|
||||
"id",
|
||||
"inboundSyncMode",
|
||||
"inboundTags",
|
||||
"name",
|
||||
"outboundTag",
|
||||
"pinnedCertSha256",
|
||||
"port",
|
||||
"remark",
|
||||
"scheme",
|
||||
"tlsVerifyMode"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"NodeView": {
|
||||
"description": "NodeView is the browser/API read contract for nodes. Credentials are\nwrite-only: responses expose only whether a node has a token configured.",
|
||||
"properties": {
|
||||
"activeCount": {
|
||||
"example": 20,
|
||||
"type": "integer"
|
||||
},
|
||||
"address": {
|
||||
"example": "node.example.com",
|
||||
"type": "string"
|
||||
},
|
||||
"allowPrivateAddress": {
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"basePath": {
|
||||
"example": "/",
|
||||
"type": "string"
|
||||
},
|
||||
"clientCount": {
|
||||
"example": 25,
|
||||
"type": "integer"
|
||||
},
|
||||
"configDirty": {
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"configDirtyAt": {
|
||||
"example": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"cpuPct": {
|
||||
"example": 12.5,
|
||||
"type": "number"
|
||||
},
|
||||
"createdAt": {
|
||||
"example": 1700000000,
|
||||
"type": "integer"
|
||||
},
|
||||
"depletedCount": {
|
||||
"example": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"disabledCount": {
|
||||
"example": 2,
|
||||
"type": "integer"
|
||||
},
|
||||
"enable": {
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"guid": {
|
||||
"example": "node-guid",
|
||||
"type": "string"
|
||||
},
|
||||
"hasApiToken": {
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"example": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"inboundCount": {
|
||||
"example": 3,
|
||||
"type": "integer"
|
||||
},
|
||||
"inboundSyncMode": {
|
||||
"example": "all",
|
||||
"type": "string"
|
||||
},
|
||||
"inboundTags": {
|
||||
"example": [
|
||||
"in-443-tcp"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"lastError": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastHeartbeat": {
|
||||
"example": 1700000000,
|
||||
"type": "integer"
|
||||
},
|
||||
"latencyMs": {
|
||||
"example": 42,
|
||||
"type": "integer"
|
||||
},
|
||||
"memPct": {
|
||||
"example": 45.2,
|
||||
"type": "number"
|
||||
},
|
||||
"name": {
|
||||
"example": "edge-1",
|
||||
"type": "string"
|
||||
},
|
||||
"netDown": {
|
||||
"example": 1048576,
|
||||
"type": "integer"
|
||||
},
|
||||
"netUp": {
|
||||
"example": 2097152,
|
||||
"type": "integer"
|
||||
},
|
||||
"onlineCount": {
|
||||
"example": 5,
|
||||
"type": "integer"
|
||||
},
|
||||
"outboundTag": {
|
||||
"example": "direct",
|
||||
"type": "string"
|
||||
},
|
||||
"panelVersion": {
|
||||
"example": "v3.x.x",
|
||||
"type": "string"
|
||||
},
|
||||
"parentGuid": {
|
||||
"type": "string"
|
||||
},
|
||||
"pinnedCertSha256": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"example": 2053,
|
||||
"type": "integer"
|
||||
},
|
||||
"remark": {
|
||||
"example": "Primary edge",
|
||||
"type": "string"
|
||||
},
|
||||
"scheme": {
|
||||
"example": "https",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"example": "online",
|
||||
"type": "string"
|
||||
},
|
||||
"tlsVerifyMode": {
|
||||
"example": "verify",
|
||||
"type": "string"
|
||||
},
|
||||
"transitive": {
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"updatedAt": {
|
||||
"example": 1700003600,
|
||||
"type": "integer"
|
||||
},
|
||||
"uptimeSecs": {
|
||||
"example": 86400,
|
||||
"type": "integer"
|
||||
},
|
||||
"xrayError": {
|
||||
"type": "string"
|
||||
},
|
||||
"xrayState": {
|
||||
"example": "running",
|
||||
"type": "string"
|
||||
},
|
||||
"xrayVersion": {
|
||||
"example": "25.10.31",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"activeCount",
|
||||
"address",
|
||||
"allowPrivateAddress",
|
||||
"basePath",
|
||||
"clientCount",
|
||||
"configDirty",
|
||||
"configDirtyAt",
|
||||
"cpuPct",
|
||||
"createdAt",
|
||||
"depletedCount",
|
||||
"disabledCount",
|
||||
"enable",
|
||||
"guid",
|
||||
"hasApiToken",
|
||||
"id",
|
||||
"inboundCount",
|
||||
"inboundSyncMode",
|
||||
"inboundTags",
|
||||
"lastError",
|
||||
"lastHeartbeat",
|
||||
"latencyMs",
|
||||
"memPct",
|
||||
"name",
|
||||
"netDown",
|
||||
"netUp",
|
||||
"onlineCount",
|
||||
"outboundTag",
|
||||
"panelVersion",
|
||||
"pinnedCertSha256",
|
||||
"port",
|
||||
"remark",
|
||||
"scheme",
|
||||
"status",
|
||||
"tlsVerifyMode",
|
||||
"updatedAt",
|
||||
"uptimeSecs",
|
||||
"xrayError",
|
||||
"xrayState",
|
||||
"xrayVersion"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"OutboundTraffics": {
|
||||
"description": "OutboundTraffics tracks traffic statistics for Xray outbound connections.",
|
||||
"properties": {
|
||||
|
||||
@@ -477,7 +477,6 @@ export interface Node {
|
||||
activeCount: number;
|
||||
address: string;
|
||||
allowPrivateAddress: boolean;
|
||||
apiToken: string;
|
||||
basePath: string;
|
||||
clientCount: number;
|
||||
configDirty: boolean;
|
||||
@@ -517,6 +516,69 @@ export interface Node {
|
||||
xrayVersion: string;
|
||||
}
|
||||
|
||||
export interface NodeMutationRequest {
|
||||
address: string;
|
||||
allowPrivateAddress: boolean;
|
||||
apiToken?: string | null;
|
||||
basePath: string;
|
||||
clearApiToken?: boolean;
|
||||
enable: boolean;
|
||||
id: number;
|
||||
inboundSyncMode: string;
|
||||
inboundTags: string[];
|
||||
name: string;
|
||||
outboundTag: string;
|
||||
pinnedCertSha256: string;
|
||||
port: number;
|
||||
remark: string;
|
||||
scheme: string;
|
||||
tlsVerifyMode: string;
|
||||
}
|
||||
|
||||
export interface NodeView {
|
||||
activeCount: number;
|
||||
address: string;
|
||||
allowPrivateAddress: boolean;
|
||||
basePath: string;
|
||||
clientCount: number;
|
||||
configDirty: boolean;
|
||||
configDirtyAt: number;
|
||||
cpuPct: number;
|
||||
createdAt: number;
|
||||
depletedCount: number;
|
||||
disabledCount: number;
|
||||
enable: boolean;
|
||||
guid: string;
|
||||
hasApiToken: boolean;
|
||||
id: number;
|
||||
inboundCount: number;
|
||||
inboundSyncMode: string;
|
||||
inboundTags: string[];
|
||||
lastError: string;
|
||||
lastHeartbeat: number;
|
||||
latencyMs: number;
|
||||
memPct: number;
|
||||
name: string;
|
||||
netDown: number;
|
||||
netUp: number;
|
||||
onlineCount: number;
|
||||
outboundTag: string;
|
||||
panelVersion: string;
|
||||
parentGuid?: string;
|
||||
pinnedCertSha256: string;
|
||||
port: number;
|
||||
remark: string;
|
||||
scheme: string;
|
||||
status: string;
|
||||
tlsVerifyMode: string;
|
||||
transitive?: boolean;
|
||||
updatedAt: number;
|
||||
uptimeSecs: number;
|
||||
xrayError: string;
|
||||
xrayState: string;
|
||||
xrayVersion: string;
|
||||
}
|
||||
|
||||
export interface OutboundTraffics {
|
||||
down: number;
|
||||
id: number;
|
||||
|
||||
@@ -507,7 +507,6 @@ export const NodeSchema = z.object({
|
||||
activeCount: z.number().int(),
|
||||
address: z.string(),
|
||||
allowPrivateAddress: z.boolean(),
|
||||
apiToken: z.string(),
|
||||
basePath: z.string(),
|
||||
clientCount: z.number().int(),
|
||||
configDirty: z.boolean(),
|
||||
@@ -548,6 +547,71 @@ export const NodeSchema = z.object({
|
||||
});
|
||||
export type Node = z.infer<typeof NodeSchema>;
|
||||
|
||||
export const NodeMutationRequestSchema = z.object({
|
||||
address: z.string(),
|
||||
allowPrivateAddress: z.boolean(),
|
||||
apiToken: z.string().nullable().optional(),
|
||||
basePath: z.string(),
|
||||
clearApiToken: z.boolean().optional(),
|
||||
enable: z.boolean(),
|
||||
id: z.number().int(),
|
||||
inboundSyncMode: z.enum(['all', 'selected']),
|
||||
inboundTags: z.array(z.string()),
|
||||
name: z.string(),
|
||||
outboundTag: z.string(),
|
||||
pinnedCertSha256: z.string(),
|
||||
port: z.number().int().min(1).max(65535),
|
||||
remark: z.string(),
|
||||
scheme: z.enum(['http', 'https']),
|
||||
tlsVerifyMode: z.enum(['verify', 'skip', 'pin', 'mtls']),
|
||||
});
|
||||
export type NodeMutationRequest = z.infer<typeof NodeMutationRequestSchema>;
|
||||
|
||||
export const NodeViewSchema = z.object({
|
||||
activeCount: z.number().int(),
|
||||
address: z.string(),
|
||||
allowPrivateAddress: z.boolean(),
|
||||
basePath: z.string(),
|
||||
clientCount: z.number().int(),
|
||||
configDirty: z.boolean(),
|
||||
configDirtyAt: z.number().int(),
|
||||
cpuPct: z.number(),
|
||||
createdAt: z.number().int(),
|
||||
depletedCount: z.number().int(),
|
||||
disabledCount: z.number().int(),
|
||||
enable: z.boolean(),
|
||||
guid: z.string(),
|
||||
hasApiToken: z.boolean(),
|
||||
id: z.number().int(),
|
||||
inboundCount: z.number().int(),
|
||||
inboundSyncMode: z.string(),
|
||||
inboundTags: z.array(z.string()),
|
||||
lastError: z.string(),
|
||||
lastHeartbeat: z.number().int(),
|
||||
latencyMs: z.number().int(),
|
||||
memPct: z.number(),
|
||||
name: z.string(),
|
||||
netDown: z.number().int(),
|
||||
netUp: z.number().int(),
|
||||
onlineCount: z.number().int(),
|
||||
outboundTag: z.string(),
|
||||
panelVersion: z.string(),
|
||||
parentGuid: z.string().optional(),
|
||||
pinnedCertSha256: z.string(),
|
||||
port: z.number().int(),
|
||||
remark: z.string(),
|
||||
scheme: z.string(),
|
||||
status: z.string(),
|
||||
tlsVerifyMode: z.string(),
|
||||
transitive: z.boolean().optional(),
|
||||
updatedAt: z.number().int(),
|
||||
uptimeSecs: z.number().int(),
|
||||
xrayError: z.string(),
|
||||
xrayState: z.string(),
|
||||
xrayVersion: z.string(),
|
||||
});
|
||||
export type NodeView = z.infer<typeof NodeViewSchema>;
|
||||
|
||||
export const OutboundTrafficsSchema = z.object({
|
||||
down: z.number().int(),
|
||||
id: z.number().int(),
|
||||
|
||||
Reference in New Issue
Block a user