feat(api): add client lookup by subscription ID (#6908)

Integrations could only find a client by subscription ID through a substring
search over the paged client list followed by a second lookup by email.
GET /panel/api/clients/get/subId/:subId now returns the full client records
(settings, attached inbounds, external links, traffic, tunnel addresses) for an
exact subId match, as a list because older rows may share an ID. It sits behind
the same admin login or admin-token check as the email lookup.
This commit is contained in:
CHANGAO MA
2026-10-24 10:54:14 +08:00
committed by GitHub
parent 3051aebb28
commit 0516a1f87c
6 changed files with 369 additions and 1 deletions
+75
View File
@@ -8501,6 +8501,81 @@
}
}
},
"/panel/api/clients/get/subId/{subId}": {
"get": {
"tags": [
"Clients"
],
"summary": "Fetch clients by subscription ID.",
"operationId": "get_panel_api_clients_get_subId_subId",
"description": "Matches the exact subscription ID and orders results by client ID. Returns an array of the same payloads as GET /clients/get/:email, since multiple clients can share a subscription ID. Includes disabled, expired and unattached clients. An unknown ID returns HTTP 200 with success:false, obj:null and a record-not-found message, matching the email lookup. Requires an admin token or a panel login session.",
"parameters": [
{
"name": "subId",
"in": "path",
"required": true,
"description": "Exact subscription ID (URL-encoded).",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"msg": {
"type": "string"
},
"obj": {
"type": "array",
"nullable": true,
"items": {
"type": "object",
"properties": {
"client": {
"$ref": "#/components/schemas/ClientRecord"
},
"inboundIds": {
"type": "array",
"items": {
"type": "integer"
}
},
"externalLinks": {
"type": "array",
"items": {
"type": "object"
}
},
"usedTraffic": {
"type": "integer",
"format": "int64"
},
"tunnelAllowedIPs": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/panel/api/clients/get/tgId/{tgId}": {
"get": {
"tags": [
+29
View File
@@ -1142,6 +1142,35 @@ export const sections: readonly Section[] = [
response:
'{\n "success": true,\n "obj": {\n "client": { "id": 1, "email": "alice@example.com", ... },\n "inboundIds": [3, 5],\n "externalLinks": [\n { "id": 11, "kind": "link", "value": "vless://...", "remark": "DE", "enable": true, "expiryTime": 0 },\n { "id": 12, "kind": "subscription", "value": "https://provider.example/sub/abc", "remark": "Provider", "enable": false, "expiryTime": 1767225600000, "namePrefix": "[zjh] ", "lastFetchAt": 1767220000000, "lastFetchError": "" }\n ]\n }\n}',
},
{
method: 'GET',
path: '/panel/api/clients/get/subId/:subId',
summary: 'Fetch clients by subscription ID.',
description:
'Matches the exact subscription ID and orders results by client ID. Returns an array of the same payloads as GET /clients/get/:email, since multiple clients can share a subscription ID. Includes disabled, expired and unattached clients. An unknown ID returns HTTP 200 with success:false, obj:null and a record-not-found message, matching the email lookup. Requires an admin token or a panel login session.',
params: [
{
name: 'subId',
in: 'path',
type: 'string',
desc: 'Exact subscription ID (URL-encoded).',
},
],
responseObjectSchema: {
type: 'array',
nullable: true,
items: {
type: 'object',
properties: {
client: { $ref: '#/components/schemas/ClientRecord' },
inboundIds: { type: 'array', items: { type: 'integer' } },
externalLinks: { type: 'array', items: { type: 'object' } },
usedTraffic: { type: 'integer', format: 'int64' },
tunnelAllowedIPs: { type: 'object', additionalProperties: { type: 'string' } },
},
},
},
},
{
method: 'GET',
path: '/panel/api/clients/get/tgId/:tgId',