@aneuhold/core-ts-api-lib 3.0.26 → 3.0.28
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/CHANGELOG.md +23 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.ts +3 -0
- package/lib/services/APIService/APIService.d.ts +29 -8
- package/lib/services/APIService/APIService.d.ts.map +1 -1
- package/lib/services/APIService/APIService.js +38 -9
- package/lib/services/APIService/APIService.js.map +1 -1
- package/lib/services/APIService/APIService.ts +44 -9
- package/lib/services/GCloudAPIService/GCloudAPIService.d.ts +52 -7
- package/lib/services/GCloudAPIService/GCloudAPIService.d.ts.map +1 -1
- package/lib/services/GCloudAPIService/GCloudAPIService.js +100 -14
- package/lib/services/GCloudAPIService/GCloudAPIService.js.map +1 -1
- package/lib/services/GCloudAPIService/GCloudAPIService.ts +133 -22
- package/lib/types/AuthRefreshToken.d.ts +17 -0
- package/lib/types/AuthRefreshToken.d.ts.map +1 -0
- package/lib/types/AuthRefreshToken.js +2 -0
- package/lib/types/AuthRefreshToken.js.map +1 -0
- package/lib/types/AuthRefreshToken.ts +17 -0
- package/lib/types/AuthValidateUser.d.ts +10 -8
- package/lib/types/AuthValidateUser.d.ts.map +1 -1
- package/lib/types/AuthValidateUser.ts +10 -8
- package/lib/types/WebSocket.d.ts +4 -1
- package/lib/types/WebSocket.d.ts.map +1 -1
- package/lib/types/WebSocket.ts +5 -2
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 🔖 [3.0.28] (2026-03-15)
|
|
9
|
+
|
|
10
|
+
### ✅ Added
|
|
11
|
+
|
|
12
|
+
- Added `AuthRefreshToken` types (`AuthRefreshTokenInput`, `AuthRefreshTokenOutput`) for the token refresh endpoint.
|
|
13
|
+
- Added `APIService.logout()` to delete the current session's refresh token server-side.
|
|
14
|
+
- Added `APIService.setAccessToken()`, `APIService.setRefreshTokenString()`, and `APIService.setOnTokensRefreshed()` for JWT token management.
|
|
15
|
+
- `GCloudAPIService` now automatically retries requests after refreshing tokens on 401 responses.
|
|
16
|
+
- `GCloudAPIService` attaches a `Bearer` `Authorization` header to all requests when an access token is set.
|
|
17
|
+
|
|
18
|
+
### 🏗️ Changed
|
|
19
|
+
|
|
20
|
+
- `AuthValidateUserInput` now accepts an optional `googleCredentialToken` for Google sign-in, with `userName` and `password` made optional to support both auth flows.
|
|
21
|
+
- `AuthValidateUserOutput` now includes optional `accessToken` and `refreshTokenString` fields.
|
|
22
|
+
|
|
23
|
+
## 🔖 [3.0.27] (2026-03-13)
|
|
24
|
+
|
|
25
|
+
### 🏗️ Changed
|
|
26
|
+
|
|
27
|
+
- Updated dependencies: now requires `@aneuhold/core-ts-db-lib@^5.0.0` and `@aneuhold/core-ts-lib@^2.4.2`.
|
|
28
|
+
|
|
8
29
|
## 🔖 [3.0.26] (2026-03-12)
|
|
9
30
|
|
|
10
31
|
### 🏗️ Changed
|
|
@@ -323,6 +344,8 @@ No direct code changes; version bump for compatibility.
|
|
|
323
344
|
|
|
324
345
|
<!-- Link References -->
|
|
325
346
|
|
|
347
|
+
[3.0.28]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.27...core-ts-api-lib-v3.0.28
|
|
348
|
+
[3.0.27]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.26...core-ts-api-lib-v3.0.27
|
|
326
349
|
[3.0.26]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.25...core-ts-api-lib-v3.0.26
|
|
327
350
|
[3.0.25]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.24...core-ts-api-lib-v3.0.25
|
|
328
351
|
[3.0.24]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.23...core-ts-api-lib-v3.0.24
|
package/lib/index.d.ts
CHANGED
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,cAAc,CAAC;AAG7B,YAAY,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
|
package/lib/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
// Export all browser-safe exports from browser.ts
|
|
2
2
|
// Since all exports in this library are browser-compatible, we simply re-export everything
|
|
3
3
|
export * from './browser.js';
|
|
4
|
+
|
|
5
|
+
// Export types only needed by backend consumers
|
|
6
|
+
export type { AuthRefreshTokenInput, AuthRefreshTokenOutput } from './types/AuthRefreshToken.js';
|
|
@@ -8,26 +8,47 @@ import type { ProjectWorkoutPrimaryInput, ProjectWorkoutPrimaryOutput } from '..
|
|
|
8
8
|
*/
|
|
9
9
|
export default class APIService {
|
|
10
10
|
/**
|
|
11
|
-
* Validates the provided
|
|
12
|
-
*
|
|
11
|
+
* Validates the provided credentials against the database and returns the
|
|
12
|
+
* user's information if successful. Supports both password and Google
|
|
13
|
+
* sign-in flows.
|
|
13
14
|
*
|
|
14
|
-
* @param input - The input containing username
|
|
15
|
-
* @returns A promise that resolves to the user's information if validation is successful.
|
|
15
|
+
* @param input - The input containing credentials (username/password or Google credential token).
|
|
16
16
|
*/
|
|
17
17
|
static validateUser(input: AuthValidateUserInput): Promise<APIResponse<AuthValidateUserOutput>>;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Logs out the current session by deleting the stored refresh token
|
|
20
|
+
* server-side.
|
|
21
|
+
*/
|
|
22
|
+
static logout(): Promise<APIResponse<undefined>>;
|
|
23
|
+
/**
|
|
24
|
+
* Sets the JWT access token to attach to all API requests.
|
|
25
|
+
*
|
|
26
|
+
* @param token - The access token.
|
|
27
|
+
*/
|
|
28
|
+
static setAccessToken(token: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Sets the refresh token string used for automatic token refresh on 401.
|
|
31
|
+
*
|
|
32
|
+
* @param token - The refresh token string.
|
|
33
|
+
*/
|
|
34
|
+
static setRefreshTokenString(token: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
37
|
+
* refreshed (e.g. to persist new tokens to localStorage).
|
|
38
|
+
*
|
|
39
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
40
|
+
*/
|
|
41
|
+
static setOnTokensRefreshed(callback: ((accessToken: string, refreshTokenString: string) => void) | null): void;
|
|
42
|
+
/**
|
|
43
|
+
* Calls the dashboard API and returns the result.
|
|
21
44
|
*
|
|
22
45
|
* @param input - The input for the dashboard API call.
|
|
23
|
-
* @returns A promise that resolves to the result of the dashboard API call.
|
|
24
46
|
*/
|
|
25
47
|
static callDashboardAPI(input: ProjectDashboardInput): Promise<APIResponse<ProjectDashboardOutput>>;
|
|
26
48
|
/**
|
|
27
49
|
* Calls the workout API and returns the result.
|
|
28
50
|
*
|
|
29
51
|
* @param input - The input for the workout API call.
|
|
30
|
-
* @returns A promise that resolves to the result of the workout API call.
|
|
31
52
|
*/
|
|
32
53
|
static callWorkoutAPI(input: ProjectWorkoutPrimaryInput): Promise<APIResponse<ProjectWorkoutPrimaryOutput>>;
|
|
33
54
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"APIService.d.ts","sourceRoot":"","sources":["../../../src/services/APIService/APIService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,+CAA+C,CAAC;AAGvD;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;;;;;OAMG;WACU,YAAY,CACvB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C
|
|
1
|
+
{"version":3,"file":"APIService.d.ts","sourceRoot":"","sources":["../../../src/services/APIService/APIService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,+CAA+C,CAAC;AAGvD;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;;;;;OAMG;WACU,YAAY,CACvB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;OAGG;WACU,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAItD;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjD;;;;;OAKG;IACH,MAAM,CAAC,oBAAoB,CACzB,QAAQ,EAAE,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,GAC3E,IAAI;IAIP;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;;OAIG;WACU,cAAc,CACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAIpD;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,MAAM;IAIjC;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,MAAM;CAGlC"}
|
|
@@ -5,21 +5,51 @@ import GCloudAPIService from '../GCloudAPIService/GCloudAPIService.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export default class APIService {
|
|
7
7
|
/**
|
|
8
|
-
* Validates the provided
|
|
9
|
-
*
|
|
8
|
+
* Validates the provided credentials against the database and returns the
|
|
9
|
+
* user's information if successful. Supports both password and Google
|
|
10
|
+
* sign-in flows.
|
|
10
11
|
*
|
|
11
|
-
* @param input - The input containing username
|
|
12
|
-
* @returns A promise that resolves to the user's information if validation is successful.
|
|
12
|
+
* @param input - The input containing credentials (username/password or Google credential token).
|
|
13
13
|
*/
|
|
14
14
|
static async validateUser(input) {
|
|
15
|
-
return
|
|
15
|
+
return GCloudAPIService.authValidateUser(input);
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Logs out the current session by deleting the stored refresh token
|
|
19
|
+
* server-side.
|
|
20
|
+
*/
|
|
21
|
+
static async logout() {
|
|
22
|
+
return GCloudAPIService.authLogout();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sets the JWT access token to attach to all API requests.
|
|
26
|
+
*
|
|
27
|
+
* @param token - The access token.
|
|
28
|
+
*/
|
|
29
|
+
static setAccessToken(token) {
|
|
30
|
+
GCloudAPIService.setAccessToken(token);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sets the refresh token string used for automatic token refresh on 401.
|
|
34
|
+
*
|
|
35
|
+
* @param token - The refresh token string.
|
|
36
|
+
*/
|
|
37
|
+
static setRefreshTokenString(token) {
|
|
38
|
+
GCloudAPIService.setRefreshTokenString(token);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
42
|
+
* refreshed (e.g. to persist new tokens to localStorage).
|
|
43
|
+
*
|
|
44
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
45
|
+
*/
|
|
46
|
+
static setOnTokensRefreshed(callback) {
|
|
47
|
+
GCloudAPIService.setOnTokensRefreshed(callback);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Calls the dashboard API and returns the result.
|
|
20
51
|
*
|
|
21
52
|
* @param input - The input for the dashboard API call.
|
|
22
|
-
* @returns A promise that resolves to the result of the dashboard API call.
|
|
23
53
|
*/
|
|
24
54
|
static async callDashboardAPI(input) {
|
|
25
55
|
return GCloudAPIService.projectDashboard(input);
|
|
@@ -28,7 +58,6 @@ export default class APIService {
|
|
|
28
58
|
* Calls the workout API and returns the result.
|
|
29
59
|
*
|
|
30
60
|
* @param input - The input for the workout API call.
|
|
31
|
-
* @returns A promise that resolves to the result of the workout API call.
|
|
32
61
|
*/
|
|
33
62
|
static async callWorkoutAPI(input) {
|
|
34
63
|
return GCloudAPIService.projectWorkout(input);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"APIService.js","sourceRoot":"","sources":["../../../src/services/APIService/APIService.ts"],"names":[],"mappings":"AAaA,OAAO,gBAAgB,MAAM,yCAAyC,CAAC;AAEvE;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,YAAY,CACvB,KAA4B;QAE5B,OAAO,MAAM,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"APIService.js","sourceRoot":"","sources":["../../../src/services/APIService/APIService.ts"],"names":[],"mappings":"AAaA,OAAO,gBAAgB,MAAM,yCAAyC,CAAC;AAEvE;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,YAAY,CACvB,KAA4B;QAE5B,OAAO,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM;QACjB,OAAO,gBAAgB,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,KAAa;QACjC,gBAAgB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAa;QACxC,gBAAgB,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,oBAAoB,CACzB,QAA4E;QAE5E,gBAAgB,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAC3B,KAA4B;QAE5B,OAAO,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,cAAc,CACzB,KAAiC;QAEjC,OAAO,gBAAgB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB;QACrB,OAAO,gBAAgB,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,GAAW;QAC1B,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB;QACrB,OAAO,gBAAgB,CAAC,UAAU,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -19,24 +19,60 @@ import GCloudAPIService from '../GCloudAPIService/GCloudAPIService.js';
|
|
|
19
19
|
*/
|
|
20
20
|
export default class APIService {
|
|
21
21
|
/**
|
|
22
|
-
* Validates the provided
|
|
23
|
-
*
|
|
22
|
+
* Validates the provided credentials against the database and returns the
|
|
23
|
+
* user's information if successful. Supports both password and Google
|
|
24
|
+
* sign-in flows.
|
|
24
25
|
*
|
|
25
|
-
* @param input - The input containing username
|
|
26
|
-
* @returns A promise that resolves to the user's information if validation is successful.
|
|
26
|
+
* @param input - The input containing credentials (username/password or Google credential token).
|
|
27
27
|
*/
|
|
28
28
|
static async validateUser(
|
|
29
29
|
input: AuthValidateUserInput
|
|
30
30
|
): Promise<APIResponse<AuthValidateUserOutput>> {
|
|
31
|
-
return
|
|
31
|
+
return GCloudAPIService.authValidateUser(input);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* Logs out the current session by deleting the stored refresh token
|
|
36
|
+
* server-side.
|
|
37
|
+
*/
|
|
38
|
+
static async logout(): Promise<APIResponse<undefined>> {
|
|
39
|
+
return GCloudAPIService.authLogout();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sets the JWT access token to attach to all API requests.
|
|
44
|
+
*
|
|
45
|
+
* @param token - The access token.
|
|
46
|
+
*/
|
|
47
|
+
static setAccessToken(token: string): void {
|
|
48
|
+
GCloudAPIService.setAccessToken(token);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sets the refresh token string used for automatic token refresh on 401.
|
|
53
|
+
*
|
|
54
|
+
* @param token - The refresh token string.
|
|
55
|
+
*/
|
|
56
|
+
static setRefreshTokenString(token: string): void {
|
|
57
|
+
GCloudAPIService.setRefreshTokenString(token);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
62
|
+
* refreshed (e.g. to persist new tokens to localStorage).
|
|
63
|
+
*
|
|
64
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
65
|
+
*/
|
|
66
|
+
static setOnTokensRefreshed(
|
|
67
|
+
callback: ((accessToken: string, refreshTokenString: string) => void) | null
|
|
68
|
+
): void {
|
|
69
|
+
GCloudAPIService.setOnTokensRefreshed(callback);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Calls the dashboard API and returns the result.
|
|
37
74
|
*
|
|
38
75
|
* @param input - The input for the dashboard API call.
|
|
39
|
-
* @returns A promise that resolves to the result of the dashboard API call.
|
|
40
76
|
*/
|
|
41
77
|
static async callDashboardAPI(
|
|
42
78
|
input: ProjectDashboardInput
|
|
@@ -48,7 +84,6 @@ export default class APIService {
|
|
|
48
84
|
* Calls the workout API and returns the result.
|
|
49
85
|
*
|
|
50
86
|
* @param input - The input for the workout API call.
|
|
51
|
-
* @returns A promise that resolves to the result of the workout API call.
|
|
52
87
|
*/
|
|
53
88
|
static async callWorkoutAPI(
|
|
54
89
|
input: ProjectWorkoutPrimaryInput
|
|
@@ -2,6 +2,11 @@ import type { APIResponse } from '../../types/APIResponse.js';
|
|
|
2
2
|
import type { AuthValidateUserInput, AuthValidateUserOutput } from '../../types/AuthValidateUser.js';
|
|
3
3
|
import type { ProjectDashboardInput, ProjectDashboardOutput } from '../../types/project/dashboard/ProjectDashboard.js';
|
|
4
4
|
import type { ProjectWorkoutPrimaryInput, ProjectWorkoutPrimaryOutput } from '../../types/project/workout/ProjectWorkout.js';
|
|
5
|
+
/**
|
|
6
|
+
* Callback invoked after tokens are successfully refreshed. The frontend
|
|
7
|
+
* should use this to persist the new tokens (e.g. to localStorage).
|
|
8
|
+
*/
|
|
9
|
+
type OnTokensRefreshedCallback = (accessToken: string, refreshTokenString: string) => void;
|
|
5
10
|
/**
|
|
6
11
|
* A service for interacting with the Google Cloud API service for personal projects.
|
|
7
12
|
*/
|
|
@@ -21,11 +26,37 @@ export default class GCloudAPIService {
|
|
|
21
26
|
*/
|
|
22
27
|
static setUrl(url: string): void;
|
|
23
28
|
/**
|
|
24
|
-
*
|
|
29
|
+
* Sets the JWT access token to attach to all API requests.
|
|
25
30
|
*
|
|
26
|
-
* @param
|
|
31
|
+
* @param token - The access token.
|
|
32
|
+
*/
|
|
33
|
+
static setAccessToken(token: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Sets the refresh token string used for automatic token refresh on 401
|
|
36
|
+
* responses.
|
|
37
|
+
*
|
|
38
|
+
* @param token - The refresh token string.
|
|
39
|
+
*/
|
|
40
|
+
static setRefreshTokenString(token: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
43
|
+
* refreshed. Use this to persist the new tokens to storage (e.g.
|
|
44
|
+
* localStorage).
|
|
45
|
+
*
|
|
46
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
47
|
+
*/
|
|
48
|
+
static setOnTokensRefreshed(callback: OnTokensRefreshedCallback | null): void;
|
|
49
|
+
/**
|
|
50
|
+
* Calls the auth validateUser endpoint.
|
|
51
|
+
*
|
|
52
|
+
* @param input - The input for the validateUser endpoint.
|
|
27
53
|
*/
|
|
28
54
|
static authValidateUser(input: AuthValidateUserInput): Promise<APIResponse<AuthValidateUserOutput>>;
|
|
55
|
+
/**
|
|
56
|
+
* Calls the auth logout endpoint to delete the current refresh token
|
|
57
|
+
* server-side using the stored refresh token string.
|
|
58
|
+
*/
|
|
59
|
+
static authLogout(): Promise<APIResponse<undefined>>;
|
|
29
60
|
/**
|
|
30
61
|
* Calls the project dashboard endpoint to get, insert, update, or delete dashboard data.
|
|
31
62
|
*
|
|
@@ -39,19 +70,33 @@ export default class GCloudAPIService {
|
|
|
39
70
|
*/
|
|
40
71
|
static projectWorkout(input: ProjectWorkoutPrimaryInput): Promise<APIResponse<ProjectWorkoutPrimaryOutput>>;
|
|
41
72
|
/**
|
|
42
|
-
* Makes a call to the
|
|
73
|
+
* Makes a call to the API. On a 401 response, automatically attempts to
|
|
74
|
+
* refresh the access token using the stored refresh token. If refresh
|
|
75
|
+
* succeeds, the original request is retried once.
|
|
43
76
|
*
|
|
44
77
|
* @param urlPath - The path to the endpoint.
|
|
45
78
|
* @param input - The input to the endpoint.
|
|
46
|
-
* @throws {Error} Will throw an error if the URL is not set.
|
|
47
79
|
*/
|
|
48
80
|
private static call;
|
|
49
81
|
/**
|
|
50
|
-
*
|
|
82
|
+
* Attempts to refresh the access token using the stored refresh token.
|
|
83
|
+
* On success, updates the stored tokens and notifies via the
|
|
84
|
+
* {@link #onTokensRefreshed} callback.
|
|
85
|
+
*/
|
|
86
|
+
private static tryRefreshTokens;
|
|
87
|
+
/**
|
|
88
|
+
* Performs a POST request and decodes the JSON response.
|
|
89
|
+
*
|
|
90
|
+
* @param urlPath - The path to the endpoint.
|
|
91
|
+
* @param input - The input to the endpoint.
|
|
92
|
+
*/
|
|
93
|
+
private static fetchAndDecode;
|
|
94
|
+
/**
|
|
95
|
+
* Decodes a fetch Response into an APIResponse.
|
|
51
96
|
*
|
|
52
|
-
* @param response - The response to decode.
|
|
53
|
-
* @returns The decoded output.
|
|
97
|
+
* @param response - The fetch response to decode.
|
|
54
98
|
*/
|
|
55
99
|
private static decodeResponse;
|
|
56
100
|
}
|
|
101
|
+
export {};
|
|
57
102
|
//# sourceMappingURL=GCloudAPIService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GCloudAPIService.d.ts","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"GCloudAPIService.d.ts","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,+CAA+C,CAAC;AAEvD;;;GAGG;AACH,KAAK,yBAAyB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3F;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IACnC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAmC;IAcrE;;;;OAIG;IACH,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjD;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,GAAG,IAAI;IAI7E;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;OAGG;WACU,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAU1D;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;;OAIG;WACU,cAAc,CACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAIpD;;;;;;;OAOG;mBACkB,IAAI;IAiBzB;;;;OAIG;mBACkB,gBAAgB;IAuBrC;;;;;OAKG;mBACkB,cAAc;IAuBnC;;;;OAIG;mBACkB,cAAc;CAYpC"}
|
|
@@ -9,6 +9,9 @@ export default class GCloudAPIService {
|
|
|
9
9
|
* the trailing slash.
|
|
10
10
|
*/
|
|
11
11
|
static #baseUrl = this.defaultUrl;
|
|
12
|
+
static #accessToken;
|
|
13
|
+
static #refreshTokenString;
|
|
14
|
+
static #onTokensRefreshed = null;
|
|
12
15
|
/**
|
|
13
16
|
* Gets the current URL of the Google Cloud API.
|
|
14
17
|
*
|
|
@@ -26,13 +29,53 @@ export default class GCloudAPIService {
|
|
|
26
29
|
this.#baseUrl = url;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
|
-
*
|
|
32
|
+
* Sets the JWT access token to attach to all API requests.
|
|
30
33
|
*
|
|
31
|
-
* @param
|
|
34
|
+
* @param token - The access token.
|
|
35
|
+
*/
|
|
36
|
+
static setAccessToken(token) {
|
|
37
|
+
this.#accessToken = token;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets the refresh token string used for automatic token refresh on 401
|
|
41
|
+
* responses.
|
|
42
|
+
*
|
|
43
|
+
* @param token - The refresh token string.
|
|
44
|
+
*/
|
|
45
|
+
static setRefreshTokenString(token) {
|
|
46
|
+
this.#refreshTokenString = token;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
50
|
+
* refreshed. Use this to persist the new tokens to storage (e.g.
|
|
51
|
+
* localStorage).
|
|
52
|
+
*
|
|
53
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
54
|
+
*/
|
|
55
|
+
static setOnTokensRefreshed(callback) {
|
|
56
|
+
this.#onTokensRefreshed = callback;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Calls the auth validateUser endpoint.
|
|
60
|
+
*
|
|
61
|
+
* @param input - The input for the validateUser endpoint.
|
|
32
62
|
*/
|
|
33
63
|
static async authValidateUser(input) {
|
|
34
64
|
return this.call('auth/validateUser', input);
|
|
35
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Calls the auth logout endpoint to delete the current refresh token
|
|
68
|
+
* server-side using the stored refresh token string.
|
|
69
|
+
*/
|
|
70
|
+
static async authLogout() {
|
|
71
|
+
if (!this.#refreshTokenString) {
|
|
72
|
+
return { success: true, errors: [], data: undefined };
|
|
73
|
+
}
|
|
74
|
+
const { decoded } = await this.fetchAndDecode('auth/logout', {
|
|
75
|
+
refreshTokenString: this.#refreshTokenString
|
|
76
|
+
});
|
|
77
|
+
return decoded;
|
|
78
|
+
}
|
|
36
79
|
/**
|
|
37
80
|
* Calls the project dashboard endpoint to get, insert, update, or delete dashboard data.
|
|
38
81
|
*
|
|
@@ -50,30 +93,73 @@ export default class GCloudAPIService {
|
|
|
50
93
|
return this.call('project/workout', input);
|
|
51
94
|
}
|
|
52
95
|
/**
|
|
53
|
-
* Makes a call to the
|
|
96
|
+
* Makes a call to the API. On a 401 response, automatically attempts to
|
|
97
|
+
* refresh the access token using the stored refresh token. If refresh
|
|
98
|
+
* succeeds, the original request is retried once.
|
|
54
99
|
*
|
|
55
100
|
* @param urlPath - The path to the endpoint.
|
|
56
101
|
* @param input - The input to the endpoint.
|
|
57
|
-
* @throws {Error} Will throw an error if the URL is not set.
|
|
58
102
|
*/
|
|
59
103
|
static async call(urlPath, input) {
|
|
104
|
+
const { response, decoded } = await this.fetchAndDecode(urlPath, input);
|
|
105
|
+
if (response.status === 401 && this.#refreshTokenString) {
|
|
106
|
+
const refreshed = await this.tryRefreshTokens();
|
|
107
|
+
if (refreshed) {
|
|
108
|
+
const retry = await this.fetchAndDecode(urlPath, input);
|
|
109
|
+
return retry.decoded;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return decoded;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Attempts to refresh the access token using the stored refresh token.
|
|
116
|
+
* On success, updates the stored tokens and notifies via the
|
|
117
|
+
* {@link #onTokensRefreshed} callback.
|
|
118
|
+
*/
|
|
119
|
+
static async tryRefreshTokens() {
|
|
120
|
+
if (!this.#refreshTokenString) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const { decoded } = await this.fetchAndDecode('auth/refresh', {
|
|
124
|
+
refreshTokenString: this.#refreshTokenString
|
|
125
|
+
});
|
|
126
|
+
if (!decoded.success) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
this.#accessToken = decoded.data.accessToken;
|
|
130
|
+
this.#refreshTokenString = decoded.data.refreshTokenString;
|
|
131
|
+
if (this.#onTokensRefreshed) {
|
|
132
|
+
this.#onTokensRefreshed(decoded.data.accessToken, decoded.data.refreshTokenString);
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Performs a POST request and decodes the JSON response.
|
|
138
|
+
*
|
|
139
|
+
* @param urlPath - The path to the endpoint.
|
|
140
|
+
* @param input - The input to the endpoint.
|
|
141
|
+
*/
|
|
142
|
+
static async fetchAndDecode(urlPath, input) {
|
|
143
|
+
const headers = new Headers({
|
|
144
|
+
Connection: 'keep-alive',
|
|
145
|
+
'Content-Type': 'application/json',
|
|
146
|
+
Accept: 'application/json'
|
|
147
|
+
});
|
|
148
|
+
if (this.#accessToken) {
|
|
149
|
+
headers.set('Authorization', `Bearer ${this.#accessToken}`);
|
|
150
|
+
}
|
|
60
151
|
const response = await fetch(this.#baseUrl + urlPath, {
|
|
61
152
|
method: 'POST',
|
|
62
|
-
headers
|
|
63
|
-
Connection: 'keep-alive',
|
|
64
|
-
'Content-Type': 'application/json',
|
|
65
|
-
Accept: 'application/json'
|
|
66
|
-
},
|
|
153
|
+
headers,
|
|
67
154
|
body: JSON.stringify(input)
|
|
68
155
|
});
|
|
69
|
-
const
|
|
70
|
-
return
|
|
156
|
+
const decoded = await this.decodeResponse(response);
|
|
157
|
+
return { response, decoded };
|
|
71
158
|
}
|
|
72
159
|
/**
|
|
73
|
-
* Decodes
|
|
160
|
+
* Decodes a fetch Response into an APIResponse.
|
|
74
161
|
*
|
|
75
|
-
* @param response - The response to decode.
|
|
76
|
-
* @returns The decoded output.
|
|
162
|
+
* @param response - The fetch response to decode.
|
|
77
163
|
*/
|
|
78
164
|
static async decodeResponse(response) {
|
|
79
165
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GCloudAPIService.js","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"GCloudAPIService.js","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DateService, ErrorUtils } from '@aneuhold/core-ts-lib';
|
|
2
2
|
import type { APIResponse } from '../../types/APIResponse.js';
|
|
3
|
+
import type { AuthRefreshTokenOutput } from '../../types/AuthRefreshToken.js';
|
|
3
4
|
import type {
|
|
4
5
|
AuthValidateUserInput,
|
|
5
6
|
AuthValidateUserOutput
|
|
@@ -13,6 +14,12 @@ import type {
|
|
|
13
14
|
ProjectWorkoutPrimaryOutput
|
|
14
15
|
} from '../../types/project/workout/ProjectWorkout.js';
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Callback invoked after tokens are successfully refreshed. The frontend
|
|
19
|
+
* should use this to persist the new tokens (e.g. to localStorage).
|
|
20
|
+
*/
|
|
21
|
+
type OnTokensRefreshedCallback = (accessToken: string, refreshTokenString: string) => void;
|
|
22
|
+
|
|
16
23
|
/**
|
|
17
24
|
* A service for interacting with the Google Cloud API service for personal projects.
|
|
18
25
|
*/
|
|
@@ -25,6 +32,12 @@ export default class GCloudAPIService {
|
|
|
25
32
|
*/
|
|
26
33
|
static #baseUrl: string = this.defaultUrl;
|
|
27
34
|
|
|
35
|
+
static #accessToken: string | undefined;
|
|
36
|
+
|
|
37
|
+
static #refreshTokenString: string | undefined;
|
|
38
|
+
|
|
39
|
+
static #onTokensRefreshed: OnTokensRefreshedCallback | null = null;
|
|
40
|
+
|
|
28
41
|
/**
|
|
29
42
|
* Gets the current URL of the Google Cloud API.
|
|
30
43
|
*
|
|
@@ -44,14 +57,58 @@ export default class GCloudAPIService {
|
|
|
44
57
|
}
|
|
45
58
|
|
|
46
59
|
/**
|
|
47
|
-
*
|
|
60
|
+
* Sets the JWT access token to attach to all API requests.
|
|
48
61
|
*
|
|
49
|
-
* @param
|
|
62
|
+
* @param token - The access token.
|
|
63
|
+
*/
|
|
64
|
+
static setAccessToken(token: string): void {
|
|
65
|
+
this.#accessToken = token;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sets the refresh token string used for automatic token refresh on 401
|
|
70
|
+
* responses.
|
|
71
|
+
*
|
|
72
|
+
* @param token - The refresh token string.
|
|
73
|
+
*/
|
|
74
|
+
static setRefreshTokenString(token: string): void {
|
|
75
|
+
this.#refreshTokenString = token;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Registers a callback that is invoked after tokens are automatically
|
|
80
|
+
* refreshed. Use this to persist the new tokens to storage (e.g.
|
|
81
|
+
* localStorage).
|
|
82
|
+
*
|
|
83
|
+
* @param callback - The callback receiving the new accessToken and refreshTokenString.
|
|
84
|
+
*/
|
|
85
|
+
static setOnTokensRefreshed(callback: OnTokensRefreshedCallback | null): void {
|
|
86
|
+
this.#onTokensRefreshed = callback;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Calls the auth validateUser endpoint.
|
|
91
|
+
*
|
|
92
|
+
* @param input - The input for the validateUser endpoint.
|
|
50
93
|
*/
|
|
51
94
|
static async authValidateUser(
|
|
52
95
|
input: AuthValidateUserInput
|
|
53
96
|
): Promise<APIResponse<AuthValidateUserOutput>> {
|
|
54
|
-
return this.call<
|
|
97
|
+
return this.call<AuthValidateUserOutput>('auth/validateUser', input);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Calls the auth logout endpoint to delete the current refresh token
|
|
102
|
+
* server-side using the stored refresh token string.
|
|
103
|
+
*/
|
|
104
|
+
static async authLogout(): Promise<APIResponse<undefined>> {
|
|
105
|
+
if (!this.#refreshTokenString) {
|
|
106
|
+
return { success: true, errors: [], data: undefined };
|
|
107
|
+
}
|
|
108
|
+
const { decoded } = await this.fetchAndDecode<undefined>('auth/logout', {
|
|
109
|
+
refreshTokenString: this.#refreshTokenString
|
|
110
|
+
});
|
|
111
|
+
return decoded;
|
|
55
112
|
}
|
|
56
113
|
|
|
57
114
|
/**
|
|
@@ -62,7 +119,7 @@ export default class GCloudAPIService {
|
|
|
62
119
|
static async projectDashboard(
|
|
63
120
|
input: ProjectDashboardInput
|
|
64
121
|
): Promise<APIResponse<ProjectDashboardOutput>> {
|
|
65
|
-
return this.call<
|
|
122
|
+
return this.call<ProjectDashboardOutput>('project/dashboard', input);
|
|
66
123
|
}
|
|
67
124
|
|
|
68
125
|
/**
|
|
@@ -73,41 +130,95 @@ export default class GCloudAPIService {
|
|
|
73
130
|
static async projectWorkout(
|
|
74
131
|
input: ProjectWorkoutPrimaryInput
|
|
75
132
|
): Promise<APIResponse<ProjectWorkoutPrimaryOutput>> {
|
|
76
|
-
return this.call<
|
|
77
|
-
'project/workout',
|
|
78
|
-
input
|
|
79
|
-
);
|
|
133
|
+
return this.call<ProjectWorkoutPrimaryOutput>('project/workout', input);
|
|
80
134
|
}
|
|
81
135
|
|
|
82
136
|
/**
|
|
83
|
-
* Makes a call to the
|
|
137
|
+
* Makes a call to the API. On a 401 response, automatically attempts to
|
|
138
|
+
* refresh the access token using the stored refresh token. If refresh
|
|
139
|
+
* succeeds, the original request is retried once.
|
|
84
140
|
*
|
|
85
141
|
* @param urlPath - The path to the endpoint.
|
|
86
142
|
* @param input - The input to the endpoint.
|
|
87
|
-
* @throws {Error} Will throw an error if the URL is not set.
|
|
88
143
|
*/
|
|
89
|
-
private static async call<
|
|
144
|
+
private static async call<TOutput>(
|
|
90
145
|
urlPath: string,
|
|
91
|
-
input:
|
|
146
|
+
input: object
|
|
92
147
|
): Promise<APIResponse<TOutput>> {
|
|
148
|
+
const { response, decoded } = await this.fetchAndDecode<TOutput>(urlPath, input);
|
|
149
|
+
|
|
150
|
+
if (response.status === 401 && this.#refreshTokenString) {
|
|
151
|
+
const refreshed = await this.tryRefreshTokens();
|
|
152
|
+
if (refreshed) {
|
|
153
|
+
const retry = await this.fetchAndDecode<TOutput>(urlPath, input);
|
|
154
|
+
return retry.decoded;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return decoded;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Attempts to refresh the access token using the stored refresh token.
|
|
163
|
+
* On success, updates the stored tokens and notifies via the
|
|
164
|
+
* {@link #onTokensRefreshed} callback.
|
|
165
|
+
*/
|
|
166
|
+
private static async tryRefreshTokens(): Promise<boolean> {
|
|
167
|
+
if (!this.#refreshTokenString) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const { decoded } = await this.fetchAndDecode<AuthRefreshTokenOutput>('auth/refresh', {
|
|
172
|
+
refreshTokenString: this.#refreshTokenString
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!decoded.success) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.#accessToken = decoded.data.accessToken;
|
|
180
|
+
this.#refreshTokenString = decoded.data.refreshTokenString;
|
|
181
|
+
|
|
182
|
+
if (this.#onTokensRefreshed) {
|
|
183
|
+
this.#onTokensRefreshed(decoded.data.accessToken, decoded.data.refreshTokenString);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Performs a POST request and decodes the JSON response.
|
|
191
|
+
*
|
|
192
|
+
* @param urlPath - The path to the endpoint.
|
|
193
|
+
* @param input - The input to the endpoint.
|
|
194
|
+
*/
|
|
195
|
+
private static async fetchAndDecode<TOutput>(
|
|
196
|
+
urlPath: string,
|
|
197
|
+
input: object
|
|
198
|
+
): Promise<{ response: Response; decoded: APIResponse<TOutput> }> {
|
|
199
|
+
const headers = new Headers({
|
|
200
|
+
Connection: 'keep-alive',
|
|
201
|
+
'Content-Type': 'application/json',
|
|
202
|
+
Accept: 'application/json'
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (this.#accessToken) {
|
|
206
|
+
headers.set('Authorization', `Bearer ${this.#accessToken}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
93
209
|
const response = await fetch(this.#baseUrl + urlPath, {
|
|
94
210
|
method: 'POST',
|
|
95
|
-
headers
|
|
96
|
-
Connection: 'keep-alive',
|
|
97
|
-
'Content-Type': 'application/json',
|
|
98
|
-
Accept: 'application/json'
|
|
99
|
-
},
|
|
211
|
+
headers,
|
|
100
212
|
body: JSON.stringify(input)
|
|
101
213
|
});
|
|
102
|
-
const
|
|
103
|
-
return
|
|
214
|
+
const decoded = await this.decodeResponse<TOutput>(response);
|
|
215
|
+
return { response, decoded };
|
|
104
216
|
}
|
|
105
217
|
|
|
106
218
|
/**
|
|
107
|
-
* Decodes
|
|
219
|
+
* Decodes a fetch Response into an APIResponse.
|
|
108
220
|
*
|
|
109
|
-
* @param response - The response to decode.
|
|
110
|
-
* @returns The decoded output.
|
|
221
|
+
* @param response - The fetch response to decode.
|
|
111
222
|
*/
|
|
112
223
|
private static async decodeResponse<TOutput>(response: Response): Promise<APIResponse<TOutput>> {
|
|
113
224
|
try {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface representing the input to the auth refresh token endpoint.
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthRefreshTokenInput {
|
|
5
|
+
/** The raw refresh token string to exchange for new tokens. */
|
|
6
|
+
refreshTokenString: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Interface representing the output of the auth refresh token endpoint.
|
|
10
|
+
*/
|
|
11
|
+
export interface AuthRefreshTokenOutput {
|
|
12
|
+
/** New JWT access token. */
|
|
13
|
+
accessToken: string;
|
|
14
|
+
/** New raw refresh token string (rotation — old one is deleted). */
|
|
15
|
+
refreshTokenString: string;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=AuthRefreshToken.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthRefreshToken.d.ts","sourceRoot":"","sources":["../../src/types/AuthRefreshToken.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthRefreshToken.js","sourceRoot":"","sources":["../../src/types/AuthRefreshToken.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface representing the input to the auth refresh token endpoint.
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthRefreshTokenInput {
|
|
5
|
+
/** The raw refresh token string to exchange for new tokens. */
|
|
6
|
+
refreshTokenString: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interface representing the output of the auth refresh token endpoint.
|
|
11
|
+
*/
|
|
12
|
+
export interface AuthRefreshTokenOutput {
|
|
13
|
+
/** New JWT access token. */
|
|
14
|
+
accessToken: string;
|
|
15
|
+
/** New raw refresh token string (rotation — old one is deleted). */
|
|
16
|
+
refreshTokenString: string;
|
|
17
|
+
}
|
|
@@ -4,14 +4,12 @@ import type { DashboardConfig } from './project/dashboard/DashboardConfig.js';
|
|
|
4
4
|
* Interface representing the input to the AuthValidateUser endpoint.
|
|
5
5
|
*/
|
|
6
6
|
export interface AuthValidateUserInput {
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
password: string;
|
|
7
|
+
/** The username of the user to be validated (password flow). */
|
|
8
|
+
userName?: string;
|
|
9
|
+
/** The password of the user to be validated (password flow). */
|
|
10
|
+
password?: string;
|
|
11
|
+
/** Google ID token received from Google Identity Services (Google flow). */
|
|
12
|
+
googleCredentialToken?: string;
|
|
15
13
|
}
|
|
16
14
|
/**
|
|
17
15
|
* Interface representing the output of the AuthValidateUser endpoint.
|
|
@@ -24,6 +22,10 @@ export interface AuthValidateUserOutput {
|
|
|
24
22
|
user: User;
|
|
25
23
|
apiKey: ApiKey;
|
|
26
24
|
};
|
|
25
|
+
/** JWT access token for authenticating API requests. */
|
|
26
|
+
accessToken?: string;
|
|
27
|
+
/** Raw refresh token string for obtaining new access tokens. */
|
|
28
|
+
refreshTokenString?: string;
|
|
27
29
|
/**
|
|
28
30
|
* Basic configuration for the projects that the user has access to.
|
|
29
31
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthValidateUser.d.ts","sourceRoot":"","sources":["../../src/types/AuthValidateUser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC
|
|
1
|
+
{"version":3,"file":"AuthValidateUser.d.ts","sourceRoot":"","sources":["../../src/types/AuthValidateUser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,IAAI,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,eAAe,CAAC;KAC7B,CAAC;CACH"}
|
|
@@ -5,14 +5,12 @@ import type { DashboardConfig } from './project/dashboard/DashboardConfig.js';
|
|
|
5
5
|
* Interface representing the input to the AuthValidateUser endpoint.
|
|
6
6
|
*/
|
|
7
7
|
export interface AuthValidateUserInput {
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
-
password: string;
|
|
8
|
+
/** The username of the user to be validated (password flow). */
|
|
9
|
+
userName?: string;
|
|
10
|
+
/** The password of the user to be validated (password flow). */
|
|
11
|
+
password?: string;
|
|
12
|
+
/** Google ID token received from Google Identity Services (Google flow). */
|
|
13
|
+
googleCredentialToken?: string;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
/**
|
|
@@ -26,6 +24,10 @@ export interface AuthValidateUserOutput {
|
|
|
26
24
|
user: User;
|
|
27
25
|
apiKey: ApiKey;
|
|
28
26
|
};
|
|
27
|
+
/** JWT access token for authenticating API requests. */
|
|
28
|
+
accessToken?: string;
|
|
29
|
+
/** Raw refresh token string for obtaining new access tokens. */
|
|
30
|
+
refreshTokenString?: string;
|
|
29
31
|
/**
|
|
30
32
|
* Basic configuration for the projects that the user has access to.
|
|
31
33
|
*/
|
package/lib/types/WebSocket.d.ts
CHANGED
|
@@ -7,13 +7,16 @@ import type { ProjectWorkoutPrimaryOutput } from './project/workout/ProjectWorko
|
|
|
7
7
|
* ```
|
|
8
8
|
const socket = io('http://localhost:8080', {
|
|
9
9
|
auth: {
|
|
10
|
-
|
|
10
|
+
accessToken: 'your-jwt-token'
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
export type WebSocketHandshakeAuth = {
|
|
16
|
+
/** @deprecated Use accessToken instead. Kept for backward compatibility. */
|
|
16
17
|
apiKey?: UUID;
|
|
18
|
+
/** JWT access token for authenticating the WebSocket connection. */
|
|
19
|
+
accessToken?: string;
|
|
17
20
|
};
|
|
18
21
|
/**
|
|
19
22
|
* Nothing for now, except a test event if wanted.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocket.d.ts","sourceRoot":"","sources":["../../src/types/WebSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"WebSocket.d.ts","sourceRoot":"","sources":["../../src/types/WebSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,cAAc,EAAE,CAAC,IAAI,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC7D,CAAC"}
|
package/lib/types/WebSocket.ts
CHANGED
|
@@ -4,17 +4,20 @@ import type { ProjectWorkoutPrimaryOutput } from './project/workout/ProjectWorko
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The data each client is expected to send over with a request. For example on the client side:
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* ```
|
|
9
9
|
const socket = io('http://localhost:8080', {
|
|
10
10
|
auth: {
|
|
11
|
-
|
|
11
|
+
accessToken: 'your-jwt-token'
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
export type WebSocketHandshakeAuth = {
|
|
17
|
+
/** @deprecated Use accessToken instead. Kept for backward compatibility. */
|
|
17
18
|
apiKey?: UUID;
|
|
19
|
+
/** JWT access token for authenticating the WebSocket connection. */
|
|
20
|
+
accessToken?: string;
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
/**
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@aneuhold/core-ts-api-lib",
|
|
3
3
|
"author": "Anton G. Neuhold Jr.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "3.0.
|
|
5
|
+
"version": "3.0.28",
|
|
6
6
|
"description": "A library for interacting with the backend and defining the backend API for personal projects.",
|
|
7
7
|
"packageManager": "pnpm@10.25.0",
|
|
8
8
|
"type": "module",
|
|
@@ -72,12 +72,12 @@
|
|
|
72
72
|
"TypeScript"
|
|
73
73
|
],
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@aneuhold/core-ts-db-lib": "^
|
|
76
|
-
"@aneuhold/core-ts-lib": "^2.4.
|
|
75
|
+
"@aneuhold/core-ts-db-lib": "^5.0.1",
|
|
76
|
+
"@aneuhold/core-ts-lib": "^2.4.3",
|
|
77
77
|
"bson": "^7.0.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@aneuhold/local-npm-registry": "^0.2.
|
|
80
|
+
"@aneuhold/local-npm-registry": "^0.2.30",
|
|
81
81
|
"@aneuhold/main-scripts": "^2.8.3",
|
|
82
82
|
"@types/node": "^25.0.2",
|
|
83
83
|
"eslint": "^9.39.2",
|