@lobehub/lobehub 2.0.0-next.47 → 2.0.0-next.48

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.
Files changed (228) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +17 -0
  3. package/apps/desktop/src/main/controllers/AuthCtr.ts +27 -2
  4. package/apps/desktop/src/main/core/infrastructure/ProtocolManager.ts +9 -4
  5. package/changelog/v1.json +5 -0
  6. package/docs/development/database-schema.dbml +2 -0
  7. package/docs/self-hosting/environment-variables/basic.mdx +49 -3
  8. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +49 -4
  9. package/locales/ar/discover.json +45 -0
  10. package/locales/ar/marketAuth.json +42 -0
  11. package/locales/ar/setting.json +94 -1
  12. package/locales/bg-BG/discover.json +45 -0
  13. package/locales/bg-BG/marketAuth.json +42 -0
  14. package/locales/bg-BG/setting.json +94 -1
  15. package/locales/de-DE/discover.json +45 -0
  16. package/locales/de-DE/marketAuth.json +42 -0
  17. package/locales/de-DE/setting.json +94 -1
  18. package/locales/en-US/discover.json +45 -0
  19. package/locales/en-US/marketAuth.json +42 -0
  20. package/locales/en-US/setting.json +94 -1
  21. package/locales/es-ES/discover.json +45 -0
  22. package/locales/es-ES/marketAuth.json +42 -0
  23. package/locales/es-ES/setting.json +94 -1
  24. package/locales/fa-IR/discover.json +45 -0
  25. package/locales/fa-IR/marketAuth.json +42 -0
  26. package/locales/fa-IR/setting.json +94 -1
  27. package/locales/fr-FR/discover.json +45 -0
  28. package/locales/fr-FR/marketAuth.json +42 -0
  29. package/locales/fr-FR/setting.json +94 -1
  30. package/locales/it-IT/discover.json +45 -0
  31. package/locales/it-IT/marketAuth.json +42 -0
  32. package/locales/it-IT/setting.json +94 -1
  33. package/locales/ja-JP/discover.json +45 -0
  34. package/locales/ja-JP/marketAuth.json +42 -0
  35. package/locales/ja-JP/setting.json +94 -1
  36. package/locales/ko-KR/discover.json +45 -0
  37. package/locales/ko-KR/marketAuth.json +42 -0
  38. package/locales/ko-KR/setting.json +94 -1
  39. package/locales/nl-NL/discover.json +45 -0
  40. package/locales/nl-NL/marketAuth.json +42 -0
  41. package/locales/nl-NL/setting.json +94 -1
  42. package/locales/pl-PL/discover.json +45 -0
  43. package/locales/pl-PL/marketAuth.json +42 -0
  44. package/locales/pl-PL/setting.json +94 -1
  45. package/locales/pt-BR/discover.json +45 -0
  46. package/locales/pt-BR/marketAuth.json +42 -0
  47. package/locales/pt-BR/setting.json +94 -1
  48. package/locales/ru-RU/discover.json +45 -0
  49. package/locales/ru-RU/marketAuth.json +42 -0
  50. package/locales/ru-RU/setting.json +94 -1
  51. package/locales/tr-TR/discover.json +45 -0
  52. package/locales/tr-TR/marketAuth.json +42 -0
  53. package/locales/tr-TR/setting.json +94 -1
  54. package/locales/vi-VN/discover.json +45 -0
  55. package/locales/vi-VN/marketAuth.json +42 -0
  56. package/locales/vi-VN/setting.json +94 -1
  57. package/locales/zh-CN/discover.json +45 -0
  58. package/locales/zh-CN/marketAuth.json +42 -0
  59. package/locales/zh-CN/setting.json +94 -1
  60. package/locales/zh-TW/discover.json +45 -0
  61. package/locales/zh-TW/marketAuth.json +42 -0
  62. package/locales/zh-TW/setting.json +94 -1
  63. package/package.json +27 -26
  64. package/packages/const/src/url.ts +1 -0
  65. package/packages/database/migrations/0044_add_tool_intervention.sql +1 -0
  66. package/packages/database/migrations/0044_high_toxin.sql +1 -0
  67. package/packages/database/migrations/0045_add_tool_intervention.sql +1 -0
  68. package/packages/database/migrations/meta/0039_snapshot.json +1 -1
  69. package/packages/database/migrations/meta/0044_snapshot.json +7813 -0
  70. package/packages/database/migrations/meta/0045_snapshot.json +8431 -0
  71. package/packages/database/migrations/meta/_journal.json +14 -0
  72. package/packages/database/src/core/migrations.json +36 -7
  73. package/packages/database/src/models/message.ts +1 -1
  74. package/packages/database/src/models/session.ts +42 -1
  75. package/packages/database/src/schemas/agent.ts +1 -0
  76. package/packages/database/src/schemas/message.ts +5 -8
  77. package/packages/electron-client-ipc/src/events/index.ts +6 -1
  78. package/packages/electron-client-ipc/src/events/remoteServer.ts +8 -0
  79. package/packages/fetch-sse/package.json +29 -0
  80. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/fetchSSE.test.ts +4 -4
  81. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/parseError.test.ts +7 -4
  82. package/packages/{utils/src/fetch → fetch-sse/src}/fetchSSE.ts +2 -2
  83. package/packages/{utils/src/fetch → fetch-sse/src}/parseError.ts +3 -3
  84. package/packages/model-bank/src/aiModels/mistral.ts +2 -1
  85. package/packages/model-runtime/src/core/contextBuilders/anthropic.test.ts +17 -11
  86. package/packages/model-runtime/src/core/contextBuilders/anthropic.ts +1 -1
  87. package/packages/model-runtime/src/core/contextBuilders/google.test.ts +1 -1
  88. package/packages/model-runtime/src/core/contextBuilders/google.ts +3 -6
  89. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +4 -2
  90. package/packages/model-runtime/src/core/contextBuilders/openai.ts +1 -1
  91. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.test.ts +1 -1
  92. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  93. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +3 -6
  94. package/packages/model-runtime/src/core/streams/openai/responsesStream.test.ts +1 -1
  95. package/packages/model-runtime/src/helpers/mergeChatMethodOptions.ts +2 -1
  96. package/packages/model-runtime/src/providers/aihubmix/index.test.ts +1 -1
  97. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +1 -1
  98. package/packages/model-runtime/src/providers/anthropic/index.test.ts +1 -1
  99. package/packages/model-runtime/src/providers/baichuan/index.test.ts +1 -1
  100. package/packages/model-runtime/src/providers/bedrock/index.test.ts +1 -1
  101. package/packages/model-runtime/src/providers/bfl/createImage.test.ts +4 -4
  102. package/packages/model-runtime/src/providers/bfl/createImage.ts +1 -1
  103. package/packages/model-runtime/src/providers/cloudflare/index.test.ts +1 -1
  104. package/packages/model-runtime/src/providers/cohere/index.test.ts +1 -1
  105. package/packages/model-runtime/src/providers/google/createImage.test.ts +2 -2
  106. package/packages/model-runtime/src/providers/google/createImage.ts +1 -1
  107. package/packages/model-runtime/src/providers/google/generateObject.test.ts +1 -1
  108. package/packages/model-runtime/src/providers/google/index.test.ts +1 -4
  109. package/packages/model-runtime/src/providers/groq/index.test.ts +1 -1
  110. package/packages/model-runtime/src/providers/hunyuan/index.test.ts +1 -1
  111. package/packages/model-runtime/src/providers/minimax/createImage.test.ts +1 -1
  112. package/packages/model-runtime/src/providers/mistral/index.test.ts +1 -1
  113. package/packages/model-runtime/src/providers/moonshot/index.test.ts +1 -1
  114. package/packages/model-runtime/src/providers/novita/index.test.ts +1 -1
  115. package/packages/model-runtime/src/providers/ollama/index.test.ts +43 -32
  116. package/packages/model-runtime/src/providers/ollama/index.ts +31 -7
  117. package/packages/model-runtime/src/providers/openrouter/index.test.ts +1 -1
  118. package/packages/model-runtime/src/providers/perplexity/index.test.ts +1 -1
  119. package/packages/model-runtime/src/providers/ppio/index.test.ts +1 -1
  120. package/packages/model-runtime/src/providers/qwen/createImage.test.ts +1 -1
  121. package/packages/model-runtime/src/providers/search1api/index.test.ts +1 -1
  122. package/packages/model-runtime/src/providers/siliconcloud/createImage.ts +1 -1
  123. package/packages/model-runtime/src/providers/taichu/index.test.ts +1 -1
  124. package/packages/model-runtime/src/providers/wenxin/index.test.ts +1 -1
  125. package/packages/model-runtime/src/providers/zhipu/index.test.ts +1 -1
  126. package/packages/model-runtime/src/utils/errorResponse.test.ts +1 -1
  127. package/packages/ssrf-safe-fetch/index.browser.ts +14 -0
  128. package/packages/ssrf-safe-fetch/package.json +8 -1
  129. package/packages/types/src/discover/assistants.ts +16 -0
  130. package/packages/types/src/index.ts +1 -0
  131. package/packages/types/src/message/common/tools.ts +10 -0
  132. package/packages/types/src/message/db/item.ts +15 -1
  133. package/packages/types/src/message/ui/params.ts +15 -1
  134. package/packages/types/src/meta.ts +4 -0
  135. package/packages/types/src/session/agentSession.ts +2 -0
  136. package/packages/utils/src/imageToBase64.ts +17 -10
  137. package/packages/utils/src/index.ts +1 -1
  138. package/src/app/(backend)/market/agent/[[...segments]]/route.ts +153 -0
  139. package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +207 -0
  140. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +1 -0
  141. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/PinList/index.tsx +4 -2
  142. package/src/app/[variants]/(main)/chat/settings/features/AgentInfoDescription/index.tsx +349 -0
  143. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -2
  144. package/src/app/[variants]/(main)/chat/settings/features/PublishResultModal/index.tsx +64 -0
  145. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +196 -0
  146. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishModal.tsx +358 -0
  147. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/index.tsx +75 -0
  148. package/src/app/[variants]/(main)/discover/(detail)/assistant/AssistantDetailPage.tsx +11 -2
  149. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/Client.tsx +12 -1
  150. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Nav.tsx +19 -12
  151. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/TagList.tsx +14 -5
  152. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/index.tsx +2 -0
  153. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Related/index.tsx +14 -5
  154. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/TagList.tsx +14 -5
  155. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/index.tsx +43 -29
  156. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Versions/index.tsx +137 -0
  157. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/index.tsx +2 -0
  158. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Header.tsx +9 -10
  159. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/ActionButton/AddAgent.tsx +105 -14
  160. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/Related/index.tsx +20 -6
  161. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/StatusPage/index.tsx +113 -0
  162. package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +4 -3
  163. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +3 -1
  164. package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantPage.tsx +4 -1
  165. package/src/app/[variants]/(main)/discover/(list)/assistant/Client.tsx +6 -2
  166. package/src/app/[variants]/(main)/discover/(list)/assistant/features/Category/index.tsx +7 -3
  167. package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +13 -2
  168. package/src/app/[variants]/(main)/discover/(list)/assistant/features/MarketSourceSwitch.tsx +64 -0
  169. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +26 -7
  170. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +10 -10
  171. package/src/app/[variants]/(main)/settings/_layout/type.ts +1 -1
  172. package/src/app/[variants]/(main)/settings/agent/index.tsx +11 -10
  173. package/src/app/[variants]/(main)/settings/common/index.tsx +1 -1
  174. package/src/app/[variants]/(main)/settings/page.tsx +13 -10
  175. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/Item.tsx +35 -36
  176. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/SearchResult.tsx +5 -5
  177. package/src/app/[variants]/(main)/settings/provider/_layout/Desktop/Container.tsx +10 -4
  178. package/src/app/market-auth-callback/layout.tsx +15 -0
  179. package/src/app/market-auth-callback/page.tsx +196 -0
  180. package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +3 -2
  181. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  182. package/src/features/AgentSetting/store/action.ts +1 -1
  183. package/src/features/ChatInput/ActionBar/STT/browser.tsx +1 -1
  184. package/src/features/ChatInput/ActionBar/STT/openai.tsx +1 -1
  185. package/src/features/Conversation/components/Extras/TTS/InitPlayer.tsx +1 -1
  186. package/src/features/PluginTag/PluginStatus.tsx +1 -1
  187. package/src/hooks/useAgentOwnershipCheck.ts +143 -0
  188. package/src/instrumentation.node.ts +3 -2
  189. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +364 -0
  190. package/src/layout/AuthProvider/MarketAuth/errors.ts +75 -0
  191. package/src/layout/AuthProvider/MarketAuth/index.ts +2 -0
  192. package/src/layout/AuthProvider/MarketAuth/oidc.ts +382 -0
  193. package/src/layout/AuthProvider/MarketAuth/types.ts +64 -0
  194. package/src/layout/AuthProvider/index.tsx +17 -4
  195. package/src/locales/default/discover.ts +46 -0
  196. package/src/locales/default/index.ts +2 -0
  197. package/src/locales/default/marketAuth.ts +42 -0
  198. package/src/locales/default/setting.ts +94 -1
  199. package/src/server/globalConfig/genServerAiProviderConfig.test.ts +5 -5
  200. package/src/server/globalConfig/genServerAiProviderConfig.ts +1 -1
  201. package/src/server/routers/lambda/market/index.ts +36 -14
  202. package/src/server/routers/lambda/message.ts +2 -2
  203. package/src/server/services/discover/index.test.ts +153 -11
  204. package/src/server/services/discover/index.ts +339 -40
  205. package/src/server/sitemap.ts +49 -35
  206. package/src/services/_url.ts +15 -1
  207. package/src/services/chat/chat.test.ts +5 -5
  208. package/src/services/chat/clientModelRuntime.test.ts +1 -1
  209. package/src/services/chat/index.ts +6 -6
  210. package/src/services/chat/types.ts +1 -2
  211. package/src/services/discover.ts +16 -5
  212. package/src/services/electron/remoteServer.ts +8 -1
  213. package/src/services/marketApi.ts +124 -0
  214. package/src/services/models.ts +2 -1
  215. package/src/store/discover/slices/assistant/action.ts +20 -7
  216. package/{packages/utils/src → src/utils}/electron/desktopRemoteRPCFetch.ts +1 -1
  217. package/{packages/utils/src → src/utils/server}/parseModels.ts +1 -2
  218. package/vitest.config.mts +2 -0
  219. package/packages/model-runtime/src/utils/imageToBase64.test.ts +0 -91
  220. package/packages/model-runtime/src/utils/imageToBase64.ts +0 -62
  221. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +0 -98
  222. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/index.tsx +0 -35
  223. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/style.ts +0 -47
  224. /package/packages/{utils/src/fetch → fetch-sse/src}/headers.ts +0 -0
  225. /package/packages/{utils/src/fetch → fetch-sse/src}/index.ts +0 -0
  226. /package/packages/{utils/src/fetch → fetch-sse/src}/request.ts +0 -0
  227. /package/{packages/utils/src → src/utils/server}/__snapshots__/parseModels.test.ts.snap +0 -0
  228. /package/{packages/utils/src → src/utils/server}/parseModels.test.ts +0 -0
@@ -0,0 +1,364 @@
1
+ 'use client';
2
+
3
+ import { ReactNode, createContext, useContext, useEffect, useState } from 'react';
4
+
5
+ import { MARKET_OIDC_ENDPOINTS } from '@/services/_url';
6
+
7
+ import { MarketAuthError } from './errors';
8
+ import { MarketOIDC } from './oidc';
9
+ import { MarketAuthContextType, MarketAuthSession, MarketUserInfo, OIDCConfig } from './types';
10
+
11
+ const MarketAuthContext = createContext<MarketAuthContextType | null>(null);
12
+
13
+ interface MarketAuthProviderProps {
14
+ children: ReactNode;
15
+ isDesktop: boolean;
16
+ }
17
+
18
+ /**
19
+ * 从 cookie 中获取 token
20
+ */
21
+ const getTokenFromCookie = (): string | null => {
22
+ if (typeof document === 'undefined') return null;
23
+
24
+ // eslint-disable-next-line unicorn/no-document-cookie
25
+ const cookies = document.cookie.split(';');
26
+ for (const cookie of cookies) {
27
+ const [name, value] = cookie.trim().split('=');
28
+ if (name === 'market-bearertoken') {
29
+ console.log('[MarketAuth] Found market token in cookie');
30
+ return value;
31
+ }
32
+ }
33
+ return null;
34
+ };
35
+
36
+ /**
37
+ * 将 token 存储到 cookie
38
+ */
39
+ const setTokenToCookie = (token: string, expiresIn: number) => {
40
+ console.log('[MarketAuth] Storing token to cookie');
41
+ const expiresAt = new Date(Date.now() + expiresIn * 1000);
42
+ // eslint-disable-next-line unicorn/no-document-cookie
43
+ document.cookie = `market-bearertoken=${token}; expires=${expiresAt.toUTCString()}; path=/; secure; samesite=strict`;
44
+ };
45
+
46
+ /**
47
+ * 从 cookie 中删除 token
48
+ */
49
+ const removeTokenFromCookie = () => {
50
+ console.log('[MarketAuth] Removing token from cookie');
51
+ // eslint-disable-next-line unicorn/no-document-cookie
52
+ document.cookie = 'market-bearertoken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
53
+ };
54
+
55
+ /**
56
+ * 获取用户信息
57
+ */
58
+ const fetchUserInfo = async (accessToken: string): Promise<MarketUserInfo | null> => {
59
+ try {
60
+ const response = await fetch(MARKET_OIDC_ENDPOINTS.userinfo, {
61
+ body: JSON.stringify({ token: accessToken }),
62
+ headers: {
63
+ 'Content-Type': 'application/json',
64
+ },
65
+ method: 'POST',
66
+ });
67
+
68
+ console.log('[MarketAuth] User info response:', response);
69
+
70
+ if (!response.ok) {
71
+ console.error(
72
+ '[MarketAuth] Failed to fetch user info:',
73
+ response.status,
74
+ response.statusText,
75
+ );
76
+ return null;
77
+ }
78
+
79
+ const userInfo = (await response.json()) as MarketUserInfo;
80
+ console.log('[MarketAuth] User info fetched successfully:', userInfo);
81
+
82
+ return userInfo;
83
+ } catch (error) {
84
+ console.error('[MarketAuth] Error fetching user info:', error);
85
+ return null;
86
+ }
87
+ };
88
+
89
+ /**
90
+ * 刷新令牌(暂时简化,后续可以实现 refresh token 逻辑)
91
+ */
92
+ const refreshToken = async (): Promise<boolean> => {
93
+ console.log('[MarketAuth] Refresh token not implemented yet');
94
+ return false;
95
+ };
96
+
97
+ /**
98
+ * Market 授权上下文提供者
99
+ */
100
+ export const MarketAuthProvider = ({ children, isDesktop }: MarketAuthProviderProps) => {
101
+ const [session, setSession] = useState<MarketAuthSession | null>(null);
102
+ const [status, setStatus] = useState<'loading' | 'authenticated' | 'unauthenticated'>('loading');
103
+ const [oidcClient, setOidcClient] = useState<MarketOIDC | null>(null);
104
+ const [shouldReauthorize, setShouldReauthorize] = useState(false);
105
+
106
+ // 初始化 OIDC 客户端(仅在客户端)
107
+ useEffect(() => {
108
+ if (typeof window !== 'undefined') {
109
+ const baseUrl = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'http://127.0.0.1:8787';
110
+ const desktopRedirectUri = new URL(MARKET_OIDC_ENDPOINTS.desktopCallback, baseUrl).toString();
111
+
112
+ // 桌面端使用 Market 手动维护的 Web 回调,Web 端使用当前域名
113
+ const redirectUri = isDesktop
114
+ ? desktopRedirectUri
115
+ : `${window.location.origin}/market-auth-callback`;
116
+
117
+ const oidcConfig: OIDCConfig = {
118
+ baseUrl,
119
+ clientId: isDesktop ? 'lobehub-desktop' : 'lobechat-com',
120
+ redirectUri,
121
+ scope: 'openid profile email',
122
+ };
123
+ setOidcClient(new MarketOIDC(oidcConfig));
124
+ }
125
+ }, [isDesktop]);
126
+
127
+ /**
128
+ * 检查并恢复会话
129
+ */
130
+ const restoreSession = () => {
131
+ console.log('[MarketAuth] Attempting to restore session');
132
+ const token = getTokenFromCookie();
133
+
134
+ if (token) {
135
+ // 从 sessionStorage 中获取完整的会话信息
136
+ const sessionData = sessionStorage.getItem('market_auth_session');
137
+ if (sessionData) {
138
+ try {
139
+ const parsedSession = JSON.parse(sessionData) as MarketAuthSession;
140
+
141
+ // 检查 token 是否过期
142
+ if (parsedSession.expiresAt > Date.now()) {
143
+ console.log('[MarketAuth] Session restored from storage');
144
+
145
+ console.log('parsedSession', parsedSession);
146
+ console.log('parsedSession.userInfo', parsedSession.userInfo);
147
+ console.log(
148
+ "sessionStorage.getItem('market_user_info')",
149
+ sessionStorage.getItem('market_user_info'),
150
+ );
151
+
152
+ // 如果 session 中没有 userInfo,尝试从单独的存储中获取
153
+ if (!parsedSession.userInfo) {
154
+ const userInfoData = sessionStorage.getItem('market_user_info');
155
+ if (userInfoData) {
156
+ try {
157
+ parsedSession.userInfo = JSON.parse(userInfoData);
158
+ } catch (error) {
159
+ console.error('[MarketAuth] Failed to parse stored user info:', error);
160
+ }
161
+ } else {
162
+ setShouldReauthorize(true);
163
+ }
164
+ }
165
+
166
+ setSession(parsedSession);
167
+ setStatus('authenticated');
168
+ return;
169
+ } else {
170
+ console.log('[MarketAuth] Stored session has expired, will trigger re-authorization');
171
+ sessionStorage.removeItem('market_auth_session');
172
+ removeTokenFromCookie();
173
+ // 标记需要重新授权,等待 oidcClient 准备好
174
+ setShouldReauthorize(true);
175
+ return;
176
+ }
177
+ } catch (error) {
178
+ console.error('[MarketAuth] Failed to parse stored session:', error);
179
+ sessionStorage.removeItem('market_auth_session');
180
+ removeTokenFromCookie();
181
+ }
182
+ }
183
+ }
184
+
185
+ console.log('[MarketAuth] No valid session found');
186
+ setStatus('unauthenticated');
187
+ };
188
+
189
+ /**
190
+ * 登录方法
191
+ */
192
+ const signIn = async (): Promise<number | null> => {
193
+ console.log('[MarketAuth] Starting sign in process');
194
+
195
+ if (!oidcClient) {
196
+ console.error('[MarketAuth] OIDC client not initialized');
197
+ throw new MarketAuthError('oidcNotReady', { message: 'OIDC client not initialized' });
198
+ }
199
+
200
+ try {
201
+ setStatus('loading');
202
+
203
+ // 启动 OIDC 授权流程并获取授权码
204
+ const authResult = await oidcClient.startAuthorization();
205
+ console.log('[MarketAuth] Authorization successful, exchanging code for token', authResult);
206
+
207
+ // 用授权码换取访问令牌
208
+ const tokenResponse = await oidcClient.exchangeCodeForToken(
209
+ authResult.code,
210
+ authResult.state,
211
+ );
212
+
213
+ console.log('[MarketAuth] Token response:', tokenResponse);
214
+
215
+ // 获取用户信息
216
+ const userInfo = await fetchUserInfo(tokenResponse.accessToken);
217
+
218
+ // 创建会话对象
219
+ const newSession: MarketAuthSession = {
220
+ accessToken: tokenResponse.accessToken,
221
+ expiresAt: Date.now() + tokenResponse.expiresIn * 1000,
222
+ expiresIn: tokenResponse.expiresIn,
223
+ scope: tokenResponse.scope,
224
+ tokenType: tokenResponse.tokenType as 'Bearer',
225
+ userInfo: userInfo || undefined,
226
+ };
227
+
228
+ // 存储 token 到 cookie 和 sessionStorage
229
+ setTokenToCookie(tokenResponse.accessToken, tokenResponse.expiresIn);
230
+ sessionStorage.setItem('market_auth_session', JSON.stringify(newSession));
231
+
232
+ // 单独存储用户信息到 sessionStorage 供其他地方使用
233
+ if (userInfo) {
234
+ sessionStorage.setItem('market_user_info', JSON.stringify(userInfo));
235
+ }
236
+
237
+ setSession(newSession);
238
+ setStatus('authenticated');
239
+
240
+ console.log('[MarketAuth] Sign in completed successfully');
241
+ return userInfo?.accountId ?? null;
242
+ } catch (error) {
243
+ console.error('[MarketAuth] Sign in failed:', error);
244
+ setStatus('unauthenticated');
245
+ throw error;
246
+ }
247
+ };
248
+
249
+ /**
250
+ * 登出方法
251
+ */
252
+ const signOut = () => {
253
+ setSession(null);
254
+ setStatus('unauthenticated');
255
+ removeTokenFromCookie();
256
+ sessionStorage.removeItem('market_auth_session');
257
+ sessionStorage.removeItem('market_user_info');
258
+ };
259
+
260
+ /**
261
+ * 获取当前用户信息
262
+ */
263
+ const getCurrentUserInfo = (): MarketUserInfo | null => {
264
+ console.log('getCurrentUserInfo-session', session, session?.userInfo);
265
+ if (session?.userInfo) {
266
+ return session.userInfo;
267
+ }
268
+
269
+ // 如果 session 中没有,尝试从 sessionStorage 中获取
270
+ try {
271
+ const userInfoData = sessionStorage.getItem('market_user_info');
272
+ if (userInfoData) {
273
+ return JSON.parse(userInfoData) as MarketUserInfo;
274
+ }
275
+ } catch (error) {
276
+ console.error('[MarketAuth] Failed to get user info from storage:', error);
277
+ }
278
+
279
+ return null;
280
+ };
281
+
282
+ /**
283
+ * 初始化时恢复会话
284
+ */
285
+ useEffect(() => {
286
+ restoreSession();
287
+ }, []);
288
+
289
+ /**
290
+ * 当需要重新授权且 OIDC 客户端准备好时,自动触发重新授权
291
+ */
292
+ useEffect(() => {
293
+ const handleAutoReauthorization = async () => {
294
+ if (shouldReauthorize && oidcClient) {
295
+ setShouldReauthorize(false); // 重置标识,避免重复触发
296
+ try {
297
+ setStatus('loading');
298
+ // 启动 OIDC 授权流程并获取授权码
299
+ const authResult = await oidcClient.startAuthorization();
300
+ // 用授权码换取访问令牌
301
+ const tokenResponse = await oidcClient.exchangeCodeForToken(
302
+ authResult.code,
303
+ authResult.state,
304
+ );
305
+
306
+ // 获取用户信息
307
+ const userInfo = await fetchUserInfo(tokenResponse.accessToken);
308
+ // 创建会话对象
309
+ const newSession: MarketAuthSession = {
310
+ accessToken: tokenResponse.accessToken,
311
+ expiresAt: Date.now() + tokenResponse.expiresIn * 1000,
312
+ expiresIn: tokenResponse.expiresIn,
313
+ scope: tokenResponse.scope,
314
+ tokenType: tokenResponse.tokenType as 'Bearer',
315
+ userInfo: userInfo || undefined,
316
+ };
317
+
318
+ // 存储 token 到 cookie 和 sessionStorage
319
+ setTokenToCookie(tokenResponse.accessToken, tokenResponse.expiresIn);
320
+ sessionStorage.setItem('market_auth_session', JSON.stringify(newSession));
321
+
322
+ // 单独存储用户信息到 sessionStorage 供其他地方使用
323
+ if (userInfo) {
324
+ sessionStorage.setItem('market_user_info', JSON.stringify(userInfo));
325
+ }
326
+
327
+ setSession(newSession);
328
+ setStatus('authenticated');
329
+
330
+ console.log('[MarketAuth] Auto re-authorization completed successfully');
331
+ } catch (error) {
332
+ console.error('[MarketAuth] Auto re-authorization failed:', error);
333
+ setStatus('unauthenticated');
334
+ }
335
+ }
336
+ };
337
+
338
+ handleAutoReauthorization();
339
+ }, [shouldReauthorize, oidcClient]);
340
+
341
+ const contextValue: MarketAuthContextType = {
342
+ getCurrentUserInfo,
343
+ isAuthenticated: status === 'authenticated',
344
+ isLoading: status === 'loading',
345
+ refreshToken,
346
+ session,
347
+ signIn,
348
+ signOut,
349
+ status,
350
+ };
351
+
352
+ return <MarketAuthContext.Provider value={contextValue}>{children}</MarketAuthContext.Provider>;
353
+ };
354
+
355
+ /**
356
+ * 使用 Market 授权的 Hook
357
+ */
358
+ export const useMarketAuth = (): MarketAuthContextType => {
359
+ const context = useContext(MarketAuthContext);
360
+ if (!context) {
361
+ throw new Error('useMarketAuth must be used within a MarketAuthProvider');
362
+ }
363
+ return context;
364
+ };
@@ -0,0 +1,75 @@
1
+ export type MarketAuthErrorCode =
2
+ | 'authorizationFailed'
3
+ | 'browserOnly'
4
+ | 'codeConsumed'
5
+ | 'codeVerifierMissing'
6
+ | 'general'
7
+ | 'handoffFailed'
8
+ | 'handoffTimeout'
9
+ | 'oidcNotReady'
10
+ | 'openBrowserFailed'
11
+ | 'openPopupFailed'
12
+ | 'popupClosed'
13
+ | 'sessionExpired'
14
+ | 'stateMissing'
15
+ | 'stateMismatch';
16
+
17
+ interface MarketAuthErrorOptions {
18
+ cause?: unknown;
19
+ message?: string;
20
+ meta?: Record<string, unknown>;
21
+ }
22
+
23
+ export class MarketAuthError extends Error {
24
+ public readonly code: MarketAuthErrorCode;
25
+
26
+ public readonly meta?: Record<string, unknown>;
27
+
28
+ constructor(code: MarketAuthErrorCode, options: MarketAuthErrorOptions = {}) {
29
+ super(options.message ?? code);
30
+ this.name = 'MarketAuthError';
31
+ this.code = code;
32
+ this.meta = options.meta;
33
+
34
+ if (options.cause) {
35
+ this.cause = options.cause;
36
+ }
37
+ }
38
+ }
39
+
40
+ const PATTERN_CODE_MAP: Array<[RegExp, MarketAuthErrorCode]> = [
41
+ [/authorization can only be initiated in a browser environment/i, 'browserOnly'],
42
+ [/authorization code already consumed/i, 'codeConsumed'],
43
+ [/authorization popup was closed/i, 'popupClosed'],
44
+ [/authorization session expired/i, 'sessionExpired'],
45
+ [/authorization state not found/i, 'stateMissing'],
46
+ [/state mismatch/i, 'stateMismatch'],
47
+ [/code verifier not found/i, 'codeVerifierMissing'],
48
+ [/failed to open authorization popup/i, 'openPopupFailed'],
49
+ [/failed to open system browser/i, 'openBrowserFailed'],
50
+ [/oidc client not initialized/i, 'oidcNotReady'],
51
+ [/failed to retrieve authorization result from handoff endpoint/i, 'handoffFailed'],
52
+ [/invalid state parameter/i, 'stateMismatch'],
53
+ [/timeout/i, 'handoffTimeout'],
54
+ ];
55
+
56
+ export const resolveMarketAuthError = (error: unknown): MarketAuthError => {
57
+ if (error instanceof MarketAuthError) {
58
+ return error;
59
+ }
60
+
61
+ if (error instanceof Error) {
62
+ const matchedPattern = PATTERN_CODE_MAP.find(([pattern]) => pattern.test(error.message));
63
+
64
+ if (matchedPattern) {
65
+ return new MarketAuthError(matchedPattern[1], { cause: error });
66
+ }
67
+
68
+ return new MarketAuthError('authorizationFailed', {
69
+ cause: error,
70
+ meta: { message: error.message },
71
+ });
72
+ }
73
+
74
+ return new MarketAuthError('general');
75
+ };
@@ -0,0 +1,2 @@
1
+ export { MarketAuthProvider, useMarketAuth } from './MarketAuthProvider';
2
+ export type { MarketAuthContextType, MarketAuthSession, MarketAuthState } from './types';