@croct/plug-nuxt 0.1.1 → 0.2.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/dist/module.json +1 -1
- package/dist/module.mjs +8 -0
- package/dist/runtime/components/Personalization.js +2 -2
- package/dist/runtime/components/Slot.js +2 -2
- package/dist/runtime/composables/useContent.js +2 -2
- package/dist/runtime/composables/useEvaluation.js +2 -2
- package/dist/runtime/plugin.client.js +4 -2
- package/dist/runtime/plugin.server.d.ts +2 -0
- package/dist/runtime/plugin.server.js +6 -0
- package/dist/runtime/server/composables/anonymize.js +2 -1
- package/dist/runtime/server/composables/evaluate.js +2 -2
- package/dist/runtime/server/composables/fetchContent.js +2 -2
- package/dist/runtime/server/composables/identify.js +2 -1
- package/dist/runtime/server/croct-resolvers.d.ts +7 -0
- package/dist/runtime/server/middleware/croct.js +26 -7
- package/dist/runtime/server/utils/security.d.ts +5 -4
- package/dist/runtime/server/utils/security.js +17 -14
- package/dist/runtime/types.d.ts +1 -1
- package/package.json +3 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -68,6 +68,7 @@ const module = defineNuxtModule({
|
|
|
68
68
|
getContents: () => clientOptionsCode
|
|
69
69
|
});
|
|
70
70
|
nuxt.options.alias["#croct/client-options"] = join(nuxt.options.buildDir, "croct/client-options");
|
|
71
|
+
addPlugin(resolver.resolve("./runtime/plugin.server"));
|
|
71
72
|
addPlugin(resolver.resolve("./runtime/plugin.client"));
|
|
72
73
|
addServerHandler({
|
|
73
74
|
handler: resolver.resolve("./runtime/server/middleware/croct"),
|
|
@@ -113,6 +114,13 @@ function generateResolversModule(options, appResolver) {
|
|
|
113
114
|
} else {
|
|
114
115
|
lines.push("export const userIdResolver = undefined;");
|
|
115
116
|
}
|
|
117
|
+
if (options.credentialsResolver !== void 0) {
|
|
118
|
+
lines.push(
|
|
119
|
+
`export { default as credentialsResolver } from '${appResolver.resolve(options.credentialsResolver)}';`
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
lines.push("export const credentialsResolver = undefined;");
|
|
123
|
+
}
|
|
116
124
|
return lines.join("\n");
|
|
117
125
|
}
|
|
118
126
|
function generateClientOptionsModule(options, appResolver) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent } from "vue";
|
|
2
|
-
import { useAsyncData } from "#app";
|
|
2
|
+
import { useAsyncData, useRequestFetch } from "#app";
|
|
3
3
|
export default defineComponent({
|
|
4
4
|
name: "Personalization",
|
|
5
5
|
props: {
|
|
@@ -28,7 +28,7 @@ export default defineComponent({
|
|
|
28
28
|
};
|
|
29
29
|
const { data, pending, error } = await useAsyncData(
|
|
30
30
|
`croct:pers:${props.query}:${JSON.stringify(options)}`,
|
|
31
|
-
() =>
|
|
31
|
+
() => useRequestFetch()("/api/_croct/evaluate", {
|
|
32
32
|
method: "POST",
|
|
33
33
|
body: { query: props.query, ...options }
|
|
34
34
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent } from "vue";
|
|
2
|
-
import { useAsyncData } from "#app";
|
|
2
|
+
import { useAsyncData, useRequestFetch } from "#app";
|
|
3
3
|
import { resolveLocale } from "../utils/locale.js";
|
|
4
4
|
export default defineComponent({
|
|
5
5
|
name: "Slot",
|
|
@@ -30,7 +30,7 @@ export default defineComponent({
|
|
|
30
30
|
};
|
|
31
31
|
const { data, error } = await useAsyncData(
|
|
32
32
|
`croct:slot:${props.id}:${JSON.stringify(options)}`,
|
|
33
|
-
() =>
|
|
33
|
+
() => useRequestFetch()("/api/_croct/content", {
|
|
34
34
|
method: "POST",
|
|
35
35
|
body: { slotId: props.id, ...options }
|
|
36
36
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useAsyncData } from "#app";
|
|
1
|
+
import { useAsyncData, useRequestFetch } from "#app";
|
|
2
2
|
import { resolveLocale } from "../utils/locale.js";
|
|
3
3
|
function useContentNuxt(slotId, options = {}) {
|
|
4
4
|
const { preferredLocale, ...rest } = options;
|
|
@@ -10,7 +10,7 @@ function useContentNuxt(slotId, options = {}) {
|
|
|
10
10
|
const cacheKey = `croct:content:${slotId}:${JSON.stringify(resolvedOptions)}`;
|
|
11
11
|
return useAsyncData(
|
|
12
12
|
cacheKey,
|
|
13
|
-
() =>
|
|
13
|
+
() => useRequestFetch()("/api/_croct/content", {
|
|
14
14
|
method: "POST",
|
|
15
15
|
body: { slotId, ...resolvedOptions }
|
|
16
16
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useAsyncData } from "#app";
|
|
1
|
+
import { useAsyncData, useRequestFetch } from "#app";
|
|
2
2
|
function useEvaluationNuxt(query, options = {}) {
|
|
3
3
|
const cacheKey = `croct:eval:${query}:${JSON.stringify(options)}`;
|
|
4
4
|
return useAsyncData(
|
|
5
5
|
cacheKey,
|
|
6
|
-
() =>
|
|
6
|
+
() => useRequestFetch()("/api/_croct/evaluate", {
|
|
7
7
|
method: "POST",
|
|
8
8
|
body: { query, ...options }
|
|
9
9
|
})
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createCroct } from "@croct/plug-vue";
|
|
2
|
-
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import { defineNuxtPlugin, useRuntimeConfig, useState } from "#app";
|
|
3
3
|
import { urlSanitizer } from "#croct/client-options";
|
|
4
4
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
5
5
|
const config = useRuntimeConfig().public.croct;
|
|
6
|
+
const resolvedAppId = useState("croct:app-id").value;
|
|
7
|
+
const appId = resolvedAppId !== void 0 && resolvedAppId !== "" ? resolvedAppId : config.appId;
|
|
6
8
|
const plugin = createCroct({
|
|
7
|
-
appId
|
|
9
|
+
appId,
|
|
8
10
|
disableCidMirroring: true,
|
|
9
11
|
...config.debug === true ? { debug: true } : {},
|
|
10
12
|
...config.test === true ? { test: true } : {},
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig, useRequestEvent, useState } from "#app";
|
|
2
|
+
export default defineNuxtPlugin(() => {
|
|
3
|
+
const event = useRequestEvent();
|
|
4
|
+
const fallback = useRuntimeConfig().public.croct.appId;
|
|
5
|
+
useState("croct:app-id", () => event?.context.croctCredentials?.appId ?? fallback);
|
|
6
|
+
});
|
|
@@ -2,5 +2,6 @@ import { useEvent } from "#imports";
|
|
|
2
2
|
import { issueToken } from "../utils/security.js";
|
|
3
3
|
import { setUserTokenCookie } from "../utils/cookie.js";
|
|
4
4
|
export async function anonymize() {
|
|
5
|
-
|
|
5
|
+
const event = useEvent();
|
|
6
|
+
setUserTokenCookie(event, await issueToken(null, void 0, event));
|
|
6
7
|
}
|
|
@@ -8,13 +8,13 @@ export async function evaluate(query, options = {}) {
|
|
|
8
8
|
const context = event.context.croct;
|
|
9
9
|
if (context === void 0) {
|
|
10
10
|
throw new Error(
|
|
11
|
-
"Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-
|
|
11
|
+
"Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-module"
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
const config = useRuntimeConfig();
|
|
15
15
|
const timeout = config.public.croct.defaultFetchTimeout;
|
|
16
16
|
return executeQuery(query, {
|
|
17
|
-
apiKey: getApiKey(),
|
|
17
|
+
apiKey: getApiKey(event),
|
|
18
18
|
clientIp: context.clientIp ?? "127.0.0.1",
|
|
19
19
|
...context.previewToken !== void 0 ? { previewToken: context.previewToken } : {},
|
|
20
20
|
...context.userToken !== void 0 ? { userToken: context.userToken } : {},
|
|
@@ -8,14 +8,14 @@ export async function fetchContent(slotId, options = {}) {
|
|
|
8
8
|
const context = event.context.croct;
|
|
9
9
|
if (context === void 0) {
|
|
10
10
|
throw new Error(
|
|
11
|
-
"Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-
|
|
11
|
+
"Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-module"
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
const config = useRuntimeConfig();
|
|
15
15
|
const timeout = config.public.croct.defaultFetchTimeout;
|
|
16
16
|
const { logger, ...rest } = options;
|
|
17
17
|
const commonOptions = {
|
|
18
|
-
apiKey: getApiKey(),
|
|
18
|
+
apiKey: getApiKey(event),
|
|
19
19
|
...typeof config.public.croct.baseEndpointUrl === "string" && config.public.croct.baseEndpointUrl !== "" ? { baseEndpointUrl: config.public.croct.baseEndpointUrl } : {},
|
|
20
20
|
...typeof timeout === "number" && timeout > 0 ? { timeout } : {},
|
|
21
21
|
logger: logger ?? FilteredLogger.include(new ConsoleLogger(), ["warn", "error"])
|
|
@@ -2,5 +2,6 @@ import { useEvent } from "#imports";
|
|
|
2
2
|
import { issueToken } from "../utils/security.js";
|
|
3
3
|
import { setUserTokenCookie } from "../utils/cookie.js";
|
|
4
4
|
export async function identify(userId) {
|
|
5
|
-
|
|
5
|
+
const event = useEvent();
|
|
6
|
+
setUserTokenCookie(event, await issueToken(userId, void 0, event));
|
|
6
7
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type {H3Event} from 'h3';
|
|
2
2
|
|
|
3
|
+
export type CroctCredentials = {
|
|
4
|
+
appId: string,
|
|
5
|
+
apiKey: string,
|
|
6
|
+
};
|
|
7
|
+
|
|
3
8
|
export type UserIdResolver = (event: H3Event) => Promise<string | null> | string | null;
|
|
4
9
|
export type LocaleResolver = (event: H3Event) => Promise<string | null> | string | null;
|
|
10
|
+
export type CredentialsResolver = (event: H3Event) =>
|
|
11
|
+
Promise<CroctCredentials | null> | CroctCredentials | null;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { Token } from "@croct/sdk/token";
|
|
11
11
|
import { base64UrlDecode } from "@croct/sdk/base64Url";
|
|
12
12
|
import { useRuntimeConfig } from "#imports";
|
|
13
|
-
import { localeResolver, userIdResolver } from "#croct/resolvers";
|
|
13
|
+
import { credentialsResolver, localeResolver, userIdResolver } from "#croct/resolvers";
|
|
14
14
|
import { setUserTokenCookie, getProductionDefaults } from "../utils/cookie.js";
|
|
15
15
|
import { getAuthenticationKey, isUserTokenAuthenticationEnabled, issueToken } from "../utils/security.js";
|
|
16
16
|
const CLIENT_ID_PATTERN = /^(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|[a-f0-9]{32})$/;
|
|
@@ -30,6 +30,7 @@ export default defineEventHandler(async (event) => {
|
|
|
30
30
|
}
|
|
31
31
|
const config = useRuntimeConfig();
|
|
32
32
|
const { cookie } = config.public.croct;
|
|
33
|
+
event.context.croctCredentials = await resolveCredentials(event);
|
|
33
34
|
const clientId = resolveClientId(getCookie(event, cookie.clientId.name));
|
|
34
35
|
const userToken = await resolveUserToken(event, getCookie(event, cookie.userToken.name));
|
|
35
36
|
const preferredLocale = await resolvePreferredLocale(event, config.public.croct.defaultPreferredLocale);
|
|
@@ -75,8 +76,26 @@ function resolveClientId(cookieValue) {
|
|
|
75
76
|
}
|
|
76
77
|
return crypto.randomUUID();
|
|
77
78
|
}
|
|
79
|
+
async function resolveCredentials(event) {
|
|
80
|
+
const config = useRuntimeConfig();
|
|
81
|
+
const fallback = {
|
|
82
|
+
appId: config.public.croct.appId,
|
|
83
|
+
apiKey: config.croct.apiKey
|
|
84
|
+
};
|
|
85
|
+
if (credentialsResolver === void 0) {
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
const credentials = await credentialsResolver(event);
|
|
89
|
+
if (credentials === null || credentials === void 0) {
|
|
90
|
+
return fallback;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
appId: credentials.appId ?? fallback.appId,
|
|
94
|
+
apiKey: credentials.apiKey ?? fallback.apiKey
|
|
95
|
+
};
|
|
96
|
+
}
|
|
78
97
|
async function resolveUserToken(event, cookieValue) {
|
|
79
|
-
const { appId } =
|
|
98
|
+
const { appId } = event.context.croctCredentials;
|
|
80
99
|
let token = null;
|
|
81
100
|
if (cookieValue !== void 0) {
|
|
82
101
|
try {
|
|
@@ -85,15 +104,15 @@ async function resolveUserToken(event, cookieValue) {
|
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
106
|
const userId = userIdResolver !== void 0 ? await userIdResolver(event) : void 0;
|
|
88
|
-
if (token === null || isUserTokenAuthenticationEnabled() && !token.isSigned() || !token.isValidNow() || userId !== void 0 && (userId === null ? !token.isAnonymous() : !token.isSubject(userId))) {
|
|
89
|
-
return issueToken(userId ?? null);
|
|
107
|
+
if (token === null || isUserTokenAuthenticationEnabled(event) && !token.isSigned() || !token.isValidNow() || userId !== void 0 && (userId === null ? !token.isAnonymous() : !token.isSubject(userId))) {
|
|
108
|
+
return issueToken(userId ?? null, void 0, event);
|
|
90
109
|
}
|
|
91
110
|
const tokenAppId = token.getApplicationId();
|
|
92
111
|
if (tokenAppId !== null && tokenAppId !== appId) {
|
|
93
|
-
return issueToken(userId ?? null);
|
|
112
|
+
return issueToken(userId ?? null, void 0, event);
|
|
94
113
|
}
|
|
95
|
-
if (token.isSigned() && !await token.matchesKeyId(getAuthenticationKey())) {
|
|
96
|
-
return issueToken(token.getSubject(), token.getTokenId() ?? void 0);
|
|
114
|
+
if (token.isSigned() && !await token.matchesKeyId(getAuthenticationKey(event))) {
|
|
115
|
+
return issueToken(token.getSubject(), token.getTokenId() ?? void 0, event);
|
|
97
116
|
}
|
|
98
117
|
return token;
|
|
99
118
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
1
2
|
import { ApiKey } from '@croct/sdk/apiKey';
|
|
2
3
|
import { Token } from '@croct/sdk/token';
|
|
3
|
-
export declare function getApiKey(): ApiKey;
|
|
4
|
-
export declare function getAuthenticationKey(): ApiKey;
|
|
5
|
-
export declare function isUserTokenAuthenticationEnabled(): boolean;
|
|
6
|
-
export declare function issueToken(userId?: string | null, tokenId?: string): Promise<Token>;
|
|
4
|
+
export declare function getApiKey(event?: H3Event): ApiKey;
|
|
5
|
+
export declare function getAuthenticationKey(event?: H3Event): ApiKey;
|
|
6
|
+
export declare function isUserTokenAuthenticationEnabled(event?: H3Event): boolean;
|
|
7
|
+
export declare function issueToken(userId?: string | null, tokenId?: string, event?: H3Event): Promise<Token>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ApiKey } from "@croct/sdk/apiKey";
|
|
2
2
|
import { Token } from "@croct/sdk/token";
|
|
3
3
|
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
export function getApiKey() {
|
|
5
|
-
const
|
|
4
|
+
export function getApiKey(event) {
|
|
5
|
+
const apiKey = resolveApiKey(event);
|
|
6
6
|
if (apiKey === "") {
|
|
7
7
|
throw new Error(
|
|
8
|
-
"Croct's API key is missing. Did you forget to set the NUXT_CROCT_API_KEY environment variable? For help, see: https://croct.help/sdk/nuxt/missing-
|
|
8
|
+
"Croct's API key is missing. Did you forget to set the NUXT_CROCT_API_KEY environment variable? For help, see: https://croct.help/sdk/nuxt/missing-api-key"
|
|
9
9
|
);
|
|
10
10
|
}
|
|
11
11
|
try {
|
|
@@ -16,8 +16,8 @@ export function getApiKey() {
|
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
export function getAuthenticationKey() {
|
|
20
|
-
const apiKey = getApiKey();
|
|
19
|
+
export function getAuthenticationKey(event) {
|
|
20
|
+
const apiKey = getApiKey(event);
|
|
21
21
|
if (!apiKey.hasPrivateKey()) {
|
|
22
22
|
throw new Error(
|
|
23
23
|
"Croct's API key does not have a private key. Please generate an API key with authenticate permissions and update the NUXT_CROCT_API_KEY environment variable."
|
|
@@ -25,16 +25,19 @@ export function getAuthenticationKey() {
|
|
|
25
25
|
}
|
|
26
26
|
return apiKey;
|
|
27
27
|
}
|
|
28
|
-
export function isUserTokenAuthenticationEnabled() {
|
|
29
|
-
|
|
30
|
-
return config.apiKey !== "" && config.disableUserTokenAuthentication !== true;
|
|
28
|
+
export function isUserTokenAuthenticationEnabled(event) {
|
|
29
|
+
return resolveApiKey(event) !== "" && useRuntimeConfig().croct.disableUserTokenAuthentication !== true;
|
|
31
30
|
}
|
|
32
|
-
export function issueToken(userId = null, tokenId) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (isUserTokenAuthenticationEnabled()) {
|
|
37
|
-
return token.withTokenId(tokenId ?? crypto.randomUUID()).signedWith(getAuthenticationKey());
|
|
31
|
+
export function issueToken(userId = null, tokenId, event) {
|
|
32
|
+
const token = Token.issue(resolveAppId(event), userId).withDuration(useRuntimeConfig().croct.tokenDuration);
|
|
33
|
+
if (isUserTokenAuthenticationEnabled(event)) {
|
|
34
|
+
return token.withTokenId(tokenId ?? crypto.randomUUID()).signedWith(getAuthenticationKey(event));
|
|
38
35
|
}
|
|
39
36
|
return Promise.resolve(token);
|
|
40
37
|
}
|
|
38
|
+
function resolveAppId(event) {
|
|
39
|
+
return event?.context.croctCredentials?.appId ?? useRuntimeConfig().public.croct.appId;
|
|
40
|
+
}
|
|
41
|
+
function resolveApiKey(event) {
|
|
42
|
+
return event?.context.croctCredentials?.apiKey ?? useRuntimeConfig().croct.apiKey;
|
|
43
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export type * from '@croct/plug/sdk/json';
|
|
|
2
2
|
export type * from '@croct/plug/slot';
|
|
3
3
|
export type * from '@croct/plug/component';
|
|
4
4
|
export type { UrlSanitizer } from '@croct/sdk/tab';
|
|
5
|
-
export type { UserIdResolver, LocaleResolver } from './server/croct-resolvers.js';
|
|
5
|
+
export type { UserIdResolver, LocaleResolver, CredentialsResolver, CroctCredentials, } from './server/croct-resolvers.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@croct/plug-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Nuxt module to plug your Nuxt 3 applications into Croct.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Croct",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"lint": "eslint .",
|
|
49
49
|
"test": "vitest run",
|
|
50
50
|
"test:watch": "vitest watch",
|
|
51
|
+
"test:e2e": "playwright test",
|
|
52
|
+
"test:e2e:prerender": "playwright test -c playwright.prerender.config.ts",
|
|
51
53
|
"validate": "tsc --noEmit"
|
|
52
54
|
},
|
|
53
55
|
"peerDependencies": {
|