@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -1
- package/README.md +11 -2
- package/dist/omadaClient/client.d.ts +6 -1
- package/dist/omadaClient/client.js +12 -1
- package/dist/omadaClient/client.js.map +1 -1
- package/dist/omadaClient/diagnose.d.ts +68 -0
- package/dist/omadaClient/diagnose.js +164 -0
- package/dist/omadaClient/diagnose.js.map +1 -0
- package/dist/omadaClient/index.d.ts +6 -0
- package/dist/omadaClient/index.js +15 -0
- package/dist/omadaClient/index.js.map +1 -1
- package/dist/omadaClient/macGroups.d.ts +58 -0
- package/dist/omadaClient/macGroups.js +124 -0
- package/dist/omadaClient/macGroups.js.map +1 -0
- package/dist/tools/infra/actions.d.ts +1 -1
- package/dist/tools/infra/actions.js +1 -1
- package/dist/tools/omada/index.js +6 -0
- package/dist/tools/omada/index.js.map +1 -1
- package/dist/tools/omada/macGroups.d.ts +19 -0
- package/dist/tools/omada/macGroups.js +77 -0
- package/dist/tools/omada/macGroups.js.map +1 -0
- package/dist/tools/omada/namespace.js +8 -0
- package/dist/tools/omada/namespace.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/.codex/review-rules.md +0 -16
- package/.dockerignore +0 -8
- package/.gitattributes +0 -30
- package/.github/workflows/ci.yml +0 -193
- package/AGENTS.md +0 -389
- package/Dockerfile +0 -50
- package/docker-compose.yml +0 -23
- package/docs/CHANGELOG.md +0 -341
- package/docs/COVERAGE.md +0 -219
- package/docs/QUICK_START.md +0 -240
- package/eslint.config.js +0 -33
- package/n8n/ai-agent-safety-monitor.json +0 -355
- package/openapi.json +0 -1259
- package/scripts/check-version-sync.mjs +0 -69
- package/scripts/generate-jwt.ts +0 -121
- package/src/config.ts +0 -440
- package/src/haClient/automations.ts +0 -261
- package/src/haClient/calendars.ts +0 -94
- package/src/haClient/config.ts +0 -52
- package/src/haClient/devices.ts +0 -653
- package/src/haClient/entities.ts +0 -128
- package/src/haClient/events.ts +0 -689
- package/src/haClient/history.ts +0 -73
- package/src/haClient/index.ts +0 -336
- package/src/haClient/request.ts +0 -142
- package/src/haClient/services.ts +0 -45
- package/src/haClient/states.ts +0 -60
- package/src/haClient/updates.ts +0 -58
- package/src/haClient/wsCommand.ts +0 -44
- package/src/index.ts +0 -164
- package/src/localAI/index.ts +0 -88
- package/src/localAI/prompts.ts +0 -50
- package/src/localAI/providers/base.ts +0 -120
- package/src/localAI/providers/ollama.ts +0 -78
- package/src/localAI/providers/openai.ts +0 -86
- package/src/localAI/types.ts +0 -67
- package/src/omadaClient/auth.ts +0 -178
- package/src/omadaClient/client.ts +0 -337
- package/src/omadaClient/device.ts +0 -118
- package/src/omadaClient/index.ts +0 -310
- package/src/omadaClient/network.ts +0 -284
- package/src/omadaClient/request.ts +0 -365
- package/src/omadaClient/security.ts +0 -51
- package/src/omadaClient/site.ts +0 -344
- package/src/ownerQuestions/service.ts +0 -536
- package/src/permissions/index.ts +0 -143
- package/src/resources/index.ts +0 -213
- package/src/server/auth.ts +0 -175
- package/src/server/chatFace.ts +0 -789
- package/src/server/chatSlice.ts +0 -57
- package/src/server/common.ts +0 -170
- package/src/server/eventSubscription.ts +0 -343
- package/src/server/http.ts +0 -626
- package/src/server/instructions.ts +0 -97
- package/src/server/rateLimiter.ts +0 -227
- package/src/server/stdio.ts +0 -41
- package/src/server/stream.ts +0 -205
- package/src/tools/ai/analyzeSensors.ts +0 -33
- package/src/tools/ai/index.ts +0 -20
- package/src/tools/common.ts +0 -442
- package/src/tools/homeassistant/askOwner.ts +0 -133
- package/src/tools/homeassistant/automationControls.ts +0 -66
- package/src/tools/homeassistant/calendar.ts +0 -97
- package/src/tools/homeassistant/callService.ts +0 -25
- package/src/tools/homeassistant/controlClimate.ts +0 -68
- package/src/tools/homeassistant/controlCover.ts +0 -48
- package/src/tools/homeassistant/controlFan.ts +0 -44
- package/src/tools/homeassistant/controlLight.ts +0 -38
- package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
- package/src/tools/homeassistant/createAutomation.ts +0 -35
- package/src/tools/homeassistant/deleteAutomation.ts +0 -24
- package/src/tools/homeassistant/entityAction.ts +0 -35
- package/src/tools/homeassistant/entityGuard.ts +0 -28
- package/src/tools/homeassistant/getAllSensors.ts +0 -46
- package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
- package/src/tools/homeassistant/getDomainSummary.ts +0 -29
- package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
- package/src/tools/homeassistant/getHistory.ts +0 -24
- package/src/tools/homeassistant/getState.ts +0 -24
- package/src/tools/homeassistant/getStates.ts +0 -46
- package/src/tools/homeassistant/getVersion.ts +0 -21
- package/src/tools/homeassistant/index.ts +0 -121
- package/src/tools/homeassistant/listAutomations.ts +0 -35
- package/src/tools/homeassistant/listEntities.ts +0 -44
- package/src/tools/homeassistant/listPersons.ts +0 -46
- package/src/tools/homeassistant/sceneAndScript.ts +0 -53
- package/src/tools/homeassistant/searchEntities.ts +0 -58
- package/src/tools/homeassistant/sendNotification.ts +0 -179
- package/src/tools/homeassistant/systemTools.ts +0 -52
- package/src/tools/homeassistant/triggerAutomation.ts +0 -24
- package/src/tools/index.ts +0 -78
- package/src/tools/infra/actions.ts +0 -95
- package/src/tools/infra/index.ts +0 -138
- package/src/tools/omada/blockClient.ts +0 -28
- package/src/tools/omada/cyclePoePort.ts +0 -32
- package/src/tools/omada/deleteClient.ts +0 -27
- package/src/tools/omada/disableClientRateLimit.ts +0 -23
- package/src/tools/omada/getClient.ts +0 -14
- package/src/tools/omada/getDevice.ts +0 -14
- package/src/tools/omada/getFirewallSetting.ts +0 -14
- package/src/tools/omada/getInternetInfo.ts +0 -14
- package/src/tools/omada/getLanNetworkList.ts +0 -14
- package/src/tools/omada/getLanProfileList.ts +0 -14
- package/src/tools/omada/getPortForwardingStatus.ts +0 -26
- package/src/tools/omada/getRateLimitProfiles.ts +0 -14
- package/src/tools/omada/getSiteNtpStatus.ts +0 -14
- package/src/tools/omada/getSsidDetail.ts +0 -22
- package/src/tools/omada/getSsidList.ts +0 -21
- package/src/tools/omada/getSwitchStackDetail.ts +0 -14
- package/src/tools/omada/getThreatList.ts +0 -42
- package/src/tools/omada/getWlanGroupList.ts +0 -14
- package/src/tools/omada/graph.ts +0 -187
- package/src/tools/omada/index.ts +0 -153
- package/src/tools/omada/listBlockedClients.ts +0 -17
- package/src/tools/omada/listClients.ts +0 -14
- package/src/tools/omada/listClientsActivity.ts +0 -25
- package/src/tools/omada/listClientsPastConnections.ts +0 -44
- package/src/tools/omada/listDevices.ts +0 -14
- package/src/tools/omada/listDevicesStats.ts +0 -41
- package/src/tools/omada/listMostActiveClients.ts +0 -14
- package/src/tools/omada/listSites.ts +0 -18
- package/src/tools/omada/namespace.ts +0 -1164
- package/src/tools/omada/searchDevices.ts +0 -20
- package/src/tools/omada/setClientRateLimit.ts +0 -26
- package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
- package/src/tools/omada/setSiteNtpServers.ts +0 -36
- package/src/tools/omada/setSsidEnabled.ts +0 -38
- package/src/tools/omada/unblockClient.ts +0 -26
- package/src/tools/types.ts +0 -11
- package/src/tools/utils/pagination-schema.ts +0 -27
- package/src/types/ai/index.ts +0 -11
- package/src/types/ai/types.ts +0 -67
- package/src/types/homeassistant/automations.ts +0 -43
- package/src/types/homeassistant/calendars.ts +0 -26
- package/src/types/homeassistant/config.ts +0 -14
- package/src/types/homeassistant/entities.ts +0 -14
- package/src/types/homeassistant/errors.ts +0 -33
- package/src/types/homeassistant/haConfig.ts +0 -7
- package/src/types/homeassistant/history.ts +0 -13
- package/src/types/homeassistant/index.ts +0 -46
- package/src/types/homeassistant/request.ts +0 -6
- package/src/types/homeassistant/serviceCall.ts +0 -18
- package/src/types/homeassistant/states.ts +0 -58
- package/src/types/homeassistant/updates.ts +0 -28
- package/src/types/index.ts +0 -12
- package/src/types/omada/activeClientInfo.ts +0 -17
- package/src/types/omada/clientActivity.ts +0 -32
- package/src/types/omada/clientBlockStatus.ts +0 -11
- package/src/types/omada/clientPastConnection.ts +0 -82
- package/src/types/omada/index.ts +0 -14
- package/src/types/omada/omadaApiResponse.ts +0 -5
- package/src/types/omada/omadaClientInfo.ts +0 -11
- package/src/types/omada/omadaDeviceInfo.ts +0 -11
- package/src/types/omada/omadaDeviceStats.ts +0 -31
- package/src/types/omada/omadaSiteSummary.ts +0 -5
- package/src/types/omada/oswStackDetail.ts +0 -11
- package/src/types/omada/paginatedResult.ts +0 -6
- package/src/types/omada/rateLimitProfile.ts +0 -58
- package/src/types/omada/threatInfo.ts +0 -38
- package/src/types/omada/tokenResult.ts +0 -6
- package/src/types.ts +0 -2
- package/src/utils/config-validations.ts +0 -68
- package/src/utils/jwt.ts +0 -112
- package/src/utils/logger.ts +0 -121
- package/src/utils/plainText.ts +0 -48
- package/src/utils/redact.ts +0 -59
- package/src/utils/sanitizeError.ts +0 -18
- package/src/utils/tlsDispatcher.ts +0 -69
- package/src/version.ts +0 -5
- package/tests/bdd/automation-creation.spec.ts +0 -214
- package/tests/bdd/automation-management.spec.ts +0 -267
- package/tests/bdd/calendar.spec.ts +0 -190
- package/tests/bdd/configuration.spec.ts +0 -283
- package/tests/bdd/device-control.spec.ts +0 -344
- package/tests/bdd/entity-listing.spec.ts +0 -208
- package/tests/bdd/entity-search.spec.ts +0 -183
- package/tests/bdd/history-retrieval.spec.ts +0 -195
- package/tests/bdd/notifications.spec.ts +0 -238
- package/tests/bdd/service-calls.spec.ts +0 -144
- package/tests/bdd/state-retrieval.spec.ts +0 -204
- package/tests/bench/hotpaths.bench.ts +0 -68
- package/tests/config.test.ts +0 -355
- package/tests/features/automation-creation.feature +0 -36
- package/tests/features/automation-management.feature +0 -54
- package/tests/features/calendar.feature +0 -37
- package/tests/features/configuration.feature +0 -70
- package/tests/features/device-control.feature +0 -100
- package/tests/features/entity-listing.feature +0 -51
- package/tests/features/entity-search.feature +0 -44
- package/tests/features/history-retrieval.feature +0 -38
- package/tests/features/notifications.feature +0 -37
- package/tests/features/service-calls.feature +0 -35
- package/tests/features/state-retrieval.feature +0 -37
- package/tests/fixtures/openapi-default-slice.json +0 -290
- package/tests/haClient/automationTrace.test.ts +0 -59
- package/tests/haClient/automations.test.ts +0 -149
- package/tests/haClient/config.test.ts +0 -117
- package/tests/haClient/entities.test.ts +0 -188
- package/tests/haClient/events.test.ts +0 -455
- package/tests/haClient/eventsTls.test.ts +0 -52
- package/tests/haClient/history.test.ts +0 -228
- package/tests/haClient/index.test.ts +0 -243
- package/tests/haClient/request.test.ts +0 -217
- package/tests/haClient/services.test.ts +0 -120
- package/tests/haClient/states.test.ts +0 -119
- package/tests/haClient/updates.test.ts +0 -193
- package/tests/index.test.ts +0 -160
- package/tests/integration/homeassistant.test.ts +0 -144
- package/tests/integration/omada.test.ts +0 -167
- package/tests/integration/server.test.ts +0 -151
- package/tests/localAI/index.test.ts +0 -177
- package/tests/localAI/ollama.test.ts +0 -187
- package/tests/localAI/openai.test.ts +0 -257
- package/tests/omadaClient/actions.test.ts +0 -161
- package/tests/omadaClient/clientBlocks.test.ts +0 -170
- package/tests/omadaClient/ntp.test.ts +0 -381
- package/tests/omadaClient/site.test.ts +0 -37
- package/tests/ownerQuestions/service.test.ts +0 -499
- package/tests/ownerQuestions/tools.test.ts +0 -186
- package/tests/permissions/index.test.ts +0 -217
- package/tests/resources/index.test.ts +0 -288
- package/tests/scripts/generateJwt.test.ts +0 -44
- package/tests/security/accessControl.test.ts +0 -223
- package/tests/security/authentication.test.ts +0 -117
- package/tests/security/configuration.test.ts +0 -170
- package/tests/security/cryptoTransport.test.ts +0 -172
- package/tests/security/dependencies.test.ts +0 -152
- package/tests/security/injection.test.ts +0 -117
- package/tests/security/insecureDesign.test.ts +0 -121
- package/tests/security/logging.test.ts +0 -131
- package/tests/security/resourceConsumption.test.ts +0 -142
- package/tests/server/auth.test.ts +0 -249
- package/tests/server/chatFace.test.ts +0 -320
- package/tests/server/common.test.ts +0 -84
- package/tests/server/http.test.ts +0 -561
- package/tests/server/instructions.test.ts +0 -152
- package/tests/server/memoizeJsonSchema.test.ts +0 -71
- package/tests/server/stdio.test.ts +0 -99
- package/tests/server/stream.test.ts +0 -285
- package/tests/server/toolVisibility.http.test.ts +0 -111
- package/tests/server/toolVisibility.test.ts +0 -129
- package/tests/tools/ai/analyzeSensors.test.ts +0 -227
- package/tests/tools/common.test.ts +0 -123
- package/tests/tools/homeassistant/handlers.test.ts +0 -872
- package/tests/tools/infra/invokeAction.test.ts +0 -280
- package/tests/tools/omada/graph.test.ts +0 -385
- package/tests/tools/omada/handlers.test.ts +0 -897
- package/tests/tools/omada/namespace.test.ts +0 -483
- package/tests/tools/registry.test.ts +0 -168
- package/tests/utils/config-validations.test.ts +0 -352
- package/tests/utils/jwt.test.ts +0 -115
- package/tests/utils/logger.test.ts +0 -327
- package/tests/utils/redact.test.ts +0 -105
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -38
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import type { OmadaApiResponse, PaginatedResult } from '../types/index.js';
|
|
2
|
-
|
|
3
|
-
import type { RequestHandler } from './request.js';
|
|
4
|
-
import type { SiteOperations } from './site.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Network-related operations for the Omada API.
|
|
8
|
-
* Covers internet, LAN, WLAN, firewall, and port forwarding configurations.
|
|
9
|
-
*/
|
|
10
|
-
export class NetworkOperations {
|
|
11
|
-
constructor(
|
|
12
|
-
private readonly request: RequestHandler,
|
|
13
|
-
private readonly site: SiteOperations,
|
|
14
|
-
private readonly buildPath: (path: string, version?: string) => string
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Get internet configuration info for a site.
|
|
19
|
-
* OperationId: getInternet
|
|
20
|
-
*/
|
|
21
|
-
public async getInternetInfo(siteId?: string): Promise<unknown> {
|
|
22
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
23
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/internet`);
|
|
24
|
-
const response = await this.request.get<OmadaApiResponse<unknown>>(path);
|
|
25
|
-
return this.request.ensureSuccess(response);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get port forwarding status for a specific type (User or UPnP).
|
|
30
|
-
* OperationId: getPortForwardStatus
|
|
31
|
-
*
|
|
32
|
-
* @param type - Port forwarding type: 'User' or 'UPnP'
|
|
33
|
-
* @param siteId - Optional site ID (uses default if not provided)
|
|
34
|
-
* @param page - Page number (required by API, default: 1)
|
|
35
|
-
* @param pageSize - Page size (required by API, range: 1-1000, default: 10)
|
|
36
|
-
*/
|
|
37
|
-
public async getPortForwardingStatus(type: 'User' | 'UPnP', siteId?: string, page = 1, pageSize = 10): Promise<PaginatedResult<unknown>> {
|
|
38
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
39
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/insight/port-forwarding/${encodeURIComponent(type)}`);
|
|
40
|
-
|
|
41
|
-
const response = await this.request.get<OmadaApiResponse<PaginatedResult<unknown>>>(path, {
|
|
42
|
-
page,
|
|
43
|
-
pageSize,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return this.request.ensureSuccess(response);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get LAN network list (v2 API) with pagination.
|
|
51
|
-
* OperationId: getLanNetworkListV2
|
|
52
|
-
*/
|
|
53
|
-
public async getLanNetworkList(siteId?: string): Promise<unknown[]> {
|
|
54
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
55
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/lan-networks`, 'v2');
|
|
56
|
-
return await this.request.fetchPaginated<unknown>(path);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Get LAN profile list with pagination.
|
|
61
|
-
* OperationId: getLanProfileList
|
|
62
|
-
*/
|
|
63
|
-
public async getLanProfileList(siteId?: string): Promise<unknown[]> {
|
|
64
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
65
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/lan-profiles`);
|
|
66
|
-
return await this.request.fetchPaginated<unknown>(path);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get WLAN group list.
|
|
71
|
-
* OperationId: getWlanGroupList
|
|
72
|
-
*/
|
|
73
|
-
public async getWlanGroupList(siteId?: string): Promise<unknown[]> {
|
|
74
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
75
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans`);
|
|
76
|
-
const response = await this.request.get<OmadaApiResponse<unknown[]>>(path);
|
|
77
|
-
return this.request.ensureSuccess(response);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Get SSID list for a specific WLAN group.
|
|
82
|
-
* OperationId: getSsidList
|
|
83
|
-
*
|
|
84
|
-
* @param wlanId - WLAN group ID (can be obtained from getWlanGroupList)
|
|
85
|
-
*/
|
|
86
|
-
public async getSsidList(wlanId: string, siteId?: string): Promise<unknown[]> {
|
|
87
|
-
if (!wlanId) {
|
|
88
|
-
throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
92
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans/${encodeURIComponent(wlanId)}/ssids`);
|
|
93
|
-
return await this.request.fetchPaginated<unknown>(path);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get detailed information for a specific SSID.
|
|
98
|
-
* OperationId: getSsidDetail
|
|
99
|
-
*
|
|
100
|
-
* @param wlanId - WLAN group ID (can be obtained from getWlanGroupList)
|
|
101
|
-
* @param ssidId - SSID ID (can be obtained from getSsidList)
|
|
102
|
-
*/
|
|
103
|
-
public async getSsidDetail(wlanId: string, ssidId: string, siteId?: string): Promise<unknown> {
|
|
104
|
-
if (!wlanId) {
|
|
105
|
-
throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
|
|
106
|
-
}
|
|
107
|
-
if (!ssidId) {
|
|
108
|
-
throw new Error('An ssidId must be provided. Use omada_read at path /wifi/ssids to get available SSID IDs.');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
112
|
-
const path = this.buildPath(
|
|
113
|
-
`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans/${encodeURIComponent(wlanId)}/ssids/${encodeURIComponent(ssidId)}`
|
|
114
|
-
);
|
|
115
|
-
const response = await this.request.get<OmadaApiResponse<unknown>>(path);
|
|
116
|
-
return this.request.ensureSuccess(response);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Get firewall settings for a site.
|
|
121
|
-
* OperationId: getFirewallSetting
|
|
122
|
-
*/
|
|
123
|
-
public async getFirewallSetting(siteId?: string): Promise<unknown> {
|
|
124
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
125
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/firewall`);
|
|
126
|
-
const response = await this.request.get<OmadaApiResponse<unknown>>(path);
|
|
127
|
-
return this.request.ensureSuccess(response);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* List every SSID on the site, flattened across WLAN groups.
|
|
132
|
-
* OperationId: listAllSsids — returns [{wlanId, wlanName, ssidList:[{ssidId, ssidName}]}].
|
|
133
|
-
*/
|
|
134
|
-
public async listAllSsids(siteId?: string): Promise<WlanSimpleInfo[]> {
|
|
135
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
136
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/ssids`);
|
|
137
|
-
const response = await this.request.get<OmadaApiResponse<WlanSimpleInfo[]>>(path);
|
|
138
|
-
return this.request.ensureSuccess(response);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* List time-range profiles for the site.
|
|
143
|
-
* OperationId: listTimeRangeProfiles — returns [{profileId, name, dayMode, ...}].
|
|
144
|
-
*/
|
|
145
|
-
public async listTimeRangeProfiles(siteId?: string): Promise<TimeRangeProfileInfo[]> {
|
|
146
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
147
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/time-range-profiles`);
|
|
148
|
-
const response = await this.request.get<OmadaApiResponse<TimeRangeProfileInfo[]>>(path);
|
|
149
|
-
return this.request.ensureSuccess(response);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Create a time-range profile. The API returns no profileId — callers must
|
|
154
|
-
* re-list and match by name to obtain the created profile's id.
|
|
155
|
-
* OperationId: createSitesTimeRangeProfiles
|
|
156
|
-
*/
|
|
157
|
-
public async createTimeRangeProfile(profile: CreateTimeRangeProfile, siteId?: string): Promise<void> {
|
|
158
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
159
|
-
const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/time-range-profiles`);
|
|
160
|
-
const response = await this.request.post<OmadaApiResponse<unknown>>(path, profile);
|
|
161
|
-
this.request.ensureSuccess(response);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Update the WLAN-schedule config of a single SSID.
|
|
166
|
-
* OperationId: updateSitesWirelessNetworkWlansSsidsUpdateWlanSchedule
|
|
167
|
-
* Omada has no direct SSID on/off — the WLAN schedule (action 0 = radio off
|
|
168
|
-
* during the scheduled period) is the controller's sanctioned mechanism.
|
|
169
|
-
*/
|
|
170
|
-
public async updateSsidWlanSchedule(
|
|
171
|
-
wlanId: string,
|
|
172
|
-
ssidId: string,
|
|
173
|
-
schedule: { wlanScheduleEnable: boolean; action?: number; scheduleId?: string },
|
|
174
|
-
siteId?: string
|
|
175
|
-
): Promise<void> {
|
|
176
|
-
const resolvedSiteId = this.site.resolveSiteId(siteId);
|
|
177
|
-
const path = this.buildPath(
|
|
178
|
-
`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans/${encodeURIComponent(wlanId)}/ssids/${encodeURIComponent(ssidId)}/update-wlan-schedule`
|
|
179
|
-
);
|
|
180
|
-
const response = await this.request.patch<OmadaApiResponse<unknown>>(path, schedule);
|
|
181
|
-
this.request.ensureSuccess(response);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Enable or disable an SSID (e.g. the guest network) by name or ssidId.
|
|
186
|
-
*
|
|
187
|
-
* Omada 6.x exposes no direct enable flag on an SSID; the sanctioned lever is
|
|
188
|
-
* the per-SSID WLAN schedule. Disabling applies an always-on "radio off"
|
|
189
|
-
* schedule (a 24/7 time-range profile is created once and reused); enabling
|
|
190
|
-
* simply turns the schedule off again. Fully reversible, and unlike a MAC
|
|
191
|
-
* filter it actually stops the SSID from broadcasting.
|
|
192
|
-
*/
|
|
193
|
-
public async setSsidEnabled(ssid: string, enabled: boolean, siteId?: string): Promise<SsidEnableResult> {
|
|
194
|
-
const wlans = await this.listAllSsids(siteId);
|
|
195
|
-
const needle = ssid.trim().toLowerCase();
|
|
196
|
-
let match: { wlanId: string; ssidId: string; ssidName: string } | undefined;
|
|
197
|
-
for (const wlan of wlans) {
|
|
198
|
-
for (const s of wlan.ssidList ?? []) {
|
|
199
|
-
if (s.ssidId === ssid || s.ssidName.toLowerCase() === needle) {
|
|
200
|
-
match = { wlanId: wlan.wlanId, ssidId: s.ssidId, ssidName: s.ssidName };
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (match) break;
|
|
205
|
-
}
|
|
206
|
-
if (!match) {
|
|
207
|
-
const available = wlans.flatMap((w) => (w.ssidList ?? []).map((s) => s.ssidName)).join(', ');
|
|
208
|
-
throw new Error(`SSID '${ssid}' not found. Available SSIDs: ${available || '(none)'}`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (enabled) {
|
|
212
|
-
await this.updateSsidWlanSchedule(match.wlanId, match.ssidId, { wlanScheduleEnable: false }, siteId);
|
|
213
|
-
return { ...match, enabled: true, method: 'wlan-schedule disabled' };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const scheduleId = await this.ensureAlwaysProfile(siteId);
|
|
217
|
-
await this.updateSsidWlanSchedule(
|
|
218
|
-
match.wlanId,
|
|
219
|
-
match.ssidId,
|
|
220
|
-
{ wlanScheduleEnable: true, action: 0, scheduleId },
|
|
221
|
-
siteId
|
|
222
|
-
);
|
|
223
|
-
return { ...match, enabled: false, method: 'wlan-schedule radio-off 24/7', scheduleId };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Find-or-create the 24/7 time-range profile used to switch SSIDs off.
|
|
228
|
-
* Created once per site under a fixed name, then reused forever.
|
|
229
|
-
*/
|
|
230
|
-
private async ensureAlwaysProfile(siteId?: string): Promise<string> {
|
|
231
|
-
const existing = await this.listTimeRangeProfiles(siteId);
|
|
232
|
-
const found = existing.find((p) => p.name === ALWAYS_PROFILE_NAME);
|
|
233
|
-
if (found) {
|
|
234
|
-
return found.profileId;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
await this.createTimeRangeProfile(
|
|
238
|
-
{
|
|
239
|
-
name: ALWAYS_PROFILE_NAME,
|
|
240
|
-
dayMode: 0, // Every Day
|
|
241
|
-
timeList: [{ dayType: 1, startTimeH: 0, startTimeM: 0, endTimeH: 24, endTimeM: 0 }],
|
|
242
|
-
},
|
|
243
|
-
siteId
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
// The create endpoint returns no id — re-list and match by name.
|
|
247
|
-
const after = await this.listTimeRangeProfiles(siteId);
|
|
248
|
-
const created = after.find((p) => p.name === ALWAYS_PROFILE_NAME);
|
|
249
|
-
if (!created) {
|
|
250
|
-
throw new Error(`Time-range profile '${ALWAYS_PROFILE_NAME}' was created but not found on re-list`);
|
|
251
|
-
}
|
|
252
|
-
return created.profileId;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** Fixed name of the reusable 24/7 profile that backs omada_setSsidEnabled(false). */
|
|
257
|
-
export const ALWAYS_PROFILE_NAME = 'mcp-always (24/7, used by omada_setSsidEnabled)';
|
|
258
|
-
|
|
259
|
-
export interface WlanSimpleInfo {
|
|
260
|
-
wlanId: string;
|
|
261
|
-
wlanName: string;
|
|
262
|
-
ssidList?: Array<{ ssidId: string; ssidName: string }>;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export interface TimeRangeProfileInfo {
|
|
266
|
-
profileId: string;
|
|
267
|
-
name: string;
|
|
268
|
-
dayMode?: number;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export interface CreateTimeRangeProfile {
|
|
272
|
-
name: string;
|
|
273
|
-
dayMode: number;
|
|
274
|
-
timeList: Array<{ dayType: number; startTimeH: number; startTimeM: number; endTimeH: number; endTimeM: number }>;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export interface SsidEnableResult {
|
|
278
|
-
wlanId: string;
|
|
279
|
-
ssidId: string;
|
|
280
|
-
ssidName: string;
|
|
281
|
-
enabled: boolean;
|
|
282
|
-
method: string;
|
|
283
|
-
scheduleId?: string;
|
|
284
|
-
}
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import type { Dispatcher } from 'undici';
|
|
2
|
-
import type { OmadaApiResponse, PaginatedResult } from '../types/index.js';
|
|
3
|
-
import { logger } from '../utils/logger.js';
|
|
4
|
-
import { createTlsDispatcher, tlsAwareFetch, type FetchInitWithDispatcher } from '../utils/tlsDispatcher.js';
|
|
5
|
-
|
|
6
|
-
import type { AuthManager } from './auth.js';
|
|
7
|
-
|
|
8
|
-
const DEFAULT_PAGE_SIZE = 200;
|
|
9
|
-
|
|
10
|
-
/** An error the Omada controller reported, with its structured errorCode when it gave one. */
|
|
11
|
-
export class OmadaApiError extends Error {
|
|
12
|
-
constructor(
|
|
13
|
-
message: string,
|
|
14
|
-
readonly errorCode?: number
|
|
15
|
-
) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = 'OmadaApiError';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface RequestOptions {
|
|
22
|
-
method?: string;
|
|
23
|
-
url: string;
|
|
24
|
-
params?: Record<string, unknown>;
|
|
25
|
-
data?: unknown;
|
|
26
|
-
headers?: Record<string, string>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface RequestHandlerConfig {
|
|
30
|
-
baseUrl: string;
|
|
31
|
-
timeout?: number;
|
|
32
|
-
strictSsl?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* HTTP request handler for Omada API calls with authentication and retry logic.
|
|
37
|
-
*/
|
|
38
|
-
export class RequestHandler {
|
|
39
|
-
private readonly baseUrl: string;
|
|
40
|
-
private readonly timeout: number;
|
|
41
|
-
/** Per-client undici dispatcher; set only when strictSsl is disabled (H2). */
|
|
42
|
-
private readonly dispatcher?: Dispatcher;
|
|
43
|
-
|
|
44
|
-
constructor(
|
|
45
|
-
config: RequestHandlerConfig,
|
|
46
|
-
private readonly auth: AuthManager
|
|
47
|
-
) {
|
|
48
|
-
this.baseUrl = config.baseUrl;
|
|
49
|
-
this.timeout = config.timeout ?? 30000;
|
|
50
|
-
this.dispatcher = createTlsDispatcher(config.strictSsl, config.baseUrl);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Make a GET request to the Omada API.
|
|
55
|
-
*/
|
|
56
|
-
public async get<T>(path: string, params?: Record<string, unknown>): Promise<T> {
|
|
57
|
-
return await this.request<T>({ method: 'GET', url: path, params });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Make a PATCH request to the Omada API.
|
|
62
|
-
*/
|
|
63
|
-
public async patch<T>(path: string, data?: unknown): Promise<T> {
|
|
64
|
-
return await this.request<T>({ method: 'PATCH', url: path, data });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Make a PUT request to the Omada API.
|
|
69
|
-
*/
|
|
70
|
-
public async put<T>(path: string, data?: unknown): Promise<T> {
|
|
71
|
-
return await this.request<T>({ method: 'PUT', url: path, data });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Make a POST request to the Omada API.
|
|
76
|
-
*/
|
|
77
|
-
public async post<T>(path: string, data?: unknown): Promise<T> {
|
|
78
|
-
return await this.request<T>({ method: 'POST', url: path, data });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Make an arbitrary HTTP request to the Omada API.
|
|
83
|
-
*/
|
|
84
|
-
public async request<T>(config: RequestOptions, retry = true): Promise<T> {
|
|
85
|
-
const accessToken = await this.auth.getAccessToken();
|
|
86
|
-
|
|
87
|
-
const method = (config.method ?? 'GET').toUpperCase();
|
|
88
|
-
const url = new URL(config.url, this.baseUrl);
|
|
89
|
-
|
|
90
|
-
// Add query parameters
|
|
91
|
-
if (config.params) {
|
|
92
|
-
for (const [key, value] of Object.entries(config.params)) {
|
|
93
|
-
if (value !== undefined && value !== null) {
|
|
94
|
-
url.searchParams.set(key, String(value));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const headers: Record<string, string> = {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
Accept: 'application/json',
|
|
102
|
-
Authorization: `AccessToken=${accessToken}`,
|
|
103
|
-
...(config.headers ?? {}),
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
logger.info('Omada request', {
|
|
107
|
-
method,
|
|
108
|
-
url: config.url,
|
|
109
|
-
params: config.params,
|
|
110
|
-
siteId: config.params?.siteId ?? undefined,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
logger.debug('Omada request details', {
|
|
114
|
-
method,
|
|
115
|
-
url: config.url,
|
|
116
|
-
headers: this.sanitizeHeaders(headers),
|
|
117
|
-
params: config.params ?? null,
|
|
118
|
-
data: this.sanitizePayload(config.data),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
// For self-signed certificates we relax TLS validation for THIS client
|
|
123
|
-
// only, via a per-instance undici dispatcher (H2). Node's global fetch
|
|
124
|
-
// ignores the legacy `agent` option, and toggling the process-global
|
|
125
|
-
// NODE_TLS_REJECT_UNAUTHORIZED env var would disable validation for every
|
|
126
|
-
// concurrent outbound request in the process.
|
|
127
|
-
const init: FetchInitWithDispatcher = {
|
|
128
|
-
method,
|
|
129
|
-
headers,
|
|
130
|
-
body: config.data ? JSON.stringify(config.data) : undefined,
|
|
131
|
-
signal: AbortSignal.timeout(this.timeout),
|
|
132
|
-
};
|
|
133
|
-
if (this.dispatcher) {
|
|
134
|
-
init.dispatcher = this.dispatcher;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// tlsAwareFetch: dispatcher requests use undici's own fetch (pairing rule).
|
|
138
|
-
const response = await tlsAwareFetch(url.toString(), init);
|
|
139
|
-
|
|
140
|
-
logger.info('Omada response', {
|
|
141
|
-
method,
|
|
142
|
-
url: config.url,
|
|
143
|
-
status: response.status,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Parse response body
|
|
147
|
-
const responseData = (await response.json()) as T;
|
|
148
|
-
|
|
149
|
-
logger.debug('Omada response payload', {
|
|
150
|
-
method,
|
|
151
|
-
url: config.url,
|
|
152
|
-
status: response.status,
|
|
153
|
-
data: this.sanitizePayload(responseData),
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Check if the response data indicates an authentication error
|
|
157
|
-
const errorCode = (responseData as { errorCode?: number } | undefined)?.errorCode;
|
|
158
|
-
const errorMsg = (responseData as { msg?: string } | undefined)?.msg;
|
|
159
|
-
|
|
160
|
-
if (retry && (this.isAuthErrorCode(errorCode) || this.isTokenExpiredMessage(errorMsg))) {
|
|
161
|
-
logger.warn('Omada authentication error in response, retrying with fresh token', {
|
|
162
|
-
method,
|
|
163
|
-
url: config.url,
|
|
164
|
-
errorCode,
|
|
165
|
-
message: errorMsg,
|
|
166
|
-
});
|
|
167
|
-
this.auth.clearToken();
|
|
168
|
-
return this.request<T>(config, false);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Handle HTTP errors after checking for auth errors in body
|
|
172
|
-
if (!response.ok && !this.isAuthErrorCode(errorCode)) {
|
|
173
|
-
logger.error('Omada request failed', {
|
|
174
|
-
method,
|
|
175
|
-
url: config.url,
|
|
176
|
-
status: response.status,
|
|
177
|
-
statusText: response.statusText,
|
|
178
|
-
errorCode,
|
|
179
|
-
message: errorMsg,
|
|
180
|
-
});
|
|
181
|
-
// Surface the controller's own structured errorCode/msg when present
|
|
182
|
-
// (safe diagnostic data — the controller's description, not an internal
|
|
183
|
-
// hostname/header) so a rejected request explains itself instead of
|
|
184
|
-
// collapsing to a bare HTTP status like "400 ".
|
|
185
|
-
const parts = [`${response.status} ${response.statusText}`.trim()];
|
|
186
|
-
if (errorMsg) {
|
|
187
|
-
parts.push(`- ${errorMsg}`);
|
|
188
|
-
}
|
|
189
|
-
if (errorCode !== undefined && errorCode !== 0) {
|
|
190
|
-
parts.push(`(errorCode ${errorCode})`);
|
|
191
|
-
}
|
|
192
|
-
throw new OmadaApiError(`Omada API request failed: ${parts.join(' ')}`, errorCode);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return responseData;
|
|
196
|
-
} catch (error) {
|
|
197
|
-
logger.error('Omada request failed', {
|
|
198
|
-
method,
|
|
199
|
-
url: config.url,
|
|
200
|
-
message: error instanceof Error ? error.message : String(error),
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
if (!retry) {
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Check if it's a network/timeout error that might be auth-related
|
|
208
|
-
const isNetworkError = error instanceof TypeError ||
|
|
209
|
-
(error instanceof Error && error.name === 'AbortError');
|
|
210
|
-
|
|
211
|
-
if (isNetworkError) {
|
|
212
|
-
throw error;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Fetch all pages of a paginated API endpoint.
|
|
221
|
-
*/
|
|
222
|
-
public async fetchPaginated<T>(path: string, params: Record<string, unknown> = {}): Promise<T[]> {
|
|
223
|
-
const records: T[] = [];
|
|
224
|
-
let page = 1;
|
|
225
|
-
let totalRows: number | undefined;
|
|
226
|
-
|
|
227
|
-
// Fetch sequential pages because OpenAPI requires explicit pagination parameters.
|
|
228
|
-
do {
|
|
229
|
-
const response = await this.get<OmadaApiResponse<PaginatedResult<T>>>(path, {
|
|
230
|
-
...params,
|
|
231
|
-
page,
|
|
232
|
-
pageSize: DEFAULT_PAGE_SIZE,
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const result = this.ensureSuccess(response);
|
|
236
|
-
const pageData = result.data ?? [];
|
|
237
|
-
totalRows = result.totalRows ?? totalRows;
|
|
238
|
-
|
|
239
|
-
records.push(...pageData);
|
|
240
|
-
page += 1;
|
|
241
|
-
|
|
242
|
-
if (pageData.length === 0) {
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
} while (!totalRows || records.length < totalRows);
|
|
246
|
-
|
|
247
|
-
return records;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Ensure an Omada API response indicates success.
|
|
252
|
-
* @throws {Error} If the response contains an error code
|
|
253
|
-
*/
|
|
254
|
-
public ensureSuccess<T>(response: OmadaApiResponse<T>): T {
|
|
255
|
-
if (response.errorCode !== 0) {
|
|
256
|
-
logger.error('Omada API error', {
|
|
257
|
-
errorCode: response.errorCode,
|
|
258
|
-
message: response.msg,
|
|
259
|
-
});
|
|
260
|
-
throw new OmadaApiError(response.msg ?? 'Omada API request failed', response.errorCode);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return (response.result ?? ({} as T)) as T;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Check if an error code indicates an authentication error.
|
|
268
|
-
*/
|
|
269
|
-
private isAuthErrorCode(errorCode?: number): boolean {
|
|
270
|
-
if (errorCode === undefined) {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return [-44106, -44111, -44112, -44113, -44114, -44116].includes(errorCode);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Check if an error message indicates token expiration.
|
|
279
|
-
*/
|
|
280
|
-
private isTokenExpiredMessage(message?: string): boolean {
|
|
281
|
-
if (!message) {
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const lowerMsg = message.toLowerCase();
|
|
286
|
-
return (
|
|
287
|
-
lowerMsg.includes('access token has expired') ||
|
|
288
|
-
lowerMsg.includes('token has expired') ||
|
|
289
|
-
lowerMsg.includes('token expired') ||
|
|
290
|
-
lowerMsg.includes('re-initiate the refreshtoken')
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Sanitize HTTP headers for logging, masking sensitive values.
|
|
296
|
-
*/
|
|
297
|
-
private sanitizeHeaders(headers: Record<string, string> | undefined): Record<string, unknown> | undefined {
|
|
298
|
-
if (!headers) {
|
|
299
|
-
return undefined;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const sanitized: Record<string, unknown> = {};
|
|
303
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
304
|
-
sanitized[key] = this.isSensitiveKey(key) ? this.maskValue(value) : value;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return sanitized;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Sanitize a payload for logging, masking sensitive values.
|
|
312
|
-
*/
|
|
313
|
-
private sanitizePayload(payload: unknown): unknown {
|
|
314
|
-
if (!payload || typeof payload !== 'object') {
|
|
315
|
-
return payload;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (Array.isArray(payload)) {
|
|
319
|
-
return payload.map((item) => this.sanitizePayload(item));
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const sanitized: Record<string, unknown> = {};
|
|
323
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
324
|
-
sanitized[key] = this.isSensitiveKey(key) ? this.maskValue(value) : this.sanitizePayload(value);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return sanitized;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Check if a key name indicates sensitive data.
|
|
332
|
-
*/
|
|
333
|
-
private isSensitiveKey(key: string): boolean {
|
|
334
|
-
const normalized = key.toLowerCase();
|
|
335
|
-
return (
|
|
336
|
-
normalized.includes('authorization') ||
|
|
337
|
-
normalized.includes('token') ||
|
|
338
|
-
normalized.includes('secret') ||
|
|
339
|
-
normalized.includes('password') ||
|
|
340
|
-
normalized.includes('client_id')
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Mask a sensitive value for logging.
|
|
346
|
-
*/
|
|
347
|
-
private maskValue(value: unknown): unknown {
|
|
348
|
-
if (typeof value === 'string') {
|
|
349
|
-
if (value.length <= 8) {
|
|
350
|
-
return '********';
|
|
351
|
-
}
|
|
352
|
-
return `${value.slice(0, 4)}…${value.slice(-4)}`;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (Array.isArray(value)) {
|
|
356
|
-
return value.map(() => '********');
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (typeof value === 'object' && value !== null) {
|
|
360
|
-
return '[masked-object]';
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return '********';
|
|
364
|
-
}
|
|
365
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { OmadaApiResponse, PaginatedResult, GetThreatListOptions, ThreatInfo } from '../types/index.js';
|
|
2
|
-
import type { RequestHandler } from './request.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Security-related operations for the Omada API.
|
|
6
|
-
* Handles threat management and security features.
|
|
7
|
-
*/
|
|
8
|
-
export class SecurityOperations {
|
|
9
|
-
constructor(
|
|
10
|
-
private readonly request: RequestHandler,
|
|
11
|
-
private readonly buildPath: (path: string) => string
|
|
12
|
-
) {}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get the global view threat management list.
|
|
16
|
-
* operationId: getGlobalThreatList
|
|
17
|
-
*
|
|
18
|
-
* @param options - Threat list query options
|
|
19
|
-
* @returns Paginated list of threat information
|
|
20
|
-
*/
|
|
21
|
-
async getThreatList(options: GetThreatListOptions): Promise<PaginatedResult<ThreatInfo>> {
|
|
22
|
-
const params: Record<string, unknown> = {
|
|
23
|
-
archived: options.archived,
|
|
24
|
-
page: options.page,
|
|
25
|
-
pageSize: options.pageSize,
|
|
26
|
-
'filters.startTime': options.startTime,
|
|
27
|
-
'filters.endTime': options.endTime,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (options.siteList) {
|
|
31
|
-
params.siteList = options.siteList;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (options.severity !== undefined) {
|
|
35
|
-
params['filters.severity'] = options.severity;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (options.sortTime) {
|
|
39
|
-
params['sorts.time'] = options.sortTime;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (options.searchKey) {
|
|
43
|
-
params.searchKey = options.searchKey;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const path = this.buildPath('/security/threat-management');
|
|
47
|
-
|
|
48
|
-
const response = await this.request.get<OmadaApiResponse<PaginatedResult<ThreatInfo>>>(path, params);
|
|
49
|
-
return this.request.ensureSuccess(response);
|
|
50
|
-
}
|
|
51
|
-
}
|