@meistrari/chat-nuxt 3.12.1 → 3.13.0-rc.1
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.d.mts +14 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +52 -7
- package/dist/runtime/auth/define.d.ts +37 -0
- package/dist/runtime/auth/define.js +3 -0
- package/dist/runtime/citations/components/chat-citation.vue +126 -76
- package/dist/runtime/embed/components/chat-embed-inner.vue +1 -0
- package/dist/runtime/server/utils/auth-token.js +25 -0
- package/dist/runtime/types/chat-auth.d.ts +2 -0
- package/package.json +5 -1
package/dist/module.d.mts
CHANGED
|
@@ -13,6 +13,20 @@ interface ChatNuxtModuleOptions {
|
|
|
13
13
|
* required runtime config keys are unset.
|
|
14
14
|
*/
|
|
15
15
|
validateConfig?: ChatNuxtConfigValidationMode;
|
|
16
|
+
/**
|
|
17
|
+
* Path, relative to `srcDir`, of a module default-exporting the host app's
|
|
18
|
+
* auth bridge built with `defineChatAuth()` (from `@meistrari/chat-nuxt/auth`).
|
|
19
|
+
* Setting it switches `#chat-auth` to the host implementation and lifts the
|
|
20
|
+
* `@meistrari/auth-nuxt` module requirement.
|
|
21
|
+
*/
|
|
22
|
+
authComposable?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Name of the cookie holding the bearer token the server proxy routes
|
|
25
|
+
* forward to the chat-api. Only read when `authComposable` is set; without
|
|
26
|
+
* it the proxies keep relying on `event.context.auth` populated by
|
|
27
|
+
* `@meistrari/auth-nuxt`.
|
|
28
|
+
*/
|
|
29
|
+
authTokenCookie?: string;
|
|
16
30
|
}
|
|
17
31
|
declare const _default: any;
|
|
18
32
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, mkdirSync, symlinkSync } from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { join, dirname } from 'node:path';
|
|
3
|
+
import { join, dirname, isAbsolute } from 'node:path';
|
|
4
4
|
import { defineNuxtModule, createResolver, addTemplate, addTypeTemplate, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
|
|
5
5
|
import { TOOL_ICON_SAFELIST } from '../dist/runtime/messages/tool-registry.js';
|
|
6
6
|
export { ChatMessageStatus } from '../dist/runtime/messages/composables/chat-message-terminal.js';
|
|
@@ -193,7 +193,33 @@ function resolveChatAuthMode(runtimeConfig) {
|
|
|
193
193
|
const telaAuth = publicConfig?.telaAuth;
|
|
194
194
|
return telaAuth?.application?.enabled ? "application" : "first-party";
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
const HOST_AUTH_COMPOSABLE_EXTENSIONS = ["", ".ts", ".mts", ".js", ".mjs"];
|
|
197
|
+
function resolveHostAuthComposable(srcDir, authComposable) {
|
|
198
|
+
const base = isAbsolute(authComposable) ? authComposable : join(srcDir, authComposable);
|
|
199
|
+
for (const extension of HOST_AUTH_COMPOSABLE_EXTENSIONS) {
|
|
200
|
+
const candidate = `${base}${extension}`;
|
|
201
|
+
if (existsSync(candidate)) {
|
|
202
|
+
return candidate;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw new Error(
|
|
206
|
+
`[@meistrari/chat-nuxt] authComposable not found: ${base}
|
|
207
|
+
Create it with:
|
|
208
|
+
|
|
209
|
+
import { defineChatAuth } from '@meistrari/chat-nuxt/auth'
|
|
210
|
+
|
|
211
|
+
export default defineChatAuth(() => ({
|
|
212
|
+
user, activeOrganization, logout, getToken, getAvailableOrganizations, switchOrganization,
|
|
213
|
+
}))
|
|
214
|
+
|
|
215
|
+
See docs/guides/host-auth.md in @meistrari/chat-nuxt.`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
function buildChatAuthTemplate(mode, hostAuthComposablePath) {
|
|
219
|
+
if (mode === "host") {
|
|
220
|
+
return `export { default as useChatAuth } from ${JSON.stringify((hostAuthComposablePath ?? "").replace(/\\/g, "/"))}
|
|
221
|
+
`;
|
|
222
|
+
}
|
|
197
223
|
if (mode === "application") {
|
|
198
224
|
return `
|
|
199
225
|
import { useTelaApplicationAuth } from '#imports'
|
|
@@ -333,10 +359,11 @@ const module$1 = defineNuxtModule({
|
|
|
333
359
|
validateConfig: false
|
|
334
360
|
},
|
|
335
361
|
setup(options, nuxt) {
|
|
362
|
+
const hostAuthComposablePath = options.authComposable ? resolveHostAuthComposable(nuxt.options.srcDir, options.authComposable) : void 0;
|
|
336
363
|
const hasAuthModule = (nuxt.options.modules ?? []).some(isAuthModuleEntry);
|
|
337
|
-
if (!hasAuthModule) {
|
|
364
|
+
if (!hasAuthModule && !hostAuthComposablePath) {
|
|
338
365
|
throw new Error(
|
|
339
|
-
|
|
366
|
+
`[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so chat auth composables and event.context.auth are available, or set chatNuxt.authComposable to provide the host app's own auth bridge.`
|
|
340
367
|
);
|
|
341
368
|
}
|
|
342
369
|
ensureIconifyPackages(nuxt.options.rootDir);
|
|
@@ -356,10 +383,14 @@ const module$1 = defineNuxtModule({
|
|
|
356
383
|
);
|
|
357
384
|
const telaBuildPath = resolveTelaBuildLayer();
|
|
358
385
|
const runtimeAliases = resolveRuntimeAliases(telaBuildPath);
|
|
359
|
-
const chatAuthMode = resolveChatAuthMode(nuxt.options.runtimeConfig);
|
|
386
|
+
const chatAuthMode = hostAuthComposablePath ? "host" : resolveChatAuthMode(nuxt.options.runtimeConfig);
|
|
387
|
+
console.info(
|
|
388
|
+
{ mode: chatAuthMode, ...options.authComposable ? { authComposable: options.authComposable } : {} },
|
|
389
|
+
"[@meistrari/chat-nuxt] auth bridge resolved"
|
|
390
|
+
);
|
|
360
391
|
const chatAuthTemplate = addTemplate({
|
|
361
392
|
filename: "chat-nuxt/auth.mjs",
|
|
362
|
-
getContents: () => buildChatAuthTemplate(chatAuthMode)
|
|
393
|
+
getContents: () => buildChatAuthTemplate(chatAuthMode, hostAuthComposablePath)
|
|
363
394
|
});
|
|
364
395
|
const chatAuthTypeTemplate = addTypeTemplate({
|
|
365
396
|
filename: "chat-nuxt/chat-auth.d.ts",
|
|
@@ -412,8 +443,14 @@ const module$1 = defineNuxtModule({
|
|
|
412
443
|
rc.chatNuxt = {
|
|
413
444
|
chatApiUrl: existingChatNuxtRuntimeConfig?.chatApiUrl || rc.chatApiUrl,
|
|
414
445
|
source: options.source || existingChatNuxtRuntimeConfig?.source,
|
|
415
|
-
clientVersion: _chatNuxtPackageVersion
|
|
446
|
+
clientVersion: _chatNuxtPackageVersion,
|
|
447
|
+
authTokenCookie: hostAuthComposablePath ? options.authTokenCookie || existingChatNuxtRuntimeConfig?.authTokenCookie : void 0
|
|
416
448
|
};
|
|
449
|
+
if (hostAuthComposablePath && !rc.chatNuxt.authTokenCookie) {
|
|
450
|
+
console.warn(
|
|
451
|
+
"[@meistrari/chat-nuxt] authComposable is set but authTokenCookie is not. Server proxy routes will only authenticate if the host populates event.context.auth.token through its own server middleware."
|
|
452
|
+
);
|
|
453
|
+
}
|
|
417
454
|
validateRuntimeConfig(rc, options.validateConfig ?? false);
|
|
418
455
|
nuxt.options.css = nuxt.options.css || [];
|
|
419
456
|
for (const css of [
|
|
@@ -427,6 +464,14 @@ const module$1 = defineNuxtModule({
|
|
|
427
464
|
}
|
|
428
465
|
addPlugin(join(runtimeDir, "plugins/markstream"));
|
|
429
466
|
addImportsDir(FEATURE_DIRS.map((feature) => join(runtimeDir, feature, "composables")));
|
|
467
|
+
const importsExtendNuxt = nuxt;
|
|
468
|
+
importsExtendNuxt.hook("imports:extend", (imports) => {
|
|
469
|
+
for (const entry of imports) {
|
|
470
|
+
if (entry.from?.startsWith(runtimeDir)) {
|
|
471
|
+
entry.priority ||= -100;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
});
|
|
430
475
|
addComponent({
|
|
431
476
|
name: "MeistrariChatEmbed",
|
|
432
477
|
filePath: join(runtimeDir, "embed/components/meistrari-chat-embed.vue"),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export type ChatAuthUser = {
|
|
3
|
+
id: string;
|
|
4
|
+
email?: string | null;
|
|
5
|
+
name?: string | null;
|
|
6
|
+
image?: string | null;
|
|
7
|
+
};
|
|
8
|
+
export type ChatAuthMemberUser = {
|
|
9
|
+
name: string;
|
|
10
|
+
email: string;
|
|
11
|
+
image?: string;
|
|
12
|
+
};
|
|
13
|
+
export type ChatAuthMember = {
|
|
14
|
+
id: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
user: ChatAuthMemberUser;
|
|
17
|
+
};
|
|
18
|
+
export type ChatAuthOrganization = {
|
|
19
|
+
id: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
logo?: string | null;
|
|
22
|
+
members?: ChatAuthMember[];
|
|
23
|
+
};
|
|
24
|
+
export type ChatAuthBridge = {
|
|
25
|
+
user: Ref<ChatAuthUser | null>;
|
|
26
|
+
activeOrganization: Ref<ChatAuthOrganization | null>;
|
|
27
|
+
logout: () => Promise<void>;
|
|
28
|
+
getToken: () => Promise<string | null | undefined>;
|
|
29
|
+
getAvailableOrganizations: () => Promise<ChatAuthOrganization[]>;
|
|
30
|
+
switchOrganization: (organizationId: string) => Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Identity helper that types a host app's auth bridge against the `#chat-auth`
|
|
34
|
+
* contract at the definition site. Default-export the result from the file
|
|
35
|
+
* referenced by the `chatNuxt.authComposable` module option.
|
|
36
|
+
*/
|
|
37
|
+
export declare function defineChatAuth(bridge: () => ChatAuthBridge): () => ChatAuthBridge;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { onClickOutside } from "@vueuse/core";
|
|
2
|
+
import { onClickOutside, useElementBounding, useWindowSize } from "@vueuse/core";
|
|
3
3
|
import { useEmbedConfig } from "../../embed/composables/embed-config";
|
|
4
4
|
import { useCitationPanel } from "../composables/citation-panel";
|
|
5
5
|
import { useCitations } from "../composables/citations";
|
|
@@ -20,9 +20,55 @@ const status = computed(() => entry.value?.status.value ?? "loading");
|
|
|
20
20
|
const meta = computed(() => entry.value?.meta.value ?? null);
|
|
21
21
|
const displayLabel = computed(() => props.label?.trim() || meta.value?.identifier || meta.value?.title || "Documento");
|
|
22
22
|
const rootEl = ref(null);
|
|
23
|
+
const popoverEl = ref(null);
|
|
23
24
|
const popoverOpen = ref(false);
|
|
24
25
|
onClickOutside(rootEl, () => {
|
|
25
26
|
popoverOpen.value = false;
|
|
27
|
+
}, {
|
|
28
|
+
ignore: [popoverEl]
|
|
29
|
+
});
|
|
30
|
+
const POPOVER_WIDTH = 300;
|
|
31
|
+
const POPOVER_GAP = 6;
|
|
32
|
+
const VIEWPORT_PADDING = 8;
|
|
33
|
+
const triggerBounds = useElementBounding(rootEl);
|
|
34
|
+
const popoverBounds = useElementBounding(popoverEl);
|
|
35
|
+
const viewport = useWindowSize();
|
|
36
|
+
const popoverStyle = computed(() => {
|
|
37
|
+
const maxHeight = Math.max(
|
|
38
|
+
0,
|
|
39
|
+
viewport.height.value - VIEWPORT_PADDING * 2
|
|
40
|
+
);
|
|
41
|
+
const maxLeft = Math.max(
|
|
42
|
+
VIEWPORT_PADDING,
|
|
43
|
+
viewport.width.value - POPOVER_WIDTH - VIEWPORT_PADDING
|
|
44
|
+
);
|
|
45
|
+
const left = Math.min(
|
|
46
|
+
Math.max(triggerBounds.left.value, VIEWPORT_PADDING),
|
|
47
|
+
maxLeft
|
|
48
|
+
);
|
|
49
|
+
const fitsAbove = triggerBounds.top.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
|
|
50
|
+
const fitsBelow = viewport.height.value - triggerBounds.bottom.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
|
|
51
|
+
const constrainedStyle = {
|
|
52
|
+
left: `${left}px`,
|
|
53
|
+
maxHeight: `${maxHeight}px`,
|
|
54
|
+
overflowY: "auto"
|
|
55
|
+
};
|
|
56
|
+
if (fitsAbove) {
|
|
57
|
+
return {
|
|
58
|
+
...constrainedStyle,
|
|
59
|
+
bottom: `${viewport.height.value - triggerBounds.top.value + POPOVER_GAP}px`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (fitsBelow) {
|
|
63
|
+
return {
|
|
64
|
+
...constrainedStyle,
|
|
65
|
+
top: `${triggerBounds.bottom.value + POPOVER_GAP}px`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
...constrainedStyle,
|
|
70
|
+
top: `${VIEWPORT_PADDING}px`
|
|
71
|
+
};
|
|
26
72
|
});
|
|
27
73
|
function togglePopover() {
|
|
28
74
|
if (status.value === "loading")
|
|
@@ -99,88 +145,92 @@ const badgeClasses = computed(() => BADGE_TONE_CLASSES[meta.value?.badge?.tone ?
|
|
|
99
145
|
@click="togglePopover"
|
|
100
146
|
><span :class="chipIcon" class="citation-ref-icon" />{{ displayLabel }}</button>
|
|
101
147
|
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@click="retry"
|
|
124
|
-
>
|
|
125
|
-
Tentar novamente
|
|
126
|
-
</button>
|
|
127
|
-
</template>
|
|
128
|
-
|
|
129
|
-
<template v-else-if="meta">
|
|
130
|
-
<div flex="~ col" gap-2px>
|
|
131
|
-
<p text-13px leading-18px font-580 text-neutral-900>
|
|
132
|
-
{{ meta.title }}
|
|
133
|
-
</p>
|
|
134
|
-
<p v-if="meta.sourceLabel || meta.identifier" text-12px leading-16px text-neutral-600>
|
|
135
|
-
{{ [meta.sourceLabel, meta.identifier].filter(Boolean).join(" \xB7 ") }}
|
|
136
|
-
</p>
|
|
137
|
-
<p v-if="formattedDate" text-12px leading-16px text-neutral-500>
|
|
138
|
-
{{ meta.dateLabel ? `${meta.dateLabel}: ` : "" }}{{ formattedDate }}
|
|
139
|
-
</p>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<span
|
|
143
|
-
v-if="meta.badge"
|
|
144
|
-
self-start px-6px py-1px rounded-4px text-11px leading-15px font-580
|
|
145
|
-
:class="badgeClasses"
|
|
146
|
-
>
|
|
147
|
-
{{ meta.badge.label }}
|
|
148
|
-
</span>
|
|
149
|
-
|
|
150
|
-
<p v-if="meta.snippet" text-12px leading-17px text-neutral-600 line-clamp-3>
|
|
151
|
-
{{ meta.snippet }}
|
|
152
|
-
</p>
|
|
153
|
-
|
|
154
|
-
<div v-if="canOpenDocument || safeSourceUrl" flex items-center gap-12px pt-2px>
|
|
148
|
+
<Teleport to="body">
|
|
149
|
+
<div
|
|
150
|
+
v-if="popoverOpen"
|
|
151
|
+
ref="popoverEl"
|
|
152
|
+
class="citation-ref-popover"
|
|
153
|
+
:style="popoverStyle"
|
|
154
|
+
w-300px
|
|
155
|
+
bg-white
|
|
156
|
+
border-0.5px border-solid border-neutral-300
|
|
157
|
+
rounded-12px p-12px
|
|
158
|
+
shadow-lg
|
|
159
|
+
cursor-default
|
|
160
|
+
flex="~ col" gap-8px
|
|
161
|
+
>
|
|
162
|
+
<template v-if="status === 'unresolved'">
|
|
163
|
+
<div flex items-start gap-6px>
|
|
164
|
+
<span i-ph-warning-circle text-14px text-amber-600 mt-2px flex-shrink-0 />
|
|
165
|
+
<p text-12px leading-16px text-neutral-700>
|
|
166
|
+
Não foi possível verificar esta referência. Ela pode não existir ou não estar disponível para o seu perfil.
|
|
167
|
+
</p>
|
|
168
|
+
</div>
|
|
155
169
|
<button
|
|
156
|
-
v-if="canOpenDocument"
|
|
157
170
|
type="button"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
cursor-pointer hover:underline
|
|
161
|
-
@click="openDocument"
|
|
171
|
+
self-start text-12px leading-16px text-neutral-700 underline cursor-pointer
|
|
172
|
+
@click="retry"
|
|
162
173
|
>
|
|
163
|
-
|
|
164
|
-
Ver documento
|
|
174
|
+
Tentar novamente
|
|
165
175
|
</button>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
</template>
|
|
177
|
+
|
|
178
|
+
<template v-else-if="meta">
|
|
179
|
+
<div flex="~ col" gap-2px>
|
|
180
|
+
<p text-13px leading-18px font-580 text-neutral-900>
|
|
181
|
+
{{ meta.title }}
|
|
182
|
+
</p>
|
|
183
|
+
<p v-if="meta.sourceLabel || meta.identifier" text-12px leading-16px text-neutral-600>
|
|
184
|
+
{{ [meta.sourceLabel, meta.identifier].filter(Boolean).join(" \xB7 ") }}
|
|
185
|
+
</p>
|
|
186
|
+
<p v-if="formattedDate" text-12px leading-16px text-neutral-500>
|
|
187
|
+
{{ meta.dateLabel ? `${meta.dateLabel}: ` : "" }}{{ formattedDate }}
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<span
|
|
192
|
+
v-if="meta.badge"
|
|
193
|
+
self-start px-6px py-1px rounded-4px text-11px leading-15px font-580
|
|
194
|
+
:class="badgeClasses"
|
|
174
195
|
>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
196
|
+
{{ meta.badge.label }}
|
|
197
|
+
</span>
|
|
198
|
+
|
|
199
|
+
<p v-if="meta.snippet" text-12px leading-17px text-neutral-600 line-clamp-3>
|
|
200
|
+
{{ meta.snippet }}
|
|
201
|
+
</p>
|
|
202
|
+
|
|
203
|
+
<div v-if="canOpenDocument || safeSourceUrl" flex items-center gap-12px pt-2px>
|
|
204
|
+
<button
|
|
205
|
+
v-if="canOpenDocument"
|
|
206
|
+
type="button"
|
|
207
|
+
inline-flex items-center gap-4px
|
|
208
|
+
text-12px leading-16px font-580 text-neutral-900
|
|
209
|
+
cursor-pointer hover:underline
|
|
210
|
+
@click="openDocument"
|
|
211
|
+
>
|
|
212
|
+
<span i-ph-file-magnifying-glass text-13px />
|
|
213
|
+
Ver documento
|
|
214
|
+
</button>
|
|
215
|
+
<a
|
|
216
|
+
v-if="safeSourceUrl"
|
|
217
|
+
:href="safeSourceUrl"
|
|
218
|
+
target="_blank"
|
|
219
|
+
rel="noopener noreferrer"
|
|
220
|
+
inline-flex items-center gap-4px
|
|
221
|
+
text-12px leading-16px text-neutral-600
|
|
222
|
+
hover:underline
|
|
223
|
+
>
|
|
224
|
+
<span i-ph-arrow-square-out text-13px />
|
|
225
|
+
Abrir na fonte
|
|
226
|
+
</a>
|
|
227
|
+
</div>
|
|
228
|
+
</template>
|
|
229
|
+
</div>
|
|
230
|
+
</Teleport>
|
|
181
231
|
</span>
|
|
182
232
|
</template>
|
|
183
233
|
|
|
184
234
|
<style scoped>
|
|
185
|
-
.citation-ref{display:inline;position:relative}.citation-ref-anchor{background:transparent;border:0;border-radius:5px;color:inherit;cursor:pointer;display:inline;font:inherit;margin:0 -2px;padding:1px 3px;text-decoration:underline;text-decoration-color:#a3a3a3;text-decoration-style:dotted;text-decoration-thickness:1px;text-underline-offset:3px;transition:background-color .15s ease,text-decoration-color .15s ease}.citation-ref-anchor:hover{background-color:#f5f5f5;text-decoration-color:#525252}.citation-ref-anchor--loading{cursor:default;opacity:.6;text-decoration-color:#d4d4d4}.citation-ref-anchor--unresolved{text-decoration-color:#f59e0b}.citation-ref-anchor--unresolved:hover{background-color:#fffbeb}.citation-ref-icon{display:inline-block;font-size:.85em;margin-right:.25em;vertical-align:-.08em}
|
|
235
|
+
.citation-ref{display:inline;position:relative}.citation-ref-popover{position:fixed;z-index:50}.citation-ref-anchor{background:transparent;border:0;border-radius:5px;color:inherit;cursor:pointer;display:inline;font:inherit;margin:0 -2px;padding:1px 3px;text-decoration:underline;text-decoration-color:#a3a3a3;text-decoration-style:dotted;text-decoration-thickness:1px;text-underline-offset:3px;transition:background-color .15s ease,text-decoration-color .15s ease}.citation-ref-anchor:hover{background-color:#f5f5f5;text-decoration-color:#525252}.citation-ref-anchor--loading{cursor:default;opacity:.6;text-decoration-color:#d4d4d4}.citation-ref-anchor--unresolved{text-decoration-color:#f59e0b}.citation-ref-anchor--unresolved:hover{background-color:#fffbeb}.citation-ref-icon{display:inline-block;font-size:.85em;margin-right:.25em;vertical-align:-.08em}
|
|
186
236
|
</style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useChatAuth } from "#chat-auth";
|
|
3
3
|
import { useBreakpoints } from "@vueuse/core";
|
|
4
|
+
import { useCitationPanel } from "../../citations/composables/citation-panel";
|
|
4
5
|
import { exportConversationToMarkdown } from "../../conversations/export";
|
|
5
6
|
import { CHAT_MOBILE_BREAKPOINT } from "../../shared/breakpoints";
|
|
6
7
|
import ChatMobileFilePreview from "./mobile/chat-mobile-file-preview.vue";
|
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
import { getCookie } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
import { parseAllowedEmailDomains } from "./domain-allow.js";
|
|
4
|
+
import { logger } from "./logger.js";
|
|
3
5
|
const DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME = "tela-jwt";
|
|
4
6
|
function resolveFirstPartyJwtCookieName(event) {
|
|
5
7
|
const config = useRuntimeConfig(event);
|
|
6
8
|
return config.public?.telaAuth?.jwtCookieName || DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME;
|
|
7
9
|
}
|
|
10
|
+
function resolveHostAuthTokenCookieName(event) {
|
|
11
|
+
const config = useRuntimeConfig(event);
|
|
12
|
+
return config.chatNuxt?.authTokenCookie || void 0;
|
|
13
|
+
}
|
|
14
|
+
function hasEmailDomainRestrictions(event) {
|
|
15
|
+
const config = useRuntimeConfig(event);
|
|
16
|
+
return parseAllowedEmailDomains(config.auth?.allowedEmailDomains).length > 0;
|
|
17
|
+
}
|
|
8
18
|
export function resolveAuthToken(event) {
|
|
9
19
|
const auth = event.context.auth;
|
|
10
20
|
if (auth?.token) {
|
|
11
21
|
return auth.token;
|
|
12
22
|
}
|
|
23
|
+
const hostCookieName = resolveHostAuthTokenCookieName(event);
|
|
24
|
+
if (hostCookieName) {
|
|
25
|
+
if (hasEmailDomainRestrictions(event)) {
|
|
26
|
+
logger.warn(
|
|
27
|
+
{ cookie: hostCookieName },
|
|
28
|
+
"authTokenCookie ignored: auth.allowedEmailDomains is set and the host cookie carries no user context for domain validation"
|
|
29
|
+
);
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
const token2 = getCookie(event, hostCookieName);
|
|
33
|
+
if (token2 && auth) {
|
|
34
|
+
auth.token = token2;
|
|
35
|
+
}
|
|
36
|
+
return token2 || void 0;
|
|
37
|
+
}
|
|
13
38
|
if (!auth?.user || !auth.workspace) {
|
|
14
39
|
return void 0;
|
|
15
40
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/chat-nuxt",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/types.d.mts",
|
|
8
8
|
"import": "./dist/module.mjs"
|
|
9
9
|
},
|
|
10
|
+
"./auth": {
|
|
11
|
+
"types": "./dist/runtime/auth/define.d.ts",
|
|
12
|
+
"import": "./dist/runtime/auth/define.js"
|
|
13
|
+
},
|
|
10
14
|
"./components/MeistrariChatEmbed": {
|
|
11
15
|
"types": "./dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts",
|
|
12
16
|
"import": "./dist/runtime/embed/components/meistrari-chat-embed.vue"
|