@qelos/integrator-nuxt 4.0.0
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 +20 -0
- package/README.md +185 -0
- package/dist/event-handler.d.ts +32 -0
- package/dist/event-handler.js +33 -0
- package/dist/event-handler.js.map +1 -0
- package/dist/middleware.d.ts +23 -0
- package/dist/middleware.js +96 -0
- package/dist/middleware.js.map +1 -0
- package/dist/module.d.ts +15 -0
- package/dist/module.js +49 -0
- package/dist/module.js.map +1 -0
- package/dist/runtime/composables/useQelos.d.ts +30 -0
- package/dist/runtime/composables/useQelos.js +38 -0
- package/dist/runtime/composables/useQelos.js.map +1 -0
- package/dist/runtime/server-handler.d.ts +2 -0
- package/dist/runtime/server-handler.js +10 -0
- package/dist/runtime/server-handler.js.map +1 -0
- package/dist/sdk-factory.d.ts +15 -0
- package/dist/sdk-factory.js +84 -0
- package/dist/sdk-factory.js.map +1 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
- package/src/event-handler.ts +66 -0
- package/src/middleware.ts +146 -0
- package/src/module.ts +84 -0
- package/src/runtime/composables/useQelos.ts +60 -0
- package/src/runtime/server-handler.ts +12 -0
- package/src/sdk-factory.ts +111 -0
- package/src/types.ts +91 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import QelosSDK from '@qelos/sdk';
|
|
2
|
+
import type { QelosSDKOptions, FetchLike } from '@qelos/sdk/types';
|
|
3
|
+
import type {
|
|
4
|
+
QelosNuxtRuntimeConfig,
|
|
5
|
+
QelosTokenPair,
|
|
6
|
+
ResolvedTokens,
|
|
7
|
+
TokenRefreshHook,
|
|
8
|
+
} from './types';
|
|
9
|
+
import type { H3Event } from 'h3';
|
|
10
|
+
|
|
11
|
+
export interface CreateSdkParams {
|
|
12
|
+
config: QelosNuxtRuntimeConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Tokens for the current request. The factory mutates this object in
|
|
15
|
+
* place when a token refresh occurs, so callers can read the latest pair
|
|
16
|
+
* after the SDK has been used.
|
|
17
|
+
*/
|
|
18
|
+
tokens: QelosTokenPair;
|
|
19
|
+
event: H3Event;
|
|
20
|
+
onTokenRefresh?: TokenRefreshHook;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const NO_AUTH_URLS = new Set([
|
|
24
|
+
'/api/token/refresh',
|
|
25
|
+
'/api/cookie/refresh',
|
|
26
|
+
'/api/signin',
|
|
27
|
+
'/api/signup',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export function createRequestSdk({ config, tokens, event, onTokenRefresh }: CreateSdkParams): QelosSDK {
|
|
31
|
+
let sdk: QelosSDK;
|
|
32
|
+
let refreshInFlight: Promise<void> | null = null;
|
|
33
|
+
|
|
34
|
+
const baseOptions: Partial<QelosSDKOptions> = config.sdkOptions || {};
|
|
35
|
+
|
|
36
|
+
async function performRefresh(): Promise<void> {
|
|
37
|
+
if (!tokens.refreshToken && !tokens.accessToken) {
|
|
38
|
+
throw new Error('no refresh token available');
|
|
39
|
+
}
|
|
40
|
+
const previous: QelosTokenPair = {
|
|
41
|
+
accessToken: tokens.accessToken,
|
|
42
|
+
refreshToken: tokens.refreshToken,
|
|
43
|
+
};
|
|
44
|
+
let refreshed: ResolvedTokens;
|
|
45
|
+
if (tokens.refreshToken) {
|
|
46
|
+
const result = await sdk.authentication.refreshToken(tokens.refreshToken);
|
|
47
|
+
refreshed = {
|
|
48
|
+
accessToken: result.payload.token,
|
|
49
|
+
refreshToken: result.payload.refreshToken,
|
|
50
|
+
};
|
|
51
|
+
} else {
|
|
52
|
+
const result = await sdk.authentication.refreshCookieToken(tokens.accessToken);
|
|
53
|
+
refreshed = {
|
|
54
|
+
accessToken: result.payload.cookieToken,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
tokens.accessToken = refreshed.accessToken;
|
|
58
|
+
tokens.refreshToken = refreshed.refreshToken;
|
|
59
|
+
if (onTokenRefresh) {
|
|
60
|
+
await onTokenRefresh({ event, oldTokens: previous, newTokens: refreshed, sdk });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ensureRefresh(): Promise<void> {
|
|
65
|
+
if (!refreshInFlight) {
|
|
66
|
+
refreshInFlight = performRefresh().finally(() => {
|
|
67
|
+
refreshInFlight = null;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return refreshInFlight;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const options: QelosSDKOptions = {
|
|
74
|
+
appUrl: config.appUrl,
|
|
75
|
+
fetch: (globalThis.fetch as unknown as FetchLike),
|
|
76
|
+
forceRefresh: !config.apiToken,
|
|
77
|
+
...baseOptions,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (config.apiToken) {
|
|
81
|
+
options.apiToken = config.apiToken;
|
|
82
|
+
} else {
|
|
83
|
+
if (tokens.accessToken) {
|
|
84
|
+
options.accessToken = tokens.accessToken;
|
|
85
|
+
}
|
|
86
|
+
if (tokens.refreshToken) {
|
|
87
|
+
options.refreshToken = tokens.refreshToken;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
options.extraHeaders = async (relativeUrl: string, forceRefresh?: boolean) => {
|
|
91
|
+
if (NO_AUTH_URLS.has(relativeUrl)) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
if (forceRefresh && tokens.refreshToken) {
|
|
95
|
+
await ensureRefresh();
|
|
96
|
+
}
|
|
97
|
+
const token = sdk?.authentication?.accessToken || tokens.accessToken;
|
|
98
|
+
if (token) {
|
|
99
|
+
return { authorization: 'Bearer ' + token };
|
|
100
|
+
}
|
|
101
|
+
return {};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
options.onFailedRefreshToken = async () => {
|
|
105
|
+
await ensureRefresh();
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
sdk = new QelosSDK(options);
|
|
110
|
+
return sdk;
|
|
111
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type QelosSDK from '@qelos/sdk';
|
|
2
|
+
import type { IUser } from '@qelos/sdk/dist/authentication';
|
|
3
|
+
import type { IWorkspace } from '@qelos/sdk/workspaces';
|
|
4
|
+
import type { QelosSDKOptions } from '@qelos/sdk/types';
|
|
5
|
+
|
|
6
|
+
export interface QelosTokenPair {
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface QelosNuxtRuntimeConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Base URL of the Qelos backend (e.g. https://yourdomain.com).
|
|
14
|
+
*/
|
|
15
|
+
appUrl: string;
|
|
16
|
+
/**
|
|
17
|
+
* Static API token used for service-to-service calls. When provided, no
|
|
18
|
+
* cookie/refresh-token handling is performed.
|
|
19
|
+
*/
|
|
20
|
+
apiToken?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Cookie name carrying the Qelos access token. Defaults to `q_access_token`.
|
|
23
|
+
*/
|
|
24
|
+
accessTokenCookie?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Cookie name carrying the Qelos refresh token. Defaults to `q_refresh_token`.
|
|
27
|
+
*/
|
|
28
|
+
refreshTokenCookie?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Path to mount the server middleware on. Defaults to `''` (every request).
|
|
31
|
+
*/
|
|
32
|
+
middlewarePath?: string;
|
|
33
|
+
/**
|
|
34
|
+
* If true, the request is rejected (401) when the user cannot be resolved.
|
|
35
|
+
* Defaults to `false` — anonymous requests pass through with `event.context.qelos.user = null`.
|
|
36
|
+
*/
|
|
37
|
+
requireAuth?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Skip the middleware entirely for requests matching any of these path
|
|
40
|
+
* prefixes. Useful for `/api/_auth`, `/health`, etc.
|
|
41
|
+
*/
|
|
42
|
+
skipPaths?: string[];
|
|
43
|
+
/**
|
|
44
|
+
* Optional extra options merged into the per-request SDK instance.
|
|
45
|
+
*/
|
|
46
|
+
sdkOptions?: Partial<QelosSDKOptions>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ResolvedTokens {
|
|
50
|
+
accessToken: string;
|
|
51
|
+
refreshToken?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TokenRefreshContext {
|
|
55
|
+
event: import('h3').H3Event;
|
|
56
|
+
oldTokens: QelosTokenPair;
|
|
57
|
+
newTokens: ResolvedTokens;
|
|
58
|
+
sdk: QelosSDK;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type TokenRefreshHook = (ctx: TokenRefreshContext) => void | Promise<void>;
|
|
62
|
+
|
|
63
|
+
export interface QelosRequestContext {
|
|
64
|
+
/**
|
|
65
|
+
* The authenticated user, or `null` when anonymous.
|
|
66
|
+
*/
|
|
67
|
+
user: IUser | null;
|
|
68
|
+
/**
|
|
69
|
+
* The active workspace for the request, or `null` when none is active /
|
|
70
|
+
* the user is anonymous.
|
|
71
|
+
*/
|
|
72
|
+
workspace: IWorkspace | null;
|
|
73
|
+
/**
|
|
74
|
+
* The full list of workspaces the user has access to.
|
|
75
|
+
*/
|
|
76
|
+
workspaces: IWorkspace[];
|
|
77
|
+
/**
|
|
78
|
+
* SDK instance bound to the current request's tokens.
|
|
79
|
+
*/
|
|
80
|
+
sdk: QelosSDK;
|
|
81
|
+
/**
|
|
82
|
+
* Tokens read from the request. Mutated when a refresh occurs.
|
|
83
|
+
*/
|
|
84
|
+
tokens: QelosTokenPair;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare module 'h3' {
|
|
88
|
+
interface H3EventContext {
|
|
89
|
+
qelos?: QelosRequestContext;
|
|
90
|
+
}
|
|
91
|
+
}
|