@lobehub/chat 1.19.11 → 1.19.12
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.
Potentially problematic release.
This version of @lobehub/chat might be problematic. Click here for more details.
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/app/api/webhooks/casdoor/__tests__/route.test.ts +60 -0
- package/src/app/api/webhooks/casdoor/route.ts +40 -0
- package/src/app/api/webhooks/casdoor/validateRequest.ts +38 -0
- package/src/config/auth.ts +6 -0
- package/src/libs/next-auth/sso-providers/casdoor.ts +49 -0
- package/src/libs/next-auth/sso-providers/index.ts +13 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.19.12](https://github.com/lobehub/lobe-chat/compare/v1.19.11...v1.19.12)
|
6
|
+
|
7
|
+
<sup>Released on **2024-09-20**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Support webhooks for casdoor.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Support webhooks for casdoor, closes [#3942](https://github.com/lobehub/lobe-chat/issues/3942) ([1f2f6a5](https://github.com/lobehub/lobe-chat/commit/1f2f6a5))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.19.11](https://github.com/lobehub/lobe-chat/compare/v1.19.10...v1.19.11)
|
6
31
|
|
7
32
|
<sup>Released on **2024-09-20**</sup>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.19.
|
3
|
+
"version": "1.19.12",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
2
|
+
|
3
|
+
interface User {
|
4
|
+
name: string;
|
5
|
+
id: string;
|
6
|
+
type: 'normal-user' | 'admin' | 'super-admin';
|
7
|
+
displayName: string;
|
8
|
+
firstName: string;
|
9
|
+
lastName: string;
|
10
|
+
avatar: string;
|
11
|
+
email: string;
|
12
|
+
emailVerified: boolean;
|
13
|
+
}
|
14
|
+
|
15
|
+
interface UserDataUpdatedEvent {
|
16
|
+
user: string; // 用户名
|
17
|
+
action: 'update-user';
|
18
|
+
extendedUser: User; // 扩展用户信息
|
19
|
+
}
|
20
|
+
|
21
|
+
const userDataUpdatedEvent: UserDataUpdatedEvent = {
|
22
|
+
user: 'admin',
|
23
|
+
action: 'update-user',
|
24
|
+
extendedUser: {
|
25
|
+
name: 'admin',
|
26
|
+
id: '35edace3-00c6-41e1-895e-97c519b1d8cc',
|
27
|
+
type: 'normal-user',
|
28
|
+
displayName: 'Admin',
|
29
|
+
firstName: '',
|
30
|
+
lastName: '',
|
31
|
+
avatar: 'https://cdn.casbin.org/img/casbin.svg',
|
32
|
+
email: 'admin@example.cn',
|
33
|
+
emailVerified: false,
|
34
|
+
},
|
35
|
+
};
|
36
|
+
|
37
|
+
const AUTH_CASDOOR_WEBHOOK_SECRET = 'casdoor-secret';
|
38
|
+
|
39
|
+
// Test Casdoor Webhooks in Local dev, here is some tips:
|
40
|
+
// - Replace the var `AUTH_CASDOOR_WETHOOK_SECRET` with the actual value in your `.env` file
|
41
|
+
// - Start web request: If you want to run the test, replace `describe.skip` with `describe` below
|
42
|
+
// - Run this test with command:
|
43
|
+
// pnpm vitest --run --testNamePattern='^ ?Test Casdoor Webhooks in Local dev' src/app/api/webhooks/casdoor/__tests__/route.test.ts
|
44
|
+
|
45
|
+
describe.skip('Test Casdoor Webhooks in Local dev', () => {
|
46
|
+
// describe('Test Casdoor Webhooks in Local dev', () => {
|
47
|
+
it('should send a POST request with casdoor headers', async () => {
|
48
|
+
const url = 'http://localhost:3010/api/webhooks/casdoor'; // 替换为目标URL
|
49
|
+
const data = userDataUpdatedEvent;
|
50
|
+
const response = await fetch(url, {
|
51
|
+
method: 'POST',
|
52
|
+
headers: {
|
53
|
+
'Content-Type': 'application/json',
|
54
|
+
'casdoor-secret': AUTH_CASDOOR_WEBHOOK_SECRET,
|
55
|
+
},
|
56
|
+
body: JSON.stringify(data),
|
57
|
+
});
|
58
|
+
expect(response.status).toBe(200); // 检查响应状态
|
59
|
+
});
|
60
|
+
});
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { NextResponse } from 'next/server';
|
2
|
+
|
3
|
+
import { authEnv } from '@/config/auth';
|
4
|
+
import { pino } from '@/libs/logger';
|
5
|
+
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
6
|
+
|
7
|
+
import { validateRequest } from './validateRequest';
|
8
|
+
|
9
|
+
export const POST = async (req: Request): Promise<NextResponse> => {
|
10
|
+
const payload = await validateRequest(req, authEnv.CASDOOR_WEBHOOK_SECRET);
|
11
|
+
|
12
|
+
if (!payload) {
|
13
|
+
return NextResponse.json(
|
14
|
+
{ error: 'webhook verification failed or payload was malformed' },
|
15
|
+
{ status: 400 },
|
16
|
+
);
|
17
|
+
}
|
18
|
+
|
19
|
+
const { action, extendedUser } = payload;
|
20
|
+
|
21
|
+
pino.trace(`casdoor webhook payload: ${{ action, extendedUser }}`);
|
22
|
+
|
23
|
+
const nextAuthUserService = new NextAuthUserService();
|
24
|
+
switch (action) {
|
25
|
+
case 'update-user': {
|
26
|
+
return nextAuthUserService.safeUpdateUser(extendedUser.id, {
|
27
|
+
avatar: extendedUser?.avatar,
|
28
|
+
email: extendedUser?.email,
|
29
|
+
fullName: extendedUser.displayName,
|
30
|
+
});
|
31
|
+
}
|
32
|
+
|
33
|
+
default: {
|
34
|
+
pino.warn(
|
35
|
+
`${req.url} received event type "${action}", but no handler is defined for this type`,
|
36
|
+
);
|
37
|
+
return NextResponse.json({ error: `unrecognised payload type: ${action}` }, { status: 400 });
|
38
|
+
}
|
39
|
+
}
|
40
|
+
};
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { headers } from 'next/headers';
|
2
|
+
|
3
|
+
import { authEnv } from '@/config/auth';
|
4
|
+
|
5
|
+
export type CasdoorUserEntity = {
|
6
|
+
avatar?: string;
|
7
|
+
displayName: string;
|
8
|
+
email?: string;
|
9
|
+
id: string;
|
10
|
+
};
|
11
|
+
|
12
|
+
interface CasdoorWebhookPayload {
|
13
|
+
action: string;
|
14
|
+
// Only support user event currently
|
15
|
+
extendedUser: CasdoorUserEntity;
|
16
|
+
}
|
17
|
+
|
18
|
+
export const validateRequest = async (request: Request, secret?: string) => {
|
19
|
+
const payloadString = await request.text();
|
20
|
+
const headerPayload = headers();
|
21
|
+
const casdoorSecret = headerPayload.get('casdoor-secret')!;
|
22
|
+
try {
|
23
|
+
if (casdoorSecret === secret) {
|
24
|
+
return JSON.parse(payloadString) as CasdoorWebhookPayload;
|
25
|
+
} else {
|
26
|
+
console.warn(
|
27
|
+
'[Casdoor]: secret verify failed, please check your secret in `CASDOOR_WEBHOOK_SECRET`',
|
28
|
+
);
|
29
|
+
return;
|
30
|
+
}
|
31
|
+
} catch (e) {
|
32
|
+
if (!authEnv.CASDOOR_WEBHOOK_SECRET) {
|
33
|
+
throw new Error('`CASDOOR_WEBHOOK_SECRET` environment variable is missing.');
|
34
|
+
}
|
35
|
+
console.error('[Casdoor]: incoming webhook failed in verification.\n', e);
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
};
|
package/src/config/auth.ts
CHANGED
@@ -201,6 +201,9 @@ export const getAuthConfig = () => {
|
|
201
201
|
LOGTO_CLIENT_SECRET: z.string().optional(),
|
202
202
|
LOGTO_ISSUER: z.string().optional(),
|
203
203
|
LOGTO_WEBHOOK_SIGNING_KEY: z.string().optional(),
|
204
|
+
|
205
|
+
// Casdoor
|
206
|
+
CASDOOR_WEBHOOK_SECRET: z.string().optional(),
|
204
207
|
},
|
205
208
|
|
206
209
|
runtimeEnv: {
|
@@ -259,6 +262,9 @@ export const getAuthConfig = () => {
|
|
259
262
|
LOGTO_CLIENT_SECRET: process.env.LOGTO_CLIENT_SECRET,
|
260
263
|
LOGTO_ISSUER: process.env.LOGTO_ISSUER,
|
261
264
|
LOGTO_WEBHOOK_SIGNING_KEY: process.env.LOGTO_WEBHOOK_SIGNING_KEY,
|
265
|
+
|
266
|
+
// Casdoor
|
267
|
+
CASDOOR_WEBHOOK_SECRET: process.env.CASDOOR_WEBHOOK_SECRET,
|
262
268
|
},
|
263
269
|
});
|
264
270
|
};
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { OIDCConfig, OIDCUserConfig } from '@auth/core/providers';
|
2
|
+
|
3
|
+
import { CommonProviderConfig } from './sso.config';
|
4
|
+
|
5
|
+
interface CasdoorProfile extends Record<string, any> {
|
6
|
+
avatar: string;
|
7
|
+
displayName: string;
|
8
|
+
email: string;
|
9
|
+
emailVerified: boolean;
|
10
|
+
firstName: string;
|
11
|
+
id: string;
|
12
|
+
lastName: string;
|
13
|
+
name: string;
|
14
|
+
owner: string;
|
15
|
+
permanentAvatar: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
function LobeCasdoorProvider(config: OIDCUserConfig<CasdoorProfile>): OIDCConfig<CasdoorProfile> {
|
19
|
+
return {
|
20
|
+
...CommonProviderConfig,
|
21
|
+
...config,
|
22
|
+
id: 'casdoor',
|
23
|
+
name: 'Casdoor',
|
24
|
+
profile(profile) {
|
25
|
+
return {
|
26
|
+
email: profile.email,
|
27
|
+
emailVerified: profile.emailVerified ? new Date() : null,
|
28
|
+
image: profile.avatar,
|
29
|
+
name: profile.displayName ?? profile.firstName ?? profile.lastName,
|
30
|
+
providerAccountId: profile.id,
|
31
|
+
};
|
32
|
+
},
|
33
|
+
type: 'oidc',
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
const provider = {
|
38
|
+
id: 'casdoor',
|
39
|
+
provider: LobeCasdoorProvider({
|
40
|
+
authorization: {
|
41
|
+
params: { scope: 'openid profile email' },
|
42
|
+
},
|
43
|
+
clientId: process.env.AUTH_CASDOOR_ID,
|
44
|
+
clientSecret: process.env.AUTH_CASDOOR_SECRET,
|
45
|
+
issuer: process.env.AUTH_CASDOOR_ISSUER,
|
46
|
+
}),
|
47
|
+
};
|
48
|
+
|
49
|
+
export default provider;
|
@@ -2,10 +2,22 @@ import Auth0 from './auth0';
|
|
2
2
|
import Authelia from './authelia';
|
3
3
|
import Authentik from './authentik';
|
4
4
|
import AzureAD from './azure-ad';
|
5
|
+
import Casdoor from './casdoor';
|
5
6
|
import CloudflareZeroTrust from './cloudflare-zero-trust';
|
6
7
|
import GenericOIDC from './generic-oidc';
|
7
8
|
import Github from './github';
|
8
9
|
import Logto from './logto';
|
9
10
|
import Zitadel from './zitadel';
|
10
11
|
|
11
|
-
export const ssoProviders = [
|
12
|
+
export const ssoProviders = [
|
13
|
+
Auth0,
|
14
|
+
Authentik,
|
15
|
+
AzureAD,
|
16
|
+
GenericOIDC,
|
17
|
+
Github,
|
18
|
+
Zitadel,
|
19
|
+
Authelia,
|
20
|
+
Logto,
|
21
|
+
CloudflareZeroTrust,
|
22
|
+
Casdoor,
|
23
|
+
];
|