@lobehub/lobehub 2.0.0-next.225 → 2.0.0-next.226
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 +25 -0
- package/changelog/v1.json +9 -0
- package/locales/en-US/setting.json +4 -0
- package/locales/zh-CN/setting.json +4 -0
- package/package.json +2 -2
- package/src/app/[variants]/(main)/chat/profile/features/Header/AgentPublishButton/ForkConfirmModal.tsx +67 -0
- package/src/app/[variants]/(main)/chat/profile/features/Header/AgentPublishButton/PublishButton.tsx +92 -105
- package/src/app/[variants]/(main)/chat/profile/features/Header/AgentPublishButton/index.tsx +13 -48
- package/src/app/[variants]/(main)/chat/profile/features/Header/AgentPublishButton/useMarketPublish.ts +69 -93
- package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +11 -28
- package/src/layout/AuthProvider/MarketAuth/ProfileSetupModal.tsx +30 -25
- package/src/layout/AuthProvider/MarketAuth/useMarketUserProfile.ts +4 -9
- package/src/libs/trpc/lambda/middleware/index.ts +1 -0
- package/src/libs/trpc/lambda/middleware/marketSDK.ts +68 -0
- package/src/locales/default/setting.ts +5 -0
- package/src/server/routers/lambda/market/agent.ts +504 -0
- package/src/server/routers/lambda/market/index.ts +17 -0
- package/src/server/routers/lambda/market/oidc.ts +169 -0
- package/src/server/routers/lambda/market/social.ts +532 -0
- package/src/server/routers/lambda/market/user.ts +123 -0
- package/src/services/marketApi.ts +24 -84
- package/src/services/social.ts +70 -166
|
@@ -3,32 +3,45 @@ import { useCallback, useRef, useState } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
5
|
import { message } from '@/components/AntdStaticMethods';
|
|
6
|
-
import { checkOwnership } from '@/hooks/useAgentOwnershipCheck';
|
|
7
6
|
import { useTokenCount } from '@/hooks/useTokenCount';
|
|
8
7
|
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
|
|
9
|
-
import {
|
|
8
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
10
9
|
import { useAgentStore } from '@/store/agent';
|
|
11
10
|
import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
|
|
12
11
|
import { useGlobalStore } from '@/store/global';
|
|
13
12
|
import { globalGeneralSelectors } from '@/store/global/selectors';
|
|
14
|
-
import { useServerConfigStore } from '@/store/serverConfig';
|
|
15
|
-
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
16
13
|
|
|
17
14
|
import type { MarketPublishAction } from './types';
|
|
18
|
-
import { generateDefaultChangelog
|
|
15
|
+
import { generateDefaultChangelog } from './utils';
|
|
16
|
+
|
|
17
|
+
export interface OriginalAgentInfo {
|
|
18
|
+
author?: {
|
|
19
|
+
avatar?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
userName?: string;
|
|
22
|
+
};
|
|
23
|
+
avatar?: string;
|
|
24
|
+
identifier: string;
|
|
25
|
+
name: string;
|
|
26
|
+
}
|
|
19
27
|
|
|
20
28
|
interface UseMarketPublishOptions {
|
|
21
29
|
action: MarketPublishAction;
|
|
22
30
|
onSuccess?: (identifier: string) => void;
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
export interface CheckOwnershipResult {
|
|
34
|
+
needsForkConfirm: boolean;
|
|
35
|
+
originalAgent: OriginalAgentInfo | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions) => {
|
|
26
39
|
const { t } = useTranslation('setting');
|
|
27
40
|
const [isPublishing, setIsPublishing] = useState(false);
|
|
41
|
+
const [isCheckingOwnership, setIsCheckingOwnership] = useState(false);
|
|
28
42
|
// 使用 ref 来同步跟踪发布状态,避免闭包导致的竞态问题
|
|
29
43
|
const isPublishingRef = useRef(false);
|
|
30
|
-
const { isAuthenticated
|
|
31
|
-
const enableMarketTrustedClient = useServerConfigStore(serverConfigSelectors.enableMarketTrustedClient);
|
|
44
|
+
const { isAuthenticated } = useMarketAuth();
|
|
32
45
|
|
|
33
46
|
// Agent data from store
|
|
34
47
|
const meta = useAgentStore(agentSelectors.currentAgentMeta, isEqual);
|
|
@@ -46,15 +59,49 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
46
59
|
|
|
47
60
|
const isSubmit = action === 'submit';
|
|
48
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Check ownership before publishing
|
|
64
|
+
* Returns whether fork confirmation is needed and original agent info
|
|
65
|
+
*/
|
|
66
|
+
const checkOwnership = useCallback(async (): Promise<CheckOwnershipResult> => {
|
|
67
|
+
const identifier = meta?.marketIdentifier;
|
|
68
|
+
|
|
69
|
+
// No identifier means new agent, no need to check
|
|
70
|
+
if (!identifier) {
|
|
71
|
+
return { needsForkConfirm: false, originalAgent: null };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
setIsCheckingOwnership(true);
|
|
76
|
+
const result = await lambdaClient.market.agent.checkOwnership.query({ identifier });
|
|
77
|
+
|
|
78
|
+
// If agent doesn't exist or user is owner, no confirmation needed
|
|
79
|
+
if (!result.exists || result.isOwner) {
|
|
80
|
+
return { needsForkConfirm: false, originalAgent: null };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// User is not owner, need fork confirmation
|
|
84
|
+
return {
|
|
85
|
+
needsForkConfirm: true,
|
|
86
|
+
originalAgent: result.originalAgent as OriginalAgentInfo,
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('[useMarketPublish] Failed to check ownership:', error);
|
|
90
|
+
// On error, proceed without confirmation
|
|
91
|
+
return { needsForkConfirm: false, originalAgent: null };
|
|
92
|
+
} finally {
|
|
93
|
+
setIsCheckingOwnership(false);
|
|
94
|
+
}
|
|
95
|
+
}, [meta?.marketIdentifier]);
|
|
96
|
+
|
|
49
97
|
const publish = useCallback(async () => {
|
|
50
98
|
// 防止重复发布:使用 ref 同步检查,避免闭包导致的竞态问题
|
|
51
99
|
if (isPublishingRef.current) {
|
|
52
100
|
return { success: false };
|
|
53
101
|
}
|
|
54
102
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
if (!isAuthenticated || (!enableMarketTrustedClient && !session?.accessToken)) {
|
|
103
|
+
// 检查认证状态 - tRPC 会自动处理 trustedClient
|
|
104
|
+
if (!isAuthenticated) {
|
|
58
105
|
return { success: false };
|
|
59
106
|
}
|
|
60
107
|
|
|
@@ -63,8 +110,6 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
63
110
|
? t('marketPublish.modal.loading.submit')
|
|
64
111
|
: t('marketPublish.modal.loading.upload');
|
|
65
112
|
|
|
66
|
-
let identifier = meta?.marketIdentifier;
|
|
67
|
-
|
|
68
113
|
const changelog = generateDefaultChangelog();
|
|
69
114
|
|
|
70
115
|
try {
|
|
@@ -72,61 +117,9 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
72
117
|
isPublishingRef.current = true;
|
|
73
118
|
setIsPublishing(true);
|
|
74
119
|
message.loading({ content: loadingMessage, key: messageKey });
|
|
75
|
-
// 只有在非 trustedClient 模式下才需要设置 accessToken
|
|
76
|
-
if (session?.accessToken) {
|
|
77
|
-
marketApiService.setAccessToken(session.accessToken);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 判断是否需要创建新 agent
|
|
81
|
-
let needsCreateAgent = false;
|
|
82
|
-
|
|
83
|
-
if (!identifier) {
|
|
84
|
-
// 没有 marketIdentifier,需要创建新 agent
|
|
85
|
-
needsCreateAgent = true;
|
|
86
|
-
} else if (isSubmit) {
|
|
87
|
-
// 有 marketIdentifier 且是 submit 操作,需要检查是否是自己的 agent
|
|
88
|
-
const userInfo = getCurrentUserInfo?.() ?? session?.userInfo;
|
|
89
|
-
const accountId = userInfo?.accountId;
|
|
90
|
-
|
|
91
|
-
if (accountId) {
|
|
92
|
-
const isOwner = await checkOwnership({
|
|
93
|
-
accessToken: session?.accessToken,
|
|
94
|
-
accountId,
|
|
95
|
-
enableMarketTrustedClient,
|
|
96
|
-
marketIdentifier: identifier,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!isOwner) {
|
|
100
|
-
// 不是自己的 agent,需要创建新的
|
|
101
|
-
needsCreateAgent = true;
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
// 无法获取用户 ID,为安全起见创建新 agent
|
|
105
|
-
needsCreateAgent = true;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
120
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
await marketApiService.getAgentDetail(identifier);
|
|
114
|
-
} catch {
|
|
115
|
-
const createPayload: Record<string, unknown> = {
|
|
116
|
-
identifier,
|
|
117
|
-
name: meta?.title || '',
|
|
118
|
-
};
|
|
119
|
-
await marketApiService.createAgent(createPayload as any);
|
|
120
|
-
}
|
|
121
|
-
} else if (!identifier) {
|
|
122
|
-
message.error({
|
|
123
|
-
content: t('marketPublish.modal.messages.missingIdentifier'),
|
|
124
|
-
key: messageKey,
|
|
125
|
-
});
|
|
126
|
-
return { success: false };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const versionPayload = {
|
|
121
|
+
// 使用 tRPC publishOrCreate - 后端会自动处理 ownership 检查
|
|
122
|
+
const result = await lambdaClient.market.agent.publishOrCreate.mutate({
|
|
130
123
|
avatar: meta?.avatar,
|
|
131
124
|
changelog,
|
|
132
125
|
config: {
|
|
@@ -157,31 +150,16 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
157
150
|
},
|
|
158
151
|
description: meta?.description || '',
|
|
159
152
|
editorData: editorData,
|
|
160
|
-
identifier
|
|
153
|
+
// 传递现有的 identifier,后端会检查 ownership
|
|
154
|
+
identifier: meta?.marketIdentifier,
|
|
161
155
|
name: meta?.title || '',
|
|
162
156
|
tags: meta?.tags,
|
|
163
157
|
tokenUsage: tokenUsage,
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
await marketApiService.createAgentVersion(versionPayload);
|
|
168
|
-
} catch (versionError) {
|
|
169
|
-
const errorMessage =
|
|
170
|
-
versionError instanceof Error
|
|
171
|
-
? versionError.message
|
|
172
|
-
: t('unknownError', { ns: 'common' });
|
|
173
|
-
message.error({
|
|
174
|
-
content: t('marketPublish.modal.messages.createVersionFailed', {
|
|
175
|
-
message: errorMessage,
|
|
176
|
-
}),
|
|
177
|
-
key: messageKey,
|
|
178
|
-
});
|
|
179
|
-
return { success: false };
|
|
180
|
-
}
|
|
158
|
+
});
|
|
181
159
|
|
|
182
|
-
//
|
|
183
|
-
if (
|
|
184
|
-
updateAgentMeta({ marketIdentifier: identifier });
|
|
160
|
+
// 如果是新创建的 agent,需要更新 meta
|
|
161
|
+
if (result.isNewAgent) {
|
|
162
|
+
updateAgentMeta({ marketIdentifier: result.identifier });
|
|
185
163
|
}
|
|
186
164
|
|
|
187
165
|
message.success({
|
|
@@ -189,8 +167,8 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
189
167
|
key: messageKey,
|
|
190
168
|
});
|
|
191
169
|
|
|
192
|
-
onSuccess?.(identifier
|
|
193
|
-
return { identifier, success: true };
|
|
170
|
+
onSuccess?.(result.identifier);
|
|
171
|
+
return { identifier: result.identifier, success: true };
|
|
194
172
|
} catch (error) {
|
|
195
173
|
const errorMessage =
|
|
196
174
|
error instanceof Error ? error.message : t('unknownError', { ns: 'common' });
|
|
@@ -211,8 +189,6 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
211
189
|
chatConfig?.historyCount,
|
|
212
190
|
chatConfig?.searchMode,
|
|
213
191
|
editorData,
|
|
214
|
-
enableMarketTrustedClient,
|
|
215
|
-
getCurrentUserInfo,
|
|
216
192
|
isAuthenticated,
|
|
217
193
|
isSubmit,
|
|
218
194
|
language,
|
|
@@ -225,8 +201,6 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
225
201
|
onSuccess,
|
|
226
202
|
plugins,
|
|
227
203
|
provider,
|
|
228
|
-
session?.accessToken,
|
|
229
|
-
session?.userInfo,
|
|
230
204
|
systemRole,
|
|
231
205
|
tokenUsage,
|
|
232
206
|
t,
|
|
@@ -234,6 +208,8 @@ export const useMarketPublish = ({ action, onSuccess }: UseMarketPublishOptions)
|
|
|
234
208
|
]);
|
|
235
209
|
|
|
236
210
|
return {
|
|
211
|
+
checkOwnership,
|
|
212
|
+
isCheckingOwnership,
|
|
237
213
|
isPublishing,
|
|
238
214
|
publish,
|
|
239
215
|
};
|
|
@@ -5,7 +5,8 @@ import { type ReactNode, createContext, useCallback, useContext, useEffect, useS
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { mutate as globalMutate } from 'swr';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
9
|
+
import { MARKET_OIDC_ENDPOINTS } from '@/services/_url';
|
|
9
10
|
import { useServerConfigStore } from '@/store/serverConfig';
|
|
10
11
|
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
11
12
|
import { useUserStore } from '@/store/user';
|
|
@@ -32,31 +33,16 @@ interface MarketAuthProviderProps {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* 获取用户信息(通过 tRPC OIDC endpoint)
|
|
36
37
|
* @param accessToken - 可选的 access token,如果不传则后端会尝试使用 trustedClientToken
|
|
37
38
|
*/
|
|
38
39
|
const fetchUserInfo = async (accessToken?: string): Promise<MarketUserInfo | null> => {
|
|
39
40
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
headers: {
|
|
43
|
-
'Content-Type': 'application/json',
|
|
44
|
-
},
|
|
45
|
-
method: 'POST',
|
|
41
|
+
const userInfo = await lambdaClient.market.oidc.getUserInfo.mutate({
|
|
42
|
+
token: accessToken,
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
console.error(
|
|
50
|
-
'[MarketAuth] Failed to fetch user info:',
|
|
51
|
-
response.status,
|
|
52
|
-
response.statusText,
|
|
53
|
-
);
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const userInfo = (await response.json()) as MarketUserInfo;
|
|
58
|
-
|
|
59
|
-
return userInfo;
|
|
45
|
+
return userInfo as MarketUserInfo;
|
|
60
46
|
} catch (error) {
|
|
61
47
|
console.error('[MarketAuth] Error fetching user info:', error);
|
|
62
48
|
return null;
|
|
@@ -136,16 +122,11 @@ const refreshToken = async (): Promise<boolean> => {
|
|
|
136
122
|
*/
|
|
137
123
|
const checkNeedsProfileSetup = async (username: string): Promise<boolean> => {
|
|
138
124
|
try {
|
|
139
|
-
const
|
|
140
|
-
if (!response.ok) {
|
|
141
|
-
// User profile not found, needs setup
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
const profile = (await response.json()) as MarketUserProfile;
|
|
125
|
+
const profile = await lambdaClient.market.user.getUserByUsername.query({ username });
|
|
145
126
|
// If userName is not set, user needs to complete profile setup
|
|
146
127
|
return !profile.userName;
|
|
147
128
|
} catch {
|
|
148
|
-
// Error fetching profile, assume needs setup
|
|
129
|
+
// Error fetching profile (e.g., NOT_FOUND), assume needs setup
|
|
149
130
|
return true;
|
|
150
131
|
}
|
|
151
132
|
};
|
|
@@ -177,7 +158,9 @@ export const MarketAuthProvider = ({ children, isDesktop }: MarketAuthProviderPr
|
|
|
177
158
|
const isUserStateInit = useUserStore((s) => s.isUserStateInit);
|
|
178
159
|
|
|
179
160
|
// 检查是否启用了 Market Trusted Client 认证
|
|
180
|
-
const enableMarketTrustedClient = useServerConfigStore(
|
|
161
|
+
const enableMarketTrustedClient = useServerConfigStore(
|
|
162
|
+
serverConfigSelectors.enableMarketTrustedClient,
|
|
163
|
+
);
|
|
181
164
|
|
|
182
165
|
// 初始化 OIDC 客户端(仅在客户端)
|
|
183
166
|
useEffect(() => {
|
|
@@ -9,7 +9,7 @@ import { memo, useCallback, useEffect, useState } from 'react';
|
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
11
11
|
import EmojiPicker from '@/components/EmojiPicker';
|
|
12
|
-
import {
|
|
12
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
13
13
|
import { useFileStore } from '@/store/file';
|
|
14
14
|
import { useGlobalStore } from '@/store/global';
|
|
15
15
|
import { globalGeneralSelectors } from '@/store/global/selectors';
|
|
@@ -211,37 +211,42 @@ const ProfileSetupModal = memo<ProfileSetupModalProps>(
|
|
|
211
211
|
if (bannerUrl) meta.bannerUrl = bannerUrl;
|
|
212
212
|
if (Object.keys(socialLinks).length > 0) meta.socialLinks = socialLinks;
|
|
213
213
|
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
userName: values.userName,
|
|
220
|
-
}),
|
|
221
|
-
headers: {
|
|
222
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
223
|
-
'Content-Type': 'application/json',
|
|
224
|
-
},
|
|
225
|
-
method: 'PUT',
|
|
214
|
+
const result = await lambdaClient.market.user.updateUserProfile.mutate({
|
|
215
|
+
avatarUrl: avatarUrl || undefined,
|
|
216
|
+
displayName: values.displayName,
|
|
217
|
+
meta: Object.keys(meta).length > 0 ? meta : undefined,
|
|
218
|
+
userName: values.userName,
|
|
226
219
|
});
|
|
227
220
|
|
|
228
|
-
if (!response.ok) {
|
|
229
|
-
const errorData = await response.json();
|
|
230
|
-
if (errorData.error === 'username_taken') {
|
|
231
|
-
message.error(t('profileSetup.errors.usernameTaken'));
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
throw new Error(errorData.message || 'Update failed');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const data = await response.json();
|
|
238
221
|
message.success(t('profileSetup.success'));
|
|
239
|
-
|
|
222
|
+
// Cast result.user to MarketUserProfile with required fields
|
|
223
|
+
const userProfile: MarketUserProfile = {
|
|
224
|
+
avatarUrl: result.user?.avatarUrl || avatarUrl || null,
|
|
225
|
+
bannerUrl: bannerUrl || null,
|
|
226
|
+
createdAt: result.user?.createdAt || new Date().toISOString(),
|
|
227
|
+
description: values.description || null,
|
|
228
|
+
displayName: values.displayName || null,
|
|
229
|
+
id: result.user?.id || 0,
|
|
230
|
+
namespace: result.user?.namespace || '',
|
|
231
|
+
socialLinks: Object.keys(socialLinks).length > 0 ? socialLinks : null,
|
|
232
|
+
type: result.user?.type || null,
|
|
233
|
+
userName: values.userName || null,
|
|
234
|
+
};
|
|
235
|
+
onSuccess?.(userProfile);
|
|
240
236
|
onClose();
|
|
241
237
|
} catch (error) {
|
|
242
238
|
console.error('[ProfileSetupModal] Update failed:', error);
|
|
243
239
|
if (error instanceof Error && error.message !== 'Validation failed') {
|
|
244
|
-
|
|
240
|
+
// Check for username taken error (tRPC CONFLICT code)
|
|
241
|
+
const errorMessage = error.message || '';
|
|
242
|
+
if (
|
|
243
|
+
errorMessage.toLowerCase().includes('already taken') ||
|
|
244
|
+
errorMessage.includes('CONFLICT')
|
|
245
|
+
) {
|
|
246
|
+
message.error(t('profileSetup.errors.usernameTaken'));
|
|
247
|
+
} else {
|
|
248
|
+
message.error(t('profileSetup.errors.updateFailed'));
|
|
249
|
+
}
|
|
245
250
|
}
|
|
246
251
|
} finally {
|
|
247
252
|
setLoading(false);
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import useSWR from 'swr';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
4
4
|
|
|
5
5
|
import { type MarketUserProfile } from './types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Fetcher function for user profile
|
|
8
|
+
* Fetcher function for user profile using tRPC
|
|
9
9
|
*/
|
|
10
10
|
const fetchUserProfile = async (username: string): Promise<MarketUserProfile | null> => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (!response.ok) {
|
|
14
|
-
throw new Error(`Failed to fetch user profile: ${response.status}`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return response.json();
|
|
11
|
+
const result = await lambdaClient.market.user.getUserByUsername.query({ username });
|
|
12
|
+
return result as MarketUserProfile;
|
|
18
13
|
};
|
|
19
14
|
|
|
20
15
|
/**
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MarketSDK } from '@lobehub/market-sdk';
|
|
2
|
+
|
|
3
|
+
import { generateTrustedClientToken, type TrustedClientUserInfo } from '@/libs/trusted-client';
|
|
4
|
+
|
|
5
|
+
import { trpc } from '../init';
|
|
6
|
+
|
|
7
|
+
interface ContextWithMarketUserInfo {
|
|
8
|
+
marketAccessToken?: string;
|
|
9
|
+
marketUserInfo?: TrustedClientUserInfo;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Middleware that initializes MarketSDK with proper authentication.
|
|
14
|
+
* This requires marketUserInfo middleware to be applied first.
|
|
15
|
+
*
|
|
16
|
+
* Provides:
|
|
17
|
+
* - ctx.marketSDK: Initialized MarketSDK instance with trustedClientToken and optional accessToken
|
|
18
|
+
* - ctx.trustedClientToken: The generated trusted client token (if available)
|
|
19
|
+
*/
|
|
20
|
+
export const marketSDK = trpc.middleware(async (opts) => {
|
|
21
|
+
const ctx = opts.ctx as ContextWithMarketUserInfo;
|
|
22
|
+
|
|
23
|
+
// Generate trusted client token if user info is available
|
|
24
|
+
const trustedClientToken = ctx.marketUserInfo
|
|
25
|
+
? generateTrustedClientToken(ctx.marketUserInfo)
|
|
26
|
+
: undefined;
|
|
27
|
+
|
|
28
|
+
// Initialize MarketSDK with both authentication methods
|
|
29
|
+
const market = new MarketSDK({
|
|
30
|
+
accessToken: ctx.marketAccessToken,
|
|
31
|
+
baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
|
|
32
|
+
trustedClientToken,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return opts.next({
|
|
36
|
+
ctx: {
|
|
37
|
+
marketSDK: market,
|
|
38
|
+
trustedClientToken,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Middleware that requires authentication for Market API access.
|
|
45
|
+
* This middleware ensures that either accessToken or trustedClientToken is available.
|
|
46
|
+
* It should be used after marketUserInfo and marketSDK middlewares.
|
|
47
|
+
*
|
|
48
|
+
* Throws UNAUTHORIZED error if neither authentication method is available.
|
|
49
|
+
*/
|
|
50
|
+
export const requireMarketAuth = trpc.middleware(async (opts) => {
|
|
51
|
+
const ctx = opts.ctx as ContextWithMarketUserInfo & {
|
|
52
|
+
trustedClientToken?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Check if any authentication is available
|
|
56
|
+
const hasAccessToken = !!ctx.marketAccessToken;
|
|
57
|
+
const hasTrustedToken = !!ctx.trustedClientToken;
|
|
58
|
+
|
|
59
|
+
if (!hasAccessToken && !hasTrustedToken) {
|
|
60
|
+
const { TRPCError } = await import('@trpc/server');
|
|
61
|
+
throw new TRPCError({
|
|
62
|
+
code: 'UNAUTHORIZED',
|
|
63
|
+
message: 'Authentication required. Please sign in.',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return opts.next();
|
|
68
|
+
});
|
|
@@ -140,6 +140,11 @@ export default {
|
|
|
140
140
|
'llm.proxyUrl.title': 'API proxy URL',
|
|
141
141
|
'llm.waitingForMore': 'More models are <1>planned to be added</1>, stay tuned',
|
|
142
142
|
'llm.waitingForMoreLinkAriaLabel': 'Open the Provider request form',
|
|
143
|
+
'marketPublish.forkConfirm.by': 'by {{author}}',
|
|
144
|
+
'marketPublish.forkConfirm.confirm': 'Confirm Publish',
|
|
145
|
+
'marketPublish.forkConfirm.description':
|
|
146
|
+
'You are about to publish a derivative version based on an existing agent from the community. Your new agent will be created as a separate entry in the marketplace.',
|
|
147
|
+
'marketPublish.forkConfirm.title': 'Publish Derivative Agent',
|
|
143
148
|
'marketPublish.modal.changelog.extra':
|
|
144
149
|
'Describe the key changes and improvements in this version',
|
|
145
150
|
'marketPublish.modal.changelog.label': 'Changelog',
|