@meowpanel/api 1.0.0-beta.3 → 1.0.0-beta.4
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/esm/src/client.d.ts +6 -25
- package/esm/src/client.d.ts.map +1 -1
- package/esm/src/client.js +6 -10
- package/esm/src/http.d.ts +1 -24
- package/esm/src/http.d.ts.map +1 -1
- package/esm/src/http.js +4 -77
- package/package.json +1 -1
- package/script/src/client.d.ts +6 -25
- package/script/src/client.d.ts.map +1 -1
- package/script/src/client.js +6 -10
- package/script/src/http.d.ts +1 -24
- package/script/src/http.d.ts.map +1 -1
- package/script/src/http.js +4 -77
package/esm/src/client.d.ts
CHANGED
|
@@ -15,19 +15,13 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export interface MeowPanelApiOptions {
|
|
17
17
|
/**
|
|
18
|
-
* Base URL of
|
|
18
|
+
* Base URL of MeowPanel instance.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @example `"https://panel.example.com"`
|
|
20
|
+
* @example "https://panel.example.com"
|
|
23
21
|
*/
|
|
24
22
|
url: string | URL;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* Can be a user session token or an API key token.
|
|
29
|
-
*/
|
|
30
|
-
token?: string;
|
|
23
|
+
/** Bearer token used to authenticate every request. */
|
|
24
|
+
token: string;
|
|
31
25
|
/**
|
|
32
26
|
* Custom fetch implementation to use for all HTTP requests.
|
|
33
27
|
*
|
|
@@ -35,24 +29,11 @@ export interface MeowPanelApiOptions {
|
|
|
35
29
|
*/
|
|
36
30
|
fetch?: typeof globalThis.fetch;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
32
|
+
* Callback invoked for every outgoing request.
|
|
39
33
|
*
|
|
40
34
|
* Useful for integrating API request visibility into your app's debug/info logger.
|
|
41
35
|
*/
|
|
42
36
|
logger?: HttpLogger;
|
|
43
|
-
/**
|
|
44
|
-
* The `credentials` mode for all fetch requests.
|
|
45
|
-
*
|
|
46
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication.
|
|
47
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
48
|
-
*/
|
|
49
|
-
credentials?: RequestCredentials;
|
|
50
|
-
/**
|
|
51
|
-
* Callback invoked when the access token is automatically refreshed.
|
|
52
|
-
*
|
|
53
|
-
* Useful for persisting the new token or updating external state.
|
|
54
|
-
*/
|
|
55
|
-
onTokenRefresh?: (token: string) => void;
|
|
56
37
|
}
|
|
57
38
|
/**
|
|
58
39
|
* The root MeowPanel API client.
|
|
@@ -66,7 +47,7 @@ export interface MeowPanelApiOptions {
|
|
|
66
47
|
*
|
|
67
48
|
* const api = new MeowPanelApi({
|
|
68
49
|
* url: 'https://panel.example.com',
|
|
69
|
-
* token: 'your-
|
|
50
|
+
* token: 'your-api-token',
|
|
70
51
|
* });
|
|
71
52
|
*
|
|
72
53
|
* // Fetch resources
|
package/esm/src/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAE3D;;;;;;;;OAQG;IACH,SAAgB,IAAI,EAAE,YAAY,CAAC;IAEnC;;;;;;;;;OASG;IACH,SAAgB,OAAO,EAAE,eAAe,CAAC;IAEzC;;;;;;;;OAQG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,SAAgB,IAAI,EAAE,aAAa,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;OAUG;IACH,SAAgB,YAAY,EAAE,qBAAqB,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,OAAO,EAAE,mBAAmB;IAyB/C;;;;;;OAMG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOpC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,UAAU,IAAI,aAAa;IAQlC;;;;;OAKG;IACI,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAG/D"}
|
package/esm/src/client.js
CHANGED
|
@@ -21,7 +21,7 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
21
21
|
*
|
|
22
22
|
* const api = new MeowPanelApi({
|
|
23
23
|
* url: 'https://panel.example.com',
|
|
24
|
-
* token: 'your-
|
|
24
|
+
* token: 'your-api-token',
|
|
25
25
|
* });
|
|
26
26
|
*
|
|
27
27
|
* // Fetch resources
|
|
@@ -160,21 +160,14 @@ export class MeowPanelApi {
|
|
|
160
160
|
*/
|
|
161
161
|
servers;
|
|
162
162
|
constructor(options) {
|
|
163
|
-
if (!options?.url)
|
|
163
|
+
if (!options?.url) {
|
|
164
164
|
throw new Error('MeowPanelApi requires a "url" option (e.g. "https://panel.example.com").');
|
|
165
|
-
|
|
165
|
+
}
|
|
166
166
|
this.http = new HttpClient({
|
|
167
167
|
url: options.url.toString(),
|
|
168
168
|
token: options.token,
|
|
169
169
|
fetch: options.fetch,
|
|
170
170
|
logger: options.logger,
|
|
171
|
-
credentials: options.credentials,
|
|
172
|
-
onTokenRefresh: (token) => {
|
|
173
|
-
for (const sse of this.sseConnections) {
|
|
174
|
-
sse.updateToken(token);
|
|
175
|
-
}
|
|
176
|
-
userCallback?.(token);
|
|
177
|
-
},
|
|
178
171
|
});
|
|
179
172
|
this.auth = new AuthResource(this.http);
|
|
180
173
|
this.account = new AccountResource(this.http);
|
|
@@ -195,6 +188,9 @@ export class MeowPanelApi {
|
|
|
195
188
|
*/
|
|
196
189
|
setToken(token) {
|
|
197
190
|
this.http.setToken(token);
|
|
191
|
+
for (const sse of this.sseConnections) {
|
|
192
|
+
sse.updateToken(token);
|
|
193
|
+
}
|
|
198
194
|
}
|
|
199
195
|
/**
|
|
200
196
|
* Create a panel-level Server-Sent Events connection to Nexus.
|
package/esm/src/http.d.ts
CHANGED
|
@@ -5,22 +5,11 @@ export interface HttpClientOptions {
|
|
|
5
5
|
/** Base URL of the MeowPanel Nexus API, e.g. `https://panel.example.com`. */
|
|
6
6
|
url: string;
|
|
7
7
|
/** Bearer token used to authenticate every request. */
|
|
8
|
-
token
|
|
8
|
+
token: string;
|
|
9
9
|
/** Custom fetch implementation. Defaults to `globalThis.fetch`. */
|
|
10
10
|
fetch?: typeof globalThis.fetch;
|
|
11
11
|
/** Optional callback invoked for every outgoing request lifecycle event. */
|
|
12
12
|
logger?: HttpLogger;
|
|
13
|
-
/**
|
|
14
|
-
* The `credentials` mode for all fetch requests.
|
|
15
|
-
*
|
|
16
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication
|
|
17
|
-
* (e.g. session cookies set by the auth endpoint).
|
|
18
|
-
*
|
|
19
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
20
|
-
*/
|
|
21
|
-
credentials?: RequestCredentials;
|
|
22
|
-
/** Callback invoked when the access token is refreshed. */
|
|
23
|
-
onTokenRefresh?: (token: string) => void;
|
|
24
13
|
}
|
|
25
14
|
/** Typed lifecycle code emitted by {@link HttpClient}'s optional logger. */
|
|
26
15
|
export type HttpLogCode = 'request.start' | 'request.success' | 'request.error';
|
|
@@ -71,9 +60,6 @@ export declare class HttpClient {
|
|
|
71
60
|
protected token: string;
|
|
72
61
|
protected readonly fetchImpl: typeof globalThis.fetch;
|
|
73
62
|
protected readonly logger?: HttpLogger;
|
|
74
|
-
protected readonly credentials?: RequestCredentials;
|
|
75
|
-
protected readonly onTokenRefresh?: (token: string) => void;
|
|
76
|
-
private refreshPromise;
|
|
77
63
|
constructor(options: HttpClientOptions);
|
|
78
64
|
/** Replace the bearer token for all subsequent requests. */
|
|
79
65
|
setToken(token: string): void;
|
|
@@ -87,18 +73,9 @@ export declare class HttpClient {
|
|
|
87
73
|
protected parseError(response: Response): Promise<ApiError>;
|
|
88
74
|
private log;
|
|
89
75
|
private executeFetch;
|
|
90
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
91
|
-
private getTokenExp;
|
|
92
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
93
|
-
private refreshTokenIfNeeded;
|
|
94
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
95
|
-
private forceRefresh;
|
|
96
76
|
/**
|
|
97
77
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
98
78
|
*
|
|
99
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
100
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
101
|
-
*
|
|
102
79
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
103
80
|
* @param init Standard `RequestInit` options.
|
|
104
81
|
* @param params Query parameters to append.
|
package/esm/src/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"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"}
|
package/esm/src/http.js
CHANGED
|
@@ -10,9 +10,6 @@ export class HttpClient {
|
|
|
10
10
|
token;
|
|
11
11
|
fetchImpl;
|
|
12
12
|
logger;
|
|
13
|
-
credentials;
|
|
14
|
-
onTokenRefresh;
|
|
15
|
-
refreshPromise = null;
|
|
16
13
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
17
14
|
return `HttpClient { ${this.baseUrl} }`;
|
|
18
15
|
}
|
|
@@ -24,11 +21,9 @@ export class HttpClient {
|
|
|
24
21
|
throw new Error(`Invalid MeowPanel URL: "${options.url}". An absolute URL is required (e.g. "https://panel.example.com").`);
|
|
25
22
|
}
|
|
26
23
|
this.baseUrl = options.url.replace(/\/$/, '') + '/api/v1';
|
|
27
|
-
this.token = options.token
|
|
24
|
+
this.token = options.token;
|
|
28
25
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
29
26
|
this.logger = options.logger;
|
|
30
|
-
this.credentials = options.credentials;
|
|
31
|
-
this.onTokenRefresh = options.onTokenRefresh;
|
|
32
27
|
}
|
|
33
28
|
/** Replace the bearer token for all subsequent requests. */
|
|
34
29
|
setToken(token) {
|
|
@@ -95,7 +90,7 @@ export class HttpClient {
|
|
|
95
90
|
params,
|
|
96
91
|
});
|
|
97
92
|
try {
|
|
98
|
-
const response = await this.fetchImpl(url,
|
|
93
|
+
const response = await this.fetchImpl(url, init);
|
|
99
94
|
const durationMs = Date.now() - startedAt;
|
|
100
95
|
if (!response.ok) {
|
|
101
96
|
const error = await this.parseError(response);
|
|
@@ -140,86 +135,18 @@ export class HttpClient {
|
|
|
140
135
|
throw error;
|
|
141
136
|
}
|
|
142
137
|
}
|
|
143
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
144
|
-
getTokenExp() {
|
|
145
|
-
const parts = this.token.split('.');
|
|
146
|
-
if (parts.length !== 3)
|
|
147
|
-
return 0; // Not a JWT token
|
|
148
|
-
try {
|
|
149
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
150
|
-
return typeof payload.exp === 'number' ? payload.exp : 0;
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return 0;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
157
|
-
async refreshTokenIfNeeded() {
|
|
158
|
-
const exp = this.getTokenExp();
|
|
159
|
-
if (exp === 0)
|
|
160
|
-
return;
|
|
161
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
162
|
-
if (exp - nowSec > 60)
|
|
163
|
-
return;
|
|
164
|
-
await this.forceRefresh();
|
|
165
|
-
}
|
|
166
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
167
|
-
async forceRefresh() {
|
|
168
|
-
if (this.refreshPromise) {
|
|
169
|
-
await this.refreshPromise;
|
|
170
|
-
return !!this.token;
|
|
171
|
-
}
|
|
172
|
-
this.refreshPromise = (async () => {
|
|
173
|
-
try {
|
|
174
|
-
const res = await this.fetchImpl(this.baseUrl + '/auth/refresh', {
|
|
175
|
-
method: 'POST',
|
|
176
|
-
headers: {
|
|
177
|
-
'Content-Type': 'application/json',
|
|
178
|
-
'Accept': 'application/json',
|
|
179
|
-
},
|
|
180
|
-
credentials: this.credentials,
|
|
181
|
-
});
|
|
182
|
-
if (res.ok) {
|
|
183
|
-
const data = await res.json();
|
|
184
|
-
this.token = data.access_token;
|
|
185
|
-
this.onTokenRefresh?.(data.access_token);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
finally {
|
|
189
|
-
this.refreshPromise = null;
|
|
190
|
-
}
|
|
191
|
-
})();
|
|
192
|
-
await this.refreshPromise;
|
|
193
|
-
return !!this.token;
|
|
194
|
-
}
|
|
195
138
|
/**
|
|
196
139
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
197
140
|
*
|
|
198
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
199
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
200
|
-
*
|
|
201
141
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
202
142
|
* @param init Standard `RequestInit` options.
|
|
203
143
|
* @param params Query parameters to append.
|
|
204
144
|
*/
|
|
205
145
|
async fetch(path, init = {}, params) {
|
|
206
|
-
await this.
|
|
207
|
-
const buildInit = () => ({
|
|
146
|
+
return await this.executeFetch(path, {
|
|
208
147
|
...init,
|
|
209
148
|
headers: { ...this.defaultHeaders, ...init.headers },
|
|
210
|
-
});
|
|
211
|
-
try {
|
|
212
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
if (error instanceof ApiError && error.status === 401 && path !== '/auth/refresh') {
|
|
216
|
-
const refreshed = await this.forceRefresh();
|
|
217
|
-
if (refreshed) {
|
|
218
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
throw error;
|
|
222
|
-
}
|
|
149
|
+
}, params);
|
|
223
150
|
}
|
|
224
151
|
/**
|
|
225
152
|
* GET and deserialize a JSON response body.
|
package/package.json
CHANGED
package/script/src/client.d.ts
CHANGED
|
@@ -15,19 +15,13 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export interface MeowPanelApiOptions {
|
|
17
17
|
/**
|
|
18
|
-
* Base URL of
|
|
18
|
+
* Base URL of MeowPanel instance.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @example `"https://panel.example.com"`
|
|
20
|
+
* @example "https://panel.example.com"
|
|
23
21
|
*/
|
|
24
22
|
url: string | URL;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* Can be a user session token or an API key token.
|
|
29
|
-
*/
|
|
30
|
-
token?: string;
|
|
23
|
+
/** Bearer token used to authenticate every request. */
|
|
24
|
+
token: string;
|
|
31
25
|
/**
|
|
32
26
|
* Custom fetch implementation to use for all HTTP requests.
|
|
33
27
|
*
|
|
@@ -35,24 +29,11 @@ export interface MeowPanelApiOptions {
|
|
|
35
29
|
*/
|
|
36
30
|
fetch?: typeof globalThis.fetch;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
32
|
+
* Callback invoked for every outgoing request.
|
|
39
33
|
*
|
|
40
34
|
* Useful for integrating API request visibility into your app's debug/info logger.
|
|
41
35
|
*/
|
|
42
36
|
logger?: HttpLogger;
|
|
43
|
-
/**
|
|
44
|
-
* The `credentials` mode for all fetch requests.
|
|
45
|
-
*
|
|
46
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication.
|
|
47
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
48
|
-
*/
|
|
49
|
-
credentials?: RequestCredentials;
|
|
50
|
-
/**
|
|
51
|
-
* Callback invoked when the access token is automatically refreshed.
|
|
52
|
-
*
|
|
53
|
-
* Useful for persisting the new token or updating external state.
|
|
54
|
-
*/
|
|
55
|
-
onTokenRefresh?: (token: string) => void;
|
|
56
37
|
}
|
|
57
38
|
/**
|
|
58
39
|
* The root MeowPanel API client.
|
|
@@ -66,7 +47,7 @@ export interface MeowPanelApiOptions {
|
|
|
66
47
|
*
|
|
67
48
|
* const api = new MeowPanelApi({
|
|
68
49
|
* url: 'https://panel.example.com',
|
|
69
|
-
* token: 'your-
|
|
50
|
+
* token: 'your-api-token',
|
|
70
51
|
* });
|
|
71
52
|
*
|
|
72
53
|
* // Fetch resources
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAE3D;;;;;;;;OAQG;IACH,SAAgB,IAAI,EAAE,YAAY,CAAC;IAEnC;;;;;;;;;OASG;IACH,SAAgB,OAAO,EAAE,eAAe,CAAC;IAEzC;;;;;;;;OAQG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,SAAgB,IAAI,EAAE,aAAa,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;OAUG;IACH,SAAgB,YAAY,EAAE,qBAAqB,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,OAAO,EAAE,mBAAmB;IAyB/C;;;;;;OAMG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOpC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,UAAU,IAAI,aAAa;IAQlC;;;;;OAKG;IACI,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAG/D"}
|
package/script/src/client.js
CHANGED
|
@@ -24,7 +24,7 @@ const index_js_1 = require("./resources/servers/index.js");
|
|
|
24
24
|
*
|
|
25
25
|
* const api = new MeowPanelApi({
|
|
26
26
|
* url: 'https://panel.example.com',
|
|
27
|
-
* token: 'your-
|
|
27
|
+
* token: 'your-api-token',
|
|
28
28
|
* });
|
|
29
29
|
*
|
|
30
30
|
* // Fetch resources
|
|
@@ -163,21 +163,14 @@ class MeowPanelApi {
|
|
|
163
163
|
*/
|
|
164
164
|
servers;
|
|
165
165
|
constructor(options) {
|
|
166
|
-
if (!options?.url)
|
|
166
|
+
if (!options?.url) {
|
|
167
167
|
throw new Error('MeowPanelApi requires a "url" option (e.g. "https://panel.example.com").');
|
|
168
|
-
|
|
168
|
+
}
|
|
169
169
|
this.http = new http_js_1.HttpClient({
|
|
170
170
|
url: options.url.toString(),
|
|
171
171
|
token: options.token,
|
|
172
172
|
fetch: options.fetch,
|
|
173
173
|
logger: options.logger,
|
|
174
|
-
credentials: options.credentials,
|
|
175
|
-
onTokenRefresh: (token) => {
|
|
176
|
-
for (const sse of this.sseConnections) {
|
|
177
|
-
sse.updateToken(token);
|
|
178
|
-
}
|
|
179
|
-
userCallback?.(token);
|
|
180
|
-
},
|
|
181
174
|
});
|
|
182
175
|
this.auth = new auth_js_1.AuthResource(this.http);
|
|
183
176
|
this.account = new account_js_1.AccountResource(this.http);
|
|
@@ -198,6 +191,9 @@ class MeowPanelApi {
|
|
|
198
191
|
*/
|
|
199
192
|
setToken(token) {
|
|
200
193
|
this.http.setToken(token);
|
|
194
|
+
for (const sse of this.sseConnections) {
|
|
195
|
+
sse.updateToken(token);
|
|
196
|
+
}
|
|
201
197
|
}
|
|
202
198
|
/**
|
|
203
199
|
* Create a panel-level Server-Sent Events connection to Nexus.
|
package/script/src/http.d.ts
CHANGED
|
@@ -5,22 +5,11 @@ export interface HttpClientOptions {
|
|
|
5
5
|
/** Base URL of the MeowPanel Nexus API, e.g. `https://panel.example.com`. */
|
|
6
6
|
url: string;
|
|
7
7
|
/** Bearer token used to authenticate every request. */
|
|
8
|
-
token
|
|
8
|
+
token: string;
|
|
9
9
|
/** Custom fetch implementation. Defaults to `globalThis.fetch`. */
|
|
10
10
|
fetch?: typeof globalThis.fetch;
|
|
11
11
|
/** Optional callback invoked for every outgoing request lifecycle event. */
|
|
12
12
|
logger?: HttpLogger;
|
|
13
|
-
/**
|
|
14
|
-
* The `credentials` mode for all fetch requests.
|
|
15
|
-
*
|
|
16
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication
|
|
17
|
-
* (e.g. session cookies set by the auth endpoint).
|
|
18
|
-
*
|
|
19
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
20
|
-
*/
|
|
21
|
-
credentials?: RequestCredentials;
|
|
22
|
-
/** Callback invoked when the access token is refreshed. */
|
|
23
|
-
onTokenRefresh?: (token: string) => void;
|
|
24
13
|
}
|
|
25
14
|
/** Typed lifecycle code emitted by {@link HttpClient}'s optional logger. */
|
|
26
15
|
export type HttpLogCode = 'request.start' | 'request.success' | 'request.error';
|
|
@@ -71,9 +60,6 @@ export declare class HttpClient {
|
|
|
71
60
|
protected token: string;
|
|
72
61
|
protected readonly fetchImpl: typeof globalThis.fetch;
|
|
73
62
|
protected readonly logger?: HttpLogger;
|
|
74
|
-
protected readonly credentials?: RequestCredentials;
|
|
75
|
-
protected readonly onTokenRefresh?: (token: string) => void;
|
|
76
|
-
private refreshPromise;
|
|
77
63
|
constructor(options: HttpClientOptions);
|
|
78
64
|
/** Replace the bearer token for all subsequent requests. */
|
|
79
65
|
setToken(token: string): void;
|
|
@@ -87,18 +73,9 @@ export declare class HttpClient {
|
|
|
87
73
|
protected parseError(response: Response): Promise<ApiError>;
|
|
88
74
|
private log;
|
|
89
75
|
private executeFetch;
|
|
90
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
91
|
-
private getTokenExp;
|
|
92
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
93
|
-
private refreshTokenIfNeeded;
|
|
94
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
95
|
-
private forceRefresh;
|
|
96
76
|
/**
|
|
97
77
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
98
78
|
*
|
|
99
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
100
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
101
|
-
*
|
|
102
79
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
103
80
|
* @param init Standard `RequestInit` options.
|
|
104
81
|
* @param params Query parameters to append.
|
package/script/src/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"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"}
|
package/script/src/http.js
CHANGED
|
@@ -13,9 +13,6 @@ class HttpClient {
|
|
|
13
13
|
token;
|
|
14
14
|
fetchImpl;
|
|
15
15
|
logger;
|
|
16
|
-
credentials;
|
|
17
|
-
onTokenRefresh;
|
|
18
|
-
refreshPromise = null;
|
|
19
16
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
20
17
|
return `HttpClient { ${this.baseUrl} }`;
|
|
21
18
|
}
|
|
@@ -27,11 +24,9 @@ class HttpClient {
|
|
|
27
24
|
throw new Error(`Invalid MeowPanel URL: "${options.url}". An absolute URL is required (e.g. "https://panel.example.com").`);
|
|
28
25
|
}
|
|
29
26
|
this.baseUrl = options.url.replace(/\/$/, '') + '/api/v1';
|
|
30
|
-
this.token = options.token
|
|
27
|
+
this.token = options.token;
|
|
31
28
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
32
29
|
this.logger = options.logger;
|
|
33
|
-
this.credentials = options.credentials;
|
|
34
|
-
this.onTokenRefresh = options.onTokenRefresh;
|
|
35
30
|
}
|
|
36
31
|
/** Replace the bearer token for all subsequent requests. */
|
|
37
32
|
setToken(token) {
|
|
@@ -98,7 +93,7 @@ class HttpClient {
|
|
|
98
93
|
params,
|
|
99
94
|
});
|
|
100
95
|
try {
|
|
101
|
-
const response = await this.fetchImpl(url,
|
|
96
|
+
const response = await this.fetchImpl(url, init);
|
|
102
97
|
const durationMs = Date.now() - startedAt;
|
|
103
98
|
if (!response.ok) {
|
|
104
99
|
const error = await this.parseError(response);
|
|
@@ -143,86 +138,18 @@ class HttpClient {
|
|
|
143
138
|
throw error;
|
|
144
139
|
}
|
|
145
140
|
}
|
|
146
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
147
|
-
getTokenExp() {
|
|
148
|
-
const parts = this.token.split('.');
|
|
149
|
-
if (parts.length !== 3)
|
|
150
|
-
return 0; // Not a JWT token
|
|
151
|
-
try {
|
|
152
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
153
|
-
return typeof payload.exp === 'number' ? payload.exp : 0;
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
return 0;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
160
|
-
async refreshTokenIfNeeded() {
|
|
161
|
-
const exp = this.getTokenExp();
|
|
162
|
-
if (exp === 0)
|
|
163
|
-
return;
|
|
164
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
165
|
-
if (exp - nowSec > 60)
|
|
166
|
-
return;
|
|
167
|
-
await this.forceRefresh();
|
|
168
|
-
}
|
|
169
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
170
|
-
async forceRefresh() {
|
|
171
|
-
if (this.refreshPromise) {
|
|
172
|
-
await this.refreshPromise;
|
|
173
|
-
return !!this.token;
|
|
174
|
-
}
|
|
175
|
-
this.refreshPromise = (async () => {
|
|
176
|
-
try {
|
|
177
|
-
const res = await this.fetchImpl(this.baseUrl + '/auth/refresh', {
|
|
178
|
-
method: 'POST',
|
|
179
|
-
headers: {
|
|
180
|
-
'Content-Type': 'application/json',
|
|
181
|
-
'Accept': 'application/json',
|
|
182
|
-
},
|
|
183
|
-
credentials: this.credentials,
|
|
184
|
-
});
|
|
185
|
-
if (res.ok) {
|
|
186
|
-
const data = await res.json();
|
|
187
|
-
this.token = data.access_token;
|
|
188
|
-
this.onTokenRefresh?.(data.access_token);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
finally {
|
|
192
|
-
this.refreshPromise = null;
|
|
193
|
-
}
|
|
194
|
-
})();
|
|
195
|
-
await this.refreshPromise;
|
|
196
|
-
return !!this.token;
|
|
197
|
-
}
|
|
198
141
|
/**
|
|
199
142
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
200
143
|
*
|
|
201
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
202
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
203
|
-
*
|
|
204
144
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
205
145
|
* @param init Standard `RequestInit` options.
|
|
206
146
|
* @param params Query parameters to append.
|
|
207
147
|
*/
|
|
208
148
|
async fetch(path, init = {}, params) {
|
|
209
|
-
await this.
|
|
210
|
-
const buildInit = () => ({
|
|
149
|
+
return await this.executeFetch(path, {
|
|
211
150
|
...init,
|
|
212
151
|
headers: { ...this.defaultHeaders, ...init.headers },
|
|
213
|
-
});
|
|
214
|
-
try {
|
|
215
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
if (error instanceof errors_js_1.ApiError && error.status === 401 && path !== '/auth/refresh') {
|
|
219
|
-
const refreshed = await this.forceRefresh();
|
|
220
|
-
if (refreshed) {
|
|
221
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
throw error;
|
|
225
|
-
}
|
|
152
|
+
}, params);
|
|
226
153
|
}
|
|
227
154
|
/**
|
|
228
155
|
* GET and deserialize a JSON response body.
|