@lobehub/lobehub 2.0.0-next.376 → 2.0.0-next.378
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 +67 -0
- package/changelog/v1.json +21 -0
- package/docs/development/database-schema.dbml +51 -0
- package/docs/self-hosting/advanced/auth/providers/casdoor.mdx +1 -1
- package/docs/self-hosting/advanced/auth/providers/casdoor.zh-CN.mdx +1 -1
- package/docs/self-hosting/advanced/auth/providers/logto.mdx +1 -1
- package/docs/self-hosting/advanced/auth/providers/logto.zh-CN.mdx +1 -1
- package/package.json +1 -2
- package/packages/database/migrations/0075_add_user_memory_persona.sql +51 -0
- package/packages/database/migrations/meta/0075_snapshot.json +11957 -0
- package/packages/database/migrations/meta/_journal.json +8 -1
- package/packages/database/src/schemas/userMemories/persona.ts +81 -0
- package/scripts/_shared/checkDeprecatedAuth.js +46 -16
- package/scripts/_shared/checkDeprecatedAuth.test.ts +180 -0
- package/src/app/(backend)/api/webhooks/casdoor/route.ts +1 -2
- package/src/app/(backend)/api/webhooks/logto/route.ts +2 -3
- package/src/app/(backend)/trpc/async/[trpc]/route.ts +1 -2
- package/src/app/(backend)/trpc/mobile/[trpc]/route.ts +1 -2
- package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +1 -0
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx +9 -0
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +27 -2
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/Actions.tsx +1 -1
- package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +40 -32
- package/src/app/[variants]/(main)/memory/(home)/index.tsx +1 -1
- package/src/app/[variants]/(main)/memory/contexts/index.tsx +2 -0
- package/src/app/[variants]/(main)/memory/experiences/index.tsx +2 -0
- package/src/app/[variants]/(main)/memory/features/MemoryAnalysis/Action.tsx +13 -2
- package/src/app/[variants]/(main)/memory/features/MemoryAnalysis/AnalysisTrigger.tsx +26 -13
- package/src/app/[variants]/(main)/memory/features/MemoryAnalysis/index.tsx +10 -1
- package/src/app/[variants]/(main)/memory/identities/index.tsx +2 -0
- package/src/app/[variants]/(main)/memory/preferences/index.tsx +2 -0
- package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +7 -3
- package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +30 -30
- package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +31 -31
- package/src/app/[variants]/(main)/settings/skill/index.tsx +2 -2
- package/src/components/FileParsingStatus/EmbeddingStatus.tsx +3 -16
- package/src/components/FileParsingStatus/index.tsx +2 -15
- package/src/features/ChatInput/ActionBar/Tools/PopoverContent.tsx +1 -3
- package/src/features/ChatInput/ActionBar/Tools/ToolsList.tsx +4 -0
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +1 -10
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +41 -16
- package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +2 -1
- package/src/features/Conversation/ChatItem/components/Title.tsx +6 -2
- package/src/features/ModelSelect/index.tsx +10 -3
- package/src/features/ProfileEditor/AgentTool.tsx +52 -33
- package/src/features/ProfileEditor/PopoverContent.tsx +28 -61
- package/src/features/SharePopover/index.tsx +3 -3
- package/src/features/SkillStore/CommunityList/Item.tsx +2 -1
- package/src/features/SkillStore/CommunityList/index.tsx +16 -22
- package/src/features/SkillStore/CustomList/Item.tsx +2 -1
- package/src/features/SkillStore/CustomList/index.tsx +11 -31
- package/src/features/SkillStore/LobeHubList/Item.tsx +4 -3
- package/src/features/SkillStore/LobeHubList/index.tsx +2 -18
- package/src/features/SkillStore/Search/index.tsx +1 -1
- package/src/features/SkillStore/index.tsx +6 -3
- package/src/features/SkillStore/style.ts +34 -1
- package/src/libs/next/config/define-config.ts +0 -3
- package/src/server/routers/lambda/agent.ts +1 -2
- package/src/server/services/user/index.ts +1 -2
- package/src/server/services/webhookUser/index.test.ts +290 -0
- package/src/server/services/webhookUser/index.ts +29 -12
- package/src/libs/logger/index.ts +0 -5
|
@@ -525,7 +525,14 @@
|
|
|
525
525
|
"when": 1769341100106,
|
|
526
526
|
"tag": "0074_add_fk_indexes_for_cascade_delete",
|
|
527
527
|
"breakpoints": true
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"idx": 75,
|
|
531
|
+
"version": "7",
|
|
532
|
+
"when": 1769362978088,
|
|
533
|
+
"tag": "0075_add_user_memory_persona",
|
|
534
|
+
"breakpoints": true
|
|
528
535
|
}
|
|
529
536
|
],
|
|
530
537
|
"version": "6"
|
|
531
|
-
}
|
|
538
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
|
+
import { index, integer, jsonb, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core';
|
|
3
|
+
|
|
4
|
+
import { createNanoId } from '../../utils/idGenerator';
|
|
5
|
+
import { timestamps, timestamptz, varchar255 } from '../_helpers';
|
|
6
|
+
import { users } from '../user';
|
|
7
|
+
|
|
8
|
+
export const userPersonaDocuments = pgTable(
|
|
9
|
+
'user_memory_persona_documents',
|
|
10
|
+
{
|
|
11
|
+
id: varchar255('id')
|
|
12
|
+
.$defaultFn(() => createNanoId(18)())
|
|
13
|
+
.primaryKey(),
|
|
14
|
+
|
|
15
|
+
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
|
16
|
+
profile: varchar255('profile').default('default').notNull(),
|
|
17
|
+
|
|
18
|
+
tagline: text('tagline'),
|
|
19
|
+
persona: text('persona'),
|
|
20
|
+
|
|
21
|
+
memoryIds: jsonb('memory_ids').$type<string[]>(),
|
|
22
|
+
sourceIds: jsonb('source_ids').$type<string[]>(),
|
|
23
|
+
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
|
|
24
|
+
|
|
25
|
+
version: integer('version').notNull().default(1),
|
|
26
|
+
capturedAt: timestamptz('captured_at').notNull().defaultNow(),
|
|
27
|
+
|
|
28
|
+
...timestamps,
|
|
29
|
+
},
|
|
30
|
+
(table) => [
|
|
31
|
+
uniqueIndex('user_persona_documents_user_id_profile_unique').on(table.userId, table.profile),
|
|
32
|
+
index('user_persona_documents_user_id_index').on(table.userId),
|
|
33
|
+
],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const userPersonaDocumentHistories = pgTable(
|
|
37
|
+
'user_memory_persona_document_histories',
|
|
38
|
+
{
|
|
39
|
+
id: varchar255('id')
|
|
40
|
+
.$defaultFn(() => createNanoId(18)())
|
|
41
|
+
.primaryKey(),
|
|
42
|
+
|
|
43
|
+
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
|
44
|
+
personaId: varchar255('persona_id').references(() => userPersonaDocuments.id, {
|
|
45
|
+
onDelete: 'cascade',
|
|
46
|
+
}),
|
|
47
|
+
profile: varchar255('profile').default('default').notNull(),
|
|
48
|
+
|
|
49
|
+
snapshotPersona: text('snapshot_persona'),
|
|
50
|
+
snapshotTagline: text('snapshot_tagline'),
|
|
51
|
+
reasoning: text('reasoning'),
|
|
52
|
+
diffPersona: text('diff_persona'),
|
|
53
|
+
diffTagline: text('diff_tagline'),
|
|
54
|
+
snapshot: text('snapshot'),
|
|
55
|
+
summary: text('summary'),
|
|
56
|
+
editedBy: varchar255('edited_by').default('agent'),
|
|
57
|
+
|
|
58
|
+
memoryIds: jsonb('memory_ids').$type<string[]>(),
|
|
59
|
+
sourceIds: jsonb('source_ids').$type<string[]>(),
|
|
60
|
+
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
|
|
61
|
+
|
|
62
|
+
previousVersion: integer('previous_version'),
|
|
63
|
+
nextVersion: integer('next_version'),
|
|
64
|
+
|
|
65
|
+
capturedAt: timestamptz('captured_at').notNull().defaultNow(),
|
|
66
|
+
|
|
67
|
+
...timestamps,
|
|
68
|
+
},
|
|
69
|
+
(table) => [
|
|
70
|
+
index('user_persona_document_histories_persona_id_index').on(table.personaId),
|
|
71
|
+
index('user_persona_document_histories_user_id_index').on(table.userId),
|
|
72
|
+
index('user_persona_document_histories_profile_index').on(table.profile),
|
|
73
|
+
],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export type UserPersonaDocument = typeof userPersonaDocuments.$inferSelect;
|
|
77
|
+
export type NewUserPersonaDocument = typeof userPersonaDocuments.$inferInsert;
|
|
78
|
+
|
|
79
|
+
export type UserPersonaDocumentHistoriesItem = typeof userPersonaDocumentHistories.$inferSelect;
|
|
80
|
+
export type NewUserPersonaDocumentHistoriesItem =
|
|
81
|
+
typeof userPersonaDocumentHistories.$inferInsert;
|
|
@@ -15,6 +15,7 @@ const MIGRATION_DOC_BASE = 'https://lobehub.com/docs/self-hosting/advanced/auth'
|
|
|
15
15
|
* message: string;
|
|
16
16
|
* docUrl?: string;
|
|
17
17
|
* formatVar?: (envVar: string) => string;
|
|
18
|
+
* severity?: 'error' | 'warning';
|
|
18
19
|
* }>}
|
|
19
20
|
*/
|
|
20
21
|
const DEPRECATED_CHECKS = [
|
|
@@ -144,8 +145,9 @@ const DEPRECATED_CHECKS = [
|
|
|
144
145
|
return [];
|
|
145
146
|
},
|
|
146
147
|
message:
|
|
147
|
-
'Casdoor webhook is
|
|
148
|
+
'Casdoor webhook is recommended for syncing user data (email, avatar, etc.) to LobeChat. This is especially important for users migrating from NextAuth to Better Auth - users without email configured in Casdoor will not be able to login. Consider configuring CASDOOR_WEBHOOK_SECRET following the documentation.',
|
|
148
149
|
name: 'Casdoor Webhook',
|
|
150
|
+
severity: 'warning',
|
|
149
151
|
},
|
|
150
152
|
{
|
|
151
153
|
docUrl: `${MIGRATION_DOC_BASE}/providers/logto`,
|
|
@@ -157,8 +159,9 @@ const DEPRECATED_CHECKS = [
|
|
|
157
159
|
return [];
|
|
158
160
|
},
|
|
159
161
|
message:
|
|
160
|
-
'Logto webhook is
|
|
162
|
+
'Logto webhook is recommended for syncing user data (email, avatar, etc.) to LobeChat. This is especially important for users migrating from NextAuth to Better Auth - users without email configured in Logto will not be able to login. Consider configuring LOGTO_WEBHOOK_SIGNING_KEY following the documentation.',
|
|
161
163
|
name: 'Logto Webhook',
|
|
164
|
+
severity: 'warning',
|
|
162
165
|
},
|
|
163
166
|
{
|
|
164
167
|
docUrl: `${MIGRATION_DOC_BASE}/nextauth-to-betterauth`,
|
|
@@ -185,18 +188,22 @@ const DEPRECATED_CHECKS = [
|
|
|
185
188
|
];
|
|
186
189
|
|
|
187
190
|
/**
|
|
188
|
-
* Print a single deprecation error
|
|
191
|
+
* Print a single deprecation block (error or warning)
|
|
189
192
|
*/
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
console.
|
|
193
|
+
function printIssueBlock(name, vars, message, docUrl, formatVar, severity = 'error') {
|
|
194
|
+
const isWarning = severity === 'warning';
|
|
195
|
+
const icon = isWarning ? '⚠️' : '❌';
|
|
196
|
+
const log = isWarning ? console.warn : console.error;
|
|
197
|
+
|
|
198
|
+
log(`\n${icon} ${name}`);
|
|
199
|
+
log('─'.repeat(50));
|
|
200
|
+
log(isWarning ? 'Missing recommended environment variables:' : 'Detected deprecated environment variables:');
|
|
194
201
|
for (const envVar of vars) {
|
|
195
|
-
|
|
202
|
+
log(` • ${formatVar ? formatVar(envVar) : envVar}`);
|
|
196
203
|
}
|
|
197
|
-
|
|
204
|
+
log(`\n${message}`);
|
|
198
205
|
if (docUrl) {
|
|
199
|
-
|
|
206
|
+
log(`📖 Documentation: ${docUrl}`);
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
|
|
@@ -208,23 +215,46 @@ function printErrorBlock(name, vars, message, docUrl, formatVar) {
|
|
|
208
215
|
function checkDeprecatedAuth(options = {}) {
|
|
209
216
|
const { action = 'redeploy' } = options;
|
|
210
217
|
|
|
211
|
-
const
|
|
218
|
+
const errors = [];
|
|
219
|
+
const warnings = [];
|
|
220
|
+
|
|
212
221
|
for (const check of DEPRECATED_CHECKS) {
|
|
213
222
|
const foundVars = check.getVars();
|
|
214
223
|
if (foundVars.length > 0) {
|
|
215
|
-
|
|
224
|
+
const issue = { ...check, foundVars };
|
|
225
|
+
if (check.severity === 'warning') {
|
|
226
|
+
warnings.push(issue);
|
|
227
|
+
} else {
|
|
228
|
+
errors.push(issue);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Print warnings (non-blocking)
|
|
234
|
+
if (warnings.length > 0) {
|
|
235
|
+
console.warn('\n' + '═'.repeat(70));
|
|
236
|
+
console.warn(`⚠️ WARNING: Found ${warnings.length} recommended configuration(s) missing`);
|
|
237
|
+
console.warn('═'.repeat(70));
|
|
238
|
+
|
|
239
|
+
for (const issue of warnings) {
|
|
240
|
+
printIssueBlock(issue.name, issue.foundVars, issue.message, issue.docUrl, issue.formatVar, 'warning');
|
|
216
241
|
}
|
|
242
|
+
|
|
243
|
+
console.warn('\n' + '═'.repeat(70));
|
|
244
|
+
console.warn('These are recommendations. Your application will still run.');
|
|
245
|
+
console.warn('═'.repeat(70) + '\n');
|
|
217
246
|
}
|
|
218
247
|
|
|
219
|
-
|
|
248
|
+
// Print errors and exit (blocking)
|
|
249
|
+
if (errors.length > 0) {
|
|
220
250
|
console.error('\n' + '═'.repeat(70));
|
|
221
251
|
console.error(
|
|
222
|
-
`❌ ERROR: Found ${
|
|
252
|
+
`❌ ERROR: Found ${errors.length} deprecated environment variable issue(s)!`,
|
|
223
253
|
);
|
|
224
254
|
console.error('═'.repeat(70));
|
|
225
255
|
|
|
226
|
-
for (const issue of
|
|
227
|
-
|
|
256
|
+
for (const issue of errors) {
|
|
257
|
+
printIssueBlock(issue.name, issue.foundVars, issue.message, issue.docUrl, issue.formatVar, 'error');
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
console.error('\n' + '═'.repeat(70));
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Store original env
|
|
4
|
+
const originalEnv = { ...process.env };
|
|
5
|
+
|
|
6
|
+
// Mock process.exit to prevent actual exit
|
|
7
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
8
|
+
|
|
9
|
+
// Mock console methods
|
|
10
|
+
const mockConsoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
11
|
+
const mockConsoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
12
|
+
|
|
13
|
+
describe('checkDeprecatedAuth', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Reset env before each test
|
|
16
|
+
process.env = { ...originalEnv };
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
// Clear module cache to ensure fresh import
|
|
19
|
+
vi.resetModules();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
// Restore original env
|
|
24
|
+
process.env = originalEnv;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should not exit when no deprecated env vars are set', async () => {
|
|
28
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
29
|
+
checkDeprecatedAuth();
|
|
30
|
+
|
|
31
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should exit with code 1 when NextAuth env vars are detected', async () => {
|
|
35
|
+
process.env.NEXT_AUTH_SECRET = 'test-secret';
|
|
36
|
+
|
|
37
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
38
|
+
checkDeprecatedAuth();
|
|
39
|
+
|
|
40
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
41
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should exit with code 1 when NEXTAUTH env vars are detected', async () => {
|
|
45
|
+
process.env.NEXTAUTH_SECRET = 'test-secret';
|
|
46
|
+
|
|
47
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
48
|
+
checkDeprecatedAuth();
|
|
49
|
+
|
|
50
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should exit with code 1 when Clerk env vars are detected', async () => {
|
|
54
|
+
process.env.CLERK_SECRET_KEY = 'test-key';
|
|
55
|
+
|
|
56
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
57
|
+
checkDeprecatedAuth();
|
|
58
|
+
|
|
59
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should exit with code 1 when ACCESS_CODE is set', async () => {
|
|
63
|
+
process.env.ACCESS_CODE = 'test-code';
|
|
64
|
+
|
|
65
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
66
|
+
checkDeprecatedAuth();
|
|
67
|
+
|
|
68
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should exit with code 1 when APP_URL has trailing slash', async () => {
|
|
72
|
+
process.env.APP_URL = 'https://example.com/';
|
|
73
|
+
|
|
74
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
75
|
+
checkDeprecatedAuth();
|
|
76
|
+
|
|
77
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should not exit when APP_URL has no trailing slash', async () => {
|
|
81
|
+
process.env.APP_URL = 'https://example.com';
|
|
82
|
+
|
|
83
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
84
|
+
checkDeprecatedAuth();
|
|
85
|
+
|
|
86
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('webhook warnings (non-blocking)', () => {
|
|
90
|
+
it('should warn but not exit when Casdoor webhook is missing', async () => {
|
|
91
|
+
process.env.AUTH_SSO_PROVIDERS = 'casdoor';
|
|
92
|
+
// CASDOOR_WEBHOOK_SECRET is not set
|
|
93
|
+
|
|
94
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
95
|
+
checkDeprecatedAuth();
|
|
96
|
+
|
|
97
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
98
|
+
expect(mockConsoleWarn).toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should warn but not exit when Logto webhook is missing', async () => {
|
|
102
|
+
process.env.AUTH_SSO_PROVIDERS = 'logto';
|
|
103
|
+
// LOGTO_WEBHOOK_SIGNING_KEY is not set
|
|
104
|
+
|
|
105
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
106
|
+
checkDeprecatedAuth();
|
|
107
|
+
|
|
108
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
109
|
+
expect(mockConsoleWarn).toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should not warn when Casdoor webhook is configured', async () => {
|
|
113
|
+
process.env.AUTH_SSO_PROVIDERS = 'casdoor';
|
|
114
|
+
process.env.CASDOOR_WEBHOOK_SECRET = 'test-secret';
|
|
115
|
+
|
|
116
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
117
|
+
checkDeprecatedAuth();
|
|
118
|
+
|
|
119
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
120
|
+
expect(mockConsoleWarn).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should not warn when Logto webhook is configured', async () => {
|
|
124
|
+
process.env.AUTH_SSO_PROVIDERS = 'logto';
|
|
125
|
+
process.env.LOGTO_WEBHOOK_SIGNING_KEY = 'test-key';
|
|
126
|
+
|
|
127
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
128
|
+
checkDeprecatedAuth();
|
|
129
|
+
|
|
130
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
131
|
+
expect(mockConsoleWarn).not.toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should not warn when provider is not casdoor or logto', async () => {
|
|
135
|
+
process.env.AUTH_SSO_PROVIDERS = 'google';
|
|
136
|
+
|
|
137
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
138
|
+
checkDeprecatedAuth();
|
|
139
|
+
|
|
140
|
+
expect(mockExit).not.toHaveBeenCalled();
|
|
141
|
+
expect(mockConsoleWarn).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('mixed errors and warnings', () => {
|
|
146
|
+
it('should exit when there are errors even if there are also warnings', async () => {
|
|
147
|
+
process.env.AUTH_SSO_PROVIDERS = 'logto'; // warning
|
|
148
|
+
process.env.ACCESS_CODE = 'test-code'; // error
|
|
149
|
+
|
|
150
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
151
|
+
checkDeprecatedAuth();
|
|
152
|
+
|
|
153
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
154
|
+
expect(mockConsoleWarn).toHaveBeenCalled();
|
|
155
|
+
expect(mockConsoleError).toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('action parameter', () => {
|
|
160
|
+
it('should use "redeploy" as default action', async () => {
|
|
161
|
+
process.env.ACCESS_CODE = 'test-code';
|
|
162
|
+
|
|
163
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
164
|
+
checkDeprecatedAuth();
|
|
165
|
+
|
|
166
|
+
const calls = mockConsoleError.mock.calls.flat().join(' ');
|
|
167
|
+
expect(calls).toContain('redeploy');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should use custom action when provided', async () => {
|
|
171
|
+
process.env.ACCESS_CODE = 'test-code';
|
|
172
|
+
|
|
173
|
+
const { checkDeprecatedAuth } = await import('./checkDeprecatedAuth.js');
|
|
174
|
+
checkDeprecatedAuth({ action: 'restart' });
|
|
175
|
+
|
|
176
|
+
const calls = mockConsoleError.mock.calls.flat().join(' ');
|
|
177
|
+
expect(calls).toContain('restart');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -2,7 +2,6 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
|
|
3
3
|
import { serverDB } from '@/database/server';
|
|
4
4
|
import { authEnv } from '@/envs/auth';
|
|
5
|
-
import { pino } from '@/libs/logger';
|
|
6
5
|
import { WebhookUserService } from '@/server/services/webhookUser';
|
|
7
6
|
|
|
8
7
|
import { validateRequest } from './validateRequest';
|
|
@@ -36,7 +35,7 @@ export const POST = async (req: Request): Promise<NextResponse> => {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
default: {
|
|
39
|
-
|
|
38
|
+
console.warn(
|
|
40
39
|
`${req.url} received event type "${action}", but no handler is defined for this type`,
|
|
41
40
|
);
|
|
42
41
|
return NextResponse.json({ error: `unrecognised payload type: ${action}` }, { status: 400 });
|
|
@@ -2,7 +2,6 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
|
|
3
3
|
import { serverDB } from '@/database/server';
|
|
4
4
|
import { authEnv } from '@/envs/auth';
|
|
5
|
-
import { pino } from '@/libs/logger';
|
|
6
5
|
import { WebhookUserService } from '@/server/services/webhookUser';
|
|
7
6
|
|
|
8
7
|
import { validateRequest } from './validateRequest';
|
|
@@ -19,7 +18,7 @@ export const POST = async (req: Request): Promise<NextResponse> => {
|
|
|
19
18
|
|
|
20
19
|
const { event, data } = payload;
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
console.log(`logto webhook payload: ${{ data, event }}`);
|
|
23
22
|
|
|
24
23
|
const webhookUserService = new WebhookUserService(serverDB);
|
|
25
24
|
switch (event) {
|
|
@@ -47,7 +46,7 @@ export const POST = async (req: Request): Promise<NextResponse> => {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
default: {
|
|
50
|
-
|
|
49
|
+
console.warn(
|
|
51
50
|
`${req.url} received event type "${event}", but no handler is defined for this type`,
|
|
52
51
|
);
|
|
53
52
|
return NextResponse.json({ error: `unrecognised payload type: ${event}` }, { status: 400 });
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
|
2
2
|
import type { NextRequest } from 'next/server';
|
|
3
3
|
|
|
4
|
-
import { pino } from '@/libs/logger';
|
|
5
4
|
import { createAsyncRouteContext } from '@/libs/trpc/async/context';
|
|
6
5
|
import { prepareRequestForTRPC } from '@/libs/trpc/utils/request-adapter';
|
|
7
6
|
import { createResponseMeta } from '@/libs/trpc/utils/responseMeta';
|
|
@@ -25,7 +24,7 @@ const handler = (req: NextRequest) => {
|
|
|
25
24
|
endpoint: '/trpc/async',
|
|
26
25
|
|
|
27
26
|
onError: ({ error, path, type }) => {
|
|
28
|
-
|
|
27
|
+
console.log(`Error in tRPC handler (async) on path: ${path}, type: ${type}`);
|
|
29
28
|
console.error(error);
|
|
30
29
|
},
|
|
31
30
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
|
2
2
|
import type { NextRequest } from 'next/server';
|
|
3
3
|
|
|
4
|
-
import { pino } from '@/libs/logger';
|
|
5
4
|
import { createLambdaContext } from '@/libs/trpc/lambda/context';
|
|
6
5
|
import { prepareRequestForTRPC } from '@/libs/trpc/utils/request-adapter';
|
|
7
6
|
import { createResponseMeta } from '@/libs/trpc/utils/responseMeta';
|
|
@@ -21,7 +20,7 @@ const handler = (req: NextRequest) => {
|
|
|
21
20
|
endpoint: '/trpc/mobile',
|
|
22
21
|
|
|
23
22
|
onError: ({ error, path, type }) => {
|
|
24
|
-
|
|
23
|
+
console.log(`Error in tRPC handler (mobile) on path: ${path}, type: ${type}`);
|
|
25
24
|
console.error(error);
|
|
26
25
|
},
|
|
27
26
|
|
|
@@ -65,6 +65,13 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
65
65
|
color: ${cssVar.colorTextTertiary};
|
|
66
66
|
text-transform: uppercase;
|
|
67
67
|
`,
|
|
68
|
+
trigger: css`
|
|
69
|
+
border-radius: ${cssVar.borderRadius};
|
|
70
|
+
|
|
71
|
+
&[data-popup-open] {
|
|
72
|
+
background: ${cssVar.colorFillTertiary};
|
|
73
|
+
}
|
|
74
|
+
`,
|
|
68
75
|
}));
|
|
69
76
|
|
|
70
77
|
interface AgentProfilePopupProps extends PropsWithChildren {
|
|
@@ -169,7 +176,9 @@ const AgentProfilePopup = memo<AgentProfilePopupProps>(({ agent, groupId, childr
|
|
|
169
176
|
|
|
170
177
|
return (
|
|
171
178
|
<Popover
|
|
179
|
+
classNames={{ trigger: styles.trigger }}
|
|
172
180
|
content={content}
|
|
181
|
+
nativeButton={false}
|
|
173
182
|
onOpenChange={setOpen}
|
|
174
183
|
open={open}
|
|
175
184
|
placement="right"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ActionIcon, Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { createStaticStyles } from 'antd-style';
|
|
4
5
|
import { UserMinus } from 'lucide-react';
|
|
5
|
-
import { memo, useState } from 'react';
|
|
6
|
+
import { memo, useMemo, useState } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { useLocation } from 'react-router-dom';
|
|
7
9
|
|
|
8
10
|
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
9
11
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
@@ -20,6 +22,18 @@ import AddGroupMemberModal from '../AddGroupMemberModal';
|
|
|
20
22
|
import AgentProfilePopup from './AgentProfilePopup';
|
|
21
23
|
import GroupMemberItem from './GroupMemberItem';
|
|
22
24
|
|
|
25
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
26
|
+
memberTrigger: css`
|
|
27
|
+
border-radius: ${cssVar.borderRadius};
|
|
28
|
+
transition: background 0.2s ${cssVar.motionEaseOut};
|
|
29
|
+
|
|
30
|
+
&[data-popup-open],
|
|
31
|
+
&[data-active='true'] {
|
|
32
|
+
background: ${cssVar.colorFillTertiary};
|
|
33
|
+
}
|
|
34
|
+
`,
|
|
35
|
+
}));
|
|
36
|
+
|
|
23
37
|
interface GroupMemberProps {
|
|
24
38
|
addModalOpen: boolean;
|
|
25
39
|
groupId?: string;
|
|
@@ -32,6 +46,7 @@ interface GroupMemberProps {
|
|
|
32
46
|
const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange, groupId }) => {
|
|
33
47
|
const { t } = useTranslation('chat');
|
|
34
48
|
const router = useQueryRoute();
|
|
49
|
+
const location = useLocation();
|
|
35
50
|
const [nickname, username] = useUserStore((s) => [
|
|
36
51
|
userProfileSelectors.nickName(s),
|
|
37
52
|
userProfileSelectors.username(s),
|
|
@@ -43,6 +58,12 @@ const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange
|
|
|
43
58
|
|
|
44
59
|
const groupMembers = useAgentGroupStore(agentGroupSelectors.getGroupMembers(groupId || ''));
|
|
45
60
|
|
|
61
|
+
const activeTab = useMemo(() => new URLSearchParams(location.search).get('tab'), [location.search]);
|
|
62
|
+
const isProfileRoute = useMemo(() => {
|
|
63
|
+
if (!groupId) return false;
|
|
64
|
+
return location.pathname === `/group/${groupId}/profile`;
|
|
65
|
+
}, [groupId, location.pathname]);
|
|
66
|
+
|
|
46
67
|
const handleAddMembers = async (selectedAgents: string[]) => {
|
|
47
68
|
if (!groupId) {
|
|
48
69
|
console.error('No active group to add members to');
|
|
@@ -96,7 +117,11 @@ const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange
|
|
|
96
117
|
key={item.id}
|
|
97
118
|
onChat={() => handleMemberClick(item.id)}
|
|
98
119
|
>
|
|
99
|
-
<div
|
|
120
|
+
<div
|
|
121
|
+
className={styles.memberTrigger}
|
|
122
|
+
data-active={isProfileRoute && activeTab === item.id ? 'true' : undefined}
|
|
123
|
+
onDoubleClick={() => handleMemberDoubleClick(item.id)}
|
|
124
|
+
>
|
|
100
125
|
<GroupMemberItem
|
|
101
126
|
actions={
|
|
102
127
|
<ActionIcon
|
|
@@ -9,7 +9,7 @@ interface ActionsProps {
|
|
|
9
9
|
|
|
10
10
|
const Actions = memo<ActionsProps>(({ dropdownMenu, isLoading }) => {
|
|
11
11
|
return (
|
|
12
|
-
<DropdownMenu items={dropdownMenu}>
|
|
12
|
+
<DropdownMenu items={dropdownMenu} nativeButton={false}>
|
|
13
13
|
<ActionIcon
|
|
14
14
|
icon={MoreHorizontalIcon}
|
|
15
15
|
loading={isLoading}
|