@aigne/afs-omada 1.11.0-beta.12
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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/cache.cjs +59 -0
- package/dist/cache.d.cts +39 -0
- package/dist/cache.d.cts.map +1 -0
- package/dist/cache.d.mts +39 -0
- package/dist/cache.d.mts.map +1 -0
- package/dist/cache.mjs +59 -0
- package/dist/cache.mjs.map +1 -0
- package/dist/client.cjs +168 -0
- package/dist/client.d.cts +66 -0
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.mts +66 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +169 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.cjs +11 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +6 -0
- package/dist/omada-afs.cjs +2076 -0
- package/dist/omada-afs.d.cts +443 -0
- package/dist/omada-afs.d.cts.map +1 -0
- package/dist/omada-afs.d.mts +443 -0
- package/dist/omada-afs.d.mts.map +1 -0
- package/dist/omada-afs.mjs +2077 -0
- package/dist/omada-afs.mjs.map +1 -0
- package/dist/types.cjs +59 -0
- package/dist/types.d.cts +43 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +43 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +58 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +58 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential. Unauthorized copying, modification, distribution, or use of
|
|
7
|
+
this Software, via any medium, is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
The Software is provided for internal use only within ArcBlock, Inc. and its
|
|
10
|
+
authorized affiliates.
|
|
11
|
+
|
|
12
|
+
## No License Granted
|
|
13
|
+
|
|
14
|
+
No license, express or implied, is granted to any party for any purpose.
|
|
15
|
+
All rights are reserved by ArcBlock, Inc.
|
|
16
|
+
|
|
17
|
+
## Public Artifact Distribution
|
|
18
|
+
|
|
19
|
+
Portions of this Software may be released publicly under separate open-source
|
|
20
|
+
licenses (such as MIT License) through designated public repositories. Such
|
|
21
|
+
public releases are governed by their respective licenses and do not affect
|
|
22
|
+
the proprietary nature of this repository.
|
|
23
|
+
|
|
24
|
+
## Contact
|
|
25
|
+
|
|
26
|
+
For licensing inquiries, contact: legal@arcblock.io
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
|
|
3
|
+
function __decorate(decorators, target, key, desc) {
|
|
4
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
6
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
7
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
exports.__decorate = __decorate;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { __decorate };
|
package/dist/cache.cjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/cache.ts
|
|
3
|
+
var OmadaCache = class {
|
|
4
|
+
store = /* @__PURE__ */ new Map();
|
|
5
|
+
ttlMs;
|
|
6
|
+
constructor(ttlSeconds) {
|
|
7
|
+
this.ttlMs = ttlSeconds * 1e3;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Get a cached value, or undefined if expired/missing.
|
|
11
|
+
*/
|
|
12
|
+
get(key) {
|
|
13
|
+
if (this.ttlMs <= 0) return void 0;
|
|
14
|
+
const entry = this.store.get(key);
|
|
15
|
+
if (!entry) return void 0;
|
|
16
|
+
if (Date.now() >= entry.expiresAt) {
|
|
17
|
+
this.store.delete(key);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
return entry.data;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Set a cached value with TTL.
|
|
24
|
+
*/
|
|
25
|
+
set(key, data) {
|
|
26
|
+
if (this.ttlMs <= 0) return;
|
|
27
|
+
this.store.set(key, {
|
|
28
|
+
data,
|
|
29
|
+
expiresAt: Date.now() + this.ttlMs
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Invalidate a specific cache key.
|
|
34
|
+
*/
|
|
35
|
+
invalidate(key) {
|
|
36
|
+
this.store.delete(key);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Invalidate all cache keys matching a prefix.
|
|
40
|
+
*/
|
|
41
|
+
invalidatePrefix(prefix) {
|
|
42
|
+
for (const key of this.store.keys()) if (key.startsWith(prefix)) this.store.delete(key);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Clear the entire cache.
|
|
46
|
+
*/
|
|
47
|
+
clear() {
|
|
48
|
+
this.store.clear();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the number of cached entries (for testing).
|
|
52
|
+
*/
|
|
53
|
+
get size() {
|
|
54
|
+
return this.store.size;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
exports.OmadaCache = OmadaCache;
|
package/dist/cache.d.cts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/cache.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* TTL cache for Omada API responses.
|
|
4
|
+
*
|
|
5
|
+
* Provides per-key caching with configurable TTL and manual invalidation.
|
|
6
|
+
* Cache keys include siteId to prevent cross-site data mixing.
|
|
7
|
+
*/
|
|
8
|
+
declare class OmadaCache {
|
|
9
|
+
private store;
|
|
10
|
+
private ttlMs;
|
|
11
|
+
constructor(ttlSeconds: number);
|
|
12
|
+
/**
|
|
13
|
+
* Get a cached value, or undefined if expired/missing.
|
|
14
|
+
*/
|
|
15
|
+
get<T>(key: string): T | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Set a cached value with TTL.
|
|
18
|
+
*/
|
|
19
|
+
set<T>(key: string, data: T): void;
|
|
20
|
+
/**
|
|
21
|
+
* Invalidate a specific cache key.
|
|
22
|
+
*/
|
|
23
|
+
invalidate(key: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Invalidate all cache keys matching a prefix.
|
|
26
|
+
*/
|
|
27
|
+
invalidatePrefix(prefix: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Clear the entire cache.
|
|
30
|
+
*/
|
|
31
|
+
clear(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get the number of cached entries (for testing).
|
|
34
|
+
*/
|
|
35
|
+
get size(): number;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { OmadaCache };
|
|
39
|
+
//# sourceMappingURL=cache.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.cts","names":[],"sources":["../src/cache.ts"],"mappings":";;AAYA;;;;;cAAa,UAAA;EAAA,QACH,KAAA;EAAA,QACA,KAAA;cAEI,UAAA;EAOR;;;EAAJ,GAAA,GAAA,CAAO,GAAA,WAAc,CAAA;EAiBjB;;;EAAJ,GAAA,GAAA,CAAO,GAAA,UAAa,IAAA,EAAM,CAAA;EAY1B;;;EAAA,UAAA,CAAW,GAAA;EAkBX;;;EAXA,gBAAA,CAAiB,MAAA;;;;EAWjB,KAAA,CAAA;;;;MAOI,IAAA,CAAA;AAAA"}
|
package/dist/cache.d.mts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/cache.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* TTL cache for Omada API responses.
|
|
4
|
+
*
|
|
5
|
+
* Provides per-key caching with configurable TTL and manual invalidation.
|
|
6
|
+
* Cache keys include siteId to prevent cross-site data mixing.
|
|
7
|
+
*/
|
|
8
|
+
declare class OmadaCache {
|
|
9
|
+
private store;
|
|
10
|
+
private ttlMs;
|
|
11
|
+
constructor(ttlSeconds: number);
|
|
12
|
+
/**
|
|
13
|
+
* Get a cached value, or undefined if expired/missing.
|
|
14
|
+
*/
|
|
15
|
+
get<T>(key: string): T | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Set a cached value with TTL.
|
|
18
|
+
*/
|
|
19
|
+
set<T>(key: string, data: T): void;
|
|
20
|
+
/**
|
|
21
|
+
* Invalidate a specific cache key.
|
|
22
|
+
*/
|
|
23
|
+
invalidate(key: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Invalidate all cache keys matching a prefix.
|
|
26
|
+
*/
|
|
27
|
+
invalidatePrefix(prefix: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Clear the entire cache.
|
|
30
|
+
*/
|
|
31
|
+
clear(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get the number of cached entries (for testing).
|
|
34
|
+
*/
|
|
35
|
+
get size(): number;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { OmadaCache };
|
|
39
|
+
//# sourceMappingURL=cache.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.mts","names":[],"sources":["../src/cache.ts"],"mappings":";;AAYA;;;;;cAAa,UAAA;EAAA,QACH,KAAA;EAAA,QACA,KAAA;cAEI,UAAA;EAOR;;;EAAJ,GAAA,GAAA,CAAO,GAAA,WAAc,CAAA;EAiBjB;;;EAAJ,GAAA,GAAA,CAAO,GAAA,UAAa,IAAA,EAAM,CAAA;EAY1B;;;EAAA,UAAA,CAAW,GAAA;EAkBX;;;EAXA,gBAAA,CAAiB,MAAA;;;;EAWjB,KAAA,CAAA;;;;MAOI,IAAA,CAAA;AAAA"}
|
package/dist/cache.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/cache.ts
|
|
2
|
+
var OmadaCache = class {
|
|
3
|
+
store = /* @__PURE__ */ new Map();
|
|
4
|
+
ttlMs;
|
|
5
|
+
constructor(ttlSeconds) {
|
|
6
|
+
this.ttlMs = ttlSeconds * 1e3;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Get a cached value, or undefined if expired/missing.
|
|
10
|
+
*/
|
|
11
|
+
get(key) {
|
|
12
|
+
if (this.ttlMs <= 0) return void 0;
|
|
13
|
+
const entry = this.store.get(key);
|
|
14
|
+
if (!entry) return void 0;
|
|
15
|
+
if (Date.now() >= entry.expiresAt) {
|
|
16
|
+
this.store.delete(key);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
return entry.data;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Set a cached value with TTL.
|
|
23
|
+
*/
|
|
24
|
+
set(key, data) {
|
|
25
|
+
if (this.ttlMs <= 0) return;
|
|
26
|
+
this.store.set(key, {
|
|
27
|
+
data,
|
|
28
|
+
expiresAt: Date.now() + this.ttlMs
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Invalidate a specific cache key.
|
|
33
|
+
*/
|
|
34
|
+
invalidate(key) {
|
|
35
|
+
this.store.delete(key);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Invalidate all cache keys matching a prefix.
|
|
39
|
+
*/
|
|
40
|
+
invalidatePrefix(prefix) {
|
|
41
|
+
for (const key of this.store.keys()) if (key.startsWith(prefix)) this.store.delete(key);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clear the entire cache.
|
|
45
|
+
*/
|
|
46
|
+
clear() {
|
|
47
|
+
this.store.clear();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the number of cached entries (for testing).
|
|
51
|
+
*/
|
|
52
|
+
get size() {
|
|
53
|
+
return this.store.size;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { OmadaCache };
|
|
59
|
+
//# sourceMappingURL=cache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.mjs","names":[],"sources":["../src/cache.ts"],"sourcesContent":["/**\n * TTL cache for Omada API responses.\n *\n * Provides per-key caching with configurable TTL and manual invalidation.\n * Cache keys include siteId to prevent cross-site data mixing.\n */\n\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nexport class OmadaCache {\n private store = new Map<string, CacheEntry<unknown>>();\n private ttlMs: number;\n\n constructor(ttlSeconds: number) {\n this.ttlMs = ttlSeconds * 1000;\n }\n\n /**\n * Get a cached value, or undefined if expired/missing.\n */\n get<T>(key: string): T | undefined {\n if (this.ttlMs <= 0) return undefined;\n\n const entry = this.store.get(key);\n if (!entry) return undefined;\n\n if (Date.now() >= entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n\n return entry.data as T;\n }\n\n /**\n * Set a cached value with TTL.\n */\n set<T>(key: string, data: T): void {\n if (this.ttlMs <= 0) return;\n\n this.store.set(key, {\n data,\n expiresAt: Date.now() + this.ttlMs,\n });\n }\n\n /**\n * Invalidate a specific cache key.\n */\n invalidate(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Invalidate all cache keys matching a prefix.\n */\n invalidatePrefix(prefix: string): void {\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key);\n }\n }\n }\n\n /**\n * Clear the entire cache.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Get the number of cached entries (for testing).\n */\n get size(): number {\n return this.store.size;\n }\n}\n"],"mappings":";AAYA,IAAa,aAAb,MAAwB;CACtB,AAAQ,wBAAQ,IAAI,KAAkC;CACtD,AAAQ;CAER,YAAY,YAAoB;AAC9B,OAAK,QAAQ,aAAa;;;;;CAM5B,IAAO,KAA4B;AACjC,MAAI,KAAK,SAAS,EAAG,QAAO;EAE5B,MAAM,QAAQ,KAAK,MAAM,IAAI,IAAI;AACjC,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,KAAK,KAAK,IAAI,MAAM,WAAW;AACjC,QAAK,MAAM,OAAO,IAAI;AACtB;;AAGF,SAAO,MAAM;;;;;CAMf,IAAO,KAAa,MAAe;AACjC,MAAI,KAAK,SAAS,EAAG;AAErB,OAAK,MAAM,IAAI,KAAK;GAClB;GACA,WAAW,KAAK,KAAK,GAAG,KAAK;GAC9B,CAAC;;;;;CAMJ,WAAW,KAAmB;AAC5B,OAAK,MAAM,OAAO,IAAI;;;;;CAMxB,iBAAiB,QAAsB;AACrC,OAAK,MAAM,OAAO,KAAK,MAAM,MAAM,CACjC,KAAI,IAAI,WAAW,OAAO,CACxB,MAAK,MAAM,OAAO,IAAI;;;;;CAQ5B,QAAc;AACZ,OAAK,MAAM,OAAO;;;;;CAMpB,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM"}
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
let ufo = require("ufo");
|
|
2
|
+
|
|
3
|
+
//#region src/client.ts
|
|
4
|
+
/** Validate that an ID-like string is safe for URL path construction. */
|
|
5
|
+
function validateIdParam(value, name) {
|
|
6
|
+
if (!/^[\w-]+$/.test(value)) throw new Error(`Invalid ${name}: must be alphanumeric with hyphens/underscores`);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* OmadaApiClient manages authentication and API calls to the Omada SDN Controller.
|
|
10
|
+
*
|
|
11
|
+
* Handles:
|
|
12
|
+
* - OAuth2 client credentials token acquisition
|
|
13
|
+
* - Non-standard Omada auth headers (Cookie + Csrf-Token)
|
|
14
|
+
* - Token refresh before expiration
|
|
15
|
+
* - Concurrent refresh prevention
|
|
16
|
+
* - omadacId auto-discovery from /api/info
|
|
17
|
+
*/
|
|
18
|
+
var OmadaApiClient = class {
|
|
19
|
+
tokenState = null;
|
|
20
|
+
refreshPromise = null;
|
|
21
|
+
omadacId;
|
|
22
|
+
/** Base URL for Omada controller (read-only, validated at construction) */
|
|
23
|
+
baseUrl;
|
|
24
|
+
/** Whether to skip TLS certificate verification (for self-signed certs) */
|
|
25
|
+
tlsRejectUnauthorized;
|
|
26
|
+
#clientId;
|
|
27
|
+
#clientSecret;
|
|
28
|
+
constructor(baseUrl, clientId, clientSecret, omadacId, strictSsl) {
|
|
29
|
+
this.baseUrl = baseUrl;
|
|
30
|
+
this.#clientId = clientId;
|
|
31
|
+
this.#clientSecret = clientSecret;
|
|
32
|
+
this.omadacId = omadacId;
|
|
33
|
+
this.tlsRejectUnauthorized = strictSsl;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Ensure omadacId is available, discovering it if not provided.
|
|
37
|
+
*/
|
|
38
|
+
async ensureOmadacId() {
|
|
39
|
+
if (this.omadacId) return this.omadacId;
|
|
40
|
+
this.omadacId = await this.discoverOmadacId();
|
|
41
|
+
return this.omadacId;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Discover omadacId from GET /api/info endpoint (no auth required).
|
|
45
|
+
*/
|
|
46
|
+
async discoverOmadacId() {
|
|
47
|
+
const url = (0, ufo.joinURL)(this.baseUrl, "/api/info");
|
|
48
|
+
const resp = await this.rawFetch(url);
|
|
49
|
+
if (!resp.ok) throw new Error(`Failed to discover omadacId: HTTP ${resp.status}`);
|
|
50
|
+
const data = await resp.json();
|
|
51
|
+
if (data.errorCode !== 0) throw new Error(`Omada API error discovering omadacId: errorCode=${data.errorCode}`);
|
|
52
|
+
if (!data.result?.omadacId) throw new Error("Omada API /api/info response missing result.omadacId field");
|
|
53
|
+
validateIdParam(data.result.omadacId, "omadacId (discovered)");
|
|
54
|
+
return data.result.omadacId;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Acquire or refresh OAuth2 token.
|
|
58
|
+
* Prevents concurrent refreshes using a shared promise.
|
|
59
|
+
*/
|
|
60
|
+
async authorize() {
|
|
61
|
+
if (this.refreshPromise) {
|
|
62
|
+
await this.refreshPromise;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.refreshPromise = this._doAuthorize();
|
|
66
|
+
try {
|
|
67
|
+
await this.refreshPromise;
|
|
68
|
+
} finally {
|
|
69
|
+
this.refreshPromise = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async _doAuthorize() {
|
|
73
|
+
const omadacId = await this.ensureOmadacId();
|
|
74
|
+
const url = (0, ufo.joinURL)(this.baseUrl, `/${omadacId}/openapi/authorize/token`);
|
|
75
|
+
const body = {
|
|
76
|
+
omadacId,
|
|
77
|
+
client_id: this.#clientId,
|
|
78
|
+
client_secret: this.#clientSecret,
|
|
79
|
+
grant_type: "client_credentials"
|
|
80
|
+
};
|
|
81
|
+
if (this.tokenState?.refreshToken) {
|
|
82
|
+
body.grant_type = "refresh_token";
|
|
83
|
+
body.refresh_token = this.tokenState.refreshToken;
|
|
84
|
+
}
|
|
85
|
+
const resp = await this.rawFetch(url, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify(body)
|
|
89
|
+
});
|
|
90
|
+
if (!resp.ok) throw new Error(`Omada authorization failed: HTTP ${resp.status}`);
|
|
91
|
+
const data = await resp.json();
|
|
92
|
+
if (data.errorCode !== 0 || !data.result?.accessToken) throw new Error(`Omada authorization failed: errorCode=${data.errorCode}`);
|
|
93
|
+
this.tokenState = {
|
|
94
|
+
accessToken: data.result.accessToken,
|
|
95
|
+
refreshToken: data.result.refreshToken,
|
|
96
|
+
expiresAt: Date.now() + (data.result.expiresIn - 60) * 1e3
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Ensure we have a valid token, refreshing if needed.
|
|
101
|
+
*/
|
|
102
|
+
async ensureToken() {
|
|
103
|
+
if (!this.tokenState || Date.now() >= this.tokenState.expiresAt) await this.authorize();
|
|
104
|
+
return this.tokenState.accessToken;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Make an authenticated GET API call to the Omada controller.
|
|
108
|
+
* Automatically handles token acquisition and the non-standard auth headers.
|
|
109
|
+
*/
|
|
110
|
+
async request(path, options = {}) {
|
|
111
|
+
const token = await this.ensureToken();
|
|
112
|
+
const omadacId = await this.ensureOmadacId();
|
|
113
|
+
let url = (0, ufo.joinURL)(this.baseUrl, `/${omadacId}/openapi/v1`, path);
|
|
114
|
+
if (options.params) {
|
|
115
|
+
const searchParams = new URLSearchParams();
|
|
116
|
+
for (const [key, value] of Object.entries(options.params)) searchParams.set(key, String(value));
|
|
117
|
+
url = `${url}?${searchParams.toString()}`;
|
|
118
|
+
}
|
|
119
|
+
const headers = {
|
|
120
|
+
"Content-Type": "application/json",
|
|
121
|
+
Cookie: `TPLINK_OMADA_SESSIONID=${token}`,
|
|
122
|
+
"Csrf-Token": token
|
|
123
|
+
};
|
|
124
|
+
const fetchOptions = {
|
|
125
|
+
method: options.method || "GET",
|
|
126
|
+
headers
|
|
127
|
+
};
|
|
128
|
+
if (options.body) fetchOptions.body = JSON.stringify(options.body);
|
|
129
|
+
const resp = await this.rawFetch(url, fetchOptions);
|
|
130
|
+
if (!resp.ok) throw new Error(`Omada API error: HTTP ${resp.status} for ${options.method || "GET"} ${path}`);
|
|
131
|
+
const data = await resp.json();
|
|
132
|
+
if (data.errorCode !== 0) throw new Error(`Omada API error: errorCode=${data.errorCode}${data.msg ? ` msg=${data.msg}` : ""} for ${path}`);
|
|
133
|
+
return data.result;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Convenience wrapper for POST requests.
|
|
137
|
+
*/
|
|
138
|
+
async post(path, body) {
|
|
139
|
+
return this.request(path, {
|
|
140
|
+
method: "POST",
|
|
141
|
+
body
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Convenience wrapper for PATCH requests.
|
|
146
|
+
*/
|
|
147
|
+
async patch(path, body) {
|
|
148
|
+
return this.request(path, {
|
|
149
|
+
method: "PATCH",
|
|
150
|
+
body
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Raw fetch wrapper that handles TLS certificate verification.
|
|
155
|
+
* When tlsRejectUnauthorized=false, skips certificate validation
|
|
156
|
+
* (common for self-signed certs on Omada controllers).
|
|
157
|
+
*/
|
|
158
|
+
rawFetch(url, options) {
|
|
159
|
+
if (!this.tlsRejectUnauthorized) return fetch(url, {
|
|
160
|
+
...options,
|
|
161
|
+
tls: { rejectUnauthorized: false }
|
|
162
|
+
});
|
|
163
|
+
return fetch(url, options);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
exports.OmadaApiClient = OmadaApiClient;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/client.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* OmadaApiClient manages authentication and API calls to the Omada SDN Controller.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - OAuth2 client credentials token acquisition
|
|
7
|
+
* - Non-standard Omada auth headers (Cookie + Csrf-Token)
|
|
8
|
+
* - Token refresh before expiration
|
|
9
|
+
* - Concurrent refresh prevention
|
|
10
|
+
* - omadacId auto-discovery from /api/info
|
|
11
|
+
*/
|
|
12
|
+
declare class OmadaApiClient {
|
|
13
|
+
#private;
|
|
14
|
+
private tokenState;
|
|
15
|
+
private refreshPromise;
|
|
16
|
+
private omadacId;
|
|
17
|
+
/** Base URL for Omada controller (read-only, validated at construction) */
|
|
18
|
+
readonly baseUrl: string;
|
|
19
|
+
/** Whether to skip TLS certificate verification (for self-signed certs) */
|
|
20
|
+
readonly tlsRejectUnauthorized: boolean;
|
|
21
|
+
constructor(baseUrl: string, clientId: string, clientSecret: string, omadacId: string | undefined, strictSsl: boolean);
|
|
22
|
+
/**
|
|
23
|
+
* Ensure omadacId is available, discovering it if not provided.
|
|
24
|
+
*/
|
|
25
|
+
ensureOmadacId(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Discover omadacId from GET /api/info endpoint (no auth required).
|
|
28
|
+
*/
|
|
29
|
+
discoverOmadacId(): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Acquire or refresh OAuth2 token.
|
|
32
|
+
* Prevents concurrent refreshes using a shared promise.
|
|
33
|
+
*/
|
|
34
|
+
authorize(): Promise<void>;
|
|
35
|
+
private _doAuthorize;
|
|
36
|
+
/**
|
|
37
|
+
* Ensure we have a valid token, refreshing if needed.
|
|
38
|
+
*/
|
|
39
|
+
private ensureToken;
|
|
40
|
+
/**
|
|
41
|
+
* Make an authenticated GET API call to the Omada controller.
|
|
42
|
+
* Automatically handles token acquisition and the non-standard auth headers.
|
|
43
|
+
*/
|
|
44
|
+
request<T = unknown>(path: string, options?: {
|
|
45
|
+
method?: string;
|
|
46
|
+
body?: unknown;
|
|
47
|
+
params?: Record<string, string | number>;
|
|
48
|
+
}): Promise<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Convenience wrapper for POST requests.
|
|
51
|
+
*/
|
|
52
|
+
post<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Convenience wrapper for PATCH requests.
|
|
55
|
+
*/
|
|
56
|
+
patch<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Raw fetch wrapper that handles TLS certificate verification.
|
|
59
|
+
* When tlsRejectUnauthorized=false, skips certificate validation
|
|
60
|
+
* (common for self-signed certs on Omada controllers).
|
|
61
|
+
*/
|
|
62
|
+
private rawFetch;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { OmadaApiClient };
|
|
66
|
+
//# sourceMappingURL=client.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;AA4BA;;;;;;;;;cAAa,cAAA;EAAA;UACH,UAAA;EAAA,QACA,cAAA;EAAA,QACA,QAAA;EAmNuD;EAAA,SAhNtD,OAAA;EALD;EAAA,SAQC,qBAAA;cAOP,OAAA,UACA,QAAA,UACA,YAAA,UACA,QAAA,sBACA,SAAA;EAdO;;;EA0BH,cAAA,CAAA,GAAkB,OAAA;EAftB;;;EAwBI,gBAAA,CAAA,GAAoB,OAAA;EATpB;;;;EAsCA,SAAA,CAAA,GAAa,OAAA;EAAA,QAeL,YAAA;EAAA;;;EAAA,QAoDA,WAAA;EAYZ;;;;EADI,OAAA,aAAA,CACJ,IAAA,UACA,OAAA;IACE,MAAA;IACA,IAAA;IACA,MAAA,GAAS,MAAA;EAAA,IAEV,OAAA,CAAQ,CAAA;EAoDA;;;EAAL,IAAA,aAAA,CAAkB,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAAA;;;EAOzD,KAAA,aAAA,CAAmB,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAAzB;;;;;EAAA,QAS/B,QAAA;AAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/client.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* OmadaApiClient manages authentication and API calls to the Omada SDN Controller.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - OAuth2 client credentials token acquisition
|
|
7
|
+
* - Non-standard Omada auth headers (Cookie + Csrf-Token)
|
|
8
|
+
* - Token refresh before expiration
|
|
9
|
+
* - Concurrent refresh prevention
|
|
10
|
+
* - omadacId auto-discovery from /api/info
|
|
11
|
+
*/
|
|
12
|
+
declare class OmadaApiClient {
|
|
13
|
+
#private;
|
|
14
|
+
private tokenState;
|
|
15
|
+
private refreshPromise;
|
|
16
|
+
private omadacId;
|
|
17
|
+
/** Base URL for Omada controller (read-only, validated at construction) */
|
|
18
|
+
readonly baseUrl: string;
|
|
19
|
+
/** Whether to skip TLS certificate verification (for self-signed certs) */
|
|
20
|
+
readonly tlsRejectUnauthorized: boolean;
|
|
21
|
+
constructor(baseUrl: string, clientId: string, clientSecret: string, omadacId: string | undefined, strictSsl: boolean);
|
|
22
|
+
/**
|
|
23
|
+
* Ensure omadacId is available, discovering it if not provided.
|
|
24
|
+
*/
|
|
25
|
+
ensureOmadacId(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Discover omadacId from GET /api/info endpoint (no auth required).
|
|
28
|
+
*/
|
|
29
|
+
discoverOmadacId(): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Acquire or refresh OAuth2 token.
|
|
32
|
+
* Prevents concurrent refreshes using a shared promise.
|
|
33
|
+
*/
|
|
34
|
+
authorize(): Promise<void>;
|
|
35
|
+
private _doAuthorize;
|
|
36
|
+
/**
|
|
37
|
+
* Ensure we have a valid token, refreshing if needed.
|
|
38
|
+
*/
|
|
39
|
+
private ensureToken;
|
|
40
|
+
/**
|
|
41
|
+
* Make an authenticated GET API call to the Omada controller.
|
|
42
|
+
* Automatically handles token acquisition and the non-standard auth headers.
|
|
43
|
+
*/
|
|
44
|
+
request<T = unknown>(path: string, options?: {
|
|
45
|
+
method?: string;
|
|
46
|
+
body?: unknown;
|
|
47
|
+
params?: Record<string, string | number>;
|
|
48
|
+
}): Promise<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Convenience wrapper for POST requests.
|
|
51
|
+
*/
|
|
52
|
+
post<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Convenience wrapper for PATCH requests.
|
|
55
|
+
*/
|
|
56
|
+
patch<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Raw fetch wrapper that handles TLS certificate verification.
|
|
59
|
+
* When tlsRejectUnauthorized=false, skips certificate validation
|
|
60
|
+
* (common for self-signed certs on Omada controllers).
|
|
61
|
+
*/
|
|
62
|
+
private rawFetch;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { OmadaApiClient };
|
|
66
|
+
//# sourceMappingURL=client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;AA4BA;;;;;;;;;cAAa,cAAA;EAAA;UACH,UAAA;EAAA,QACA,cAAA;EAAA,QACA,QAAA;EAmNuD;EAAA,SAhNtD,OAAA;EALD;EAAA,SAQC,qBAAA;cAOP,OAAA,UACA,QAAA,UACA,YAAA,UACA,QAAA,sBACA,SAAA;EAdO;;;EA0BH,cAAA,CAAA,GAAkB,OAAA;EAftB;;;EAwBI,gBAAA,CAAA,GAAoB,OAAA;EATpB;;;;EAsCA,SAAA,CAAA,GAAa,OAAA;EAAA,QAeL,YAAA;EAAA;;;EAAA,QAoDA,WAAA;EAYZ;;;;EADI,OAAA,aAAA,CACJ,IAAA,UACA,OAAA;IACE,MAAA;IACA,IAAA;IACA,MAAA,GAAS,MAAA;EAAA,IAEV,OAAA,CAAQ,CAAA;EAoDA;;;EAAL,IAAA,aAAA,CAAkB,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAAA;;;EAOzD,KAAA,aAAA,CAAmB,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAAzB;;;;;EAAA,QAS/B,QAAA;AAAA"}
|