@lobehub/lobehub 2.0.0-next.2 → 2.0.0-next.21

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 (603) hide show
  1. package/.env.desktop +1 -2
  2. package/.env.example +0 -3
  3. package/.env.example.development +0 -2
  4. package/.github/workflows/claude-auto-testing.yml +73 -0
  5. package/.github/workflows/claude-translate-comments.yml +67 -0
  6. package/.github/workflows/desktop-pr-build.yml +18 -16
  7. package/.github/workflows/docker.yml +25 -20
  8. package/.github/workflows/e2e.yml +17 -3
  9. package/.github/workflows/release-desktop-beta.yml +12 -12
  10. package/.github/workflows/release.yml +3 -5
  11. package/.github/workflows/test.yml +47 -12
  12. package/.nvmrc +1 -1
  13. package/CHANGELOG.md +484 -0
  14. package/Dockerfile +1 -3
  15. package/README.md +2 -45
  16. package/README.zh-CN.md +2 -45
  17. package/apps/desktop/src/main/controllers/AuthCtr.ts +53 -39
  18. package/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +706 -0
  19. package/apps/desktop/src/main/utils/next-electron-rsc.ts +7 -5
  20. package/apps/desktop/tsconfig.json +0 -1
  21. package/changelog/v1.json +159 -0
  22. package/docs/development/database-schema.dbml +11 -1
  23. package/docs/self-hosting/advanced/auth/next-auth/auth0.mdx +2 -2
  24. package/docs/self-hosting/advanced/auth/next-auth/auth0.zh-CN.mdx +2 -2
  25. package/docs/self-hosting/advanced/auth/next-auth/authelia.mdx +2 -2
  26. package/docs/self-hosting/advanced/auth/next-auth/authelia.zh-CN.mdx +2 -2
  27. package/docs/self-hosting/advanced/auth/next-auth/authentik.mdx +2 -2
  28. package/docs/self-hosting/advanced/auth/next-auth/authentik.zh-CN.mdx +2 -2
  29. package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +2 -2
  30. package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +2 -2
  31. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.mdx +2 -2
  32. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.zh-CN.mdx +2 -2
  33. package/docs/self-hosting/advanced/auth/next-auth/github.mdx +2 -2
  34. package/docs/self-hosting/advanced/auth/next-auth/github.zh-CN.mdx +2 -2
  35. package/docs/self-hosting/advanced/auth/next-auth/google.mdx +32 -29
  36. package/docs/self-hosting/advanced/auth/next-auth/keycloak.mdx +2 -2
  37. package/docs/self-hosting/advanced/auth/next-auth/keycloak.zh-CN.mdx +2 -2
  38. package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +5 -3
  39. package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +5 -3
  40. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.mdx +2 -2
  41. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.zh-CN.mdx +2 -2
  42. package/docs/self-hosting/advanced/auth/next-auth/okta.mdx +2 -2
  43. package/docs/self-hosting/advanced/auth/next-auth/okta.zh-CN.mdx +2 -2
  44. package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +2 -2
  45. package/docs/self-hosting/advanced/auth/next-auth/wechat.zh-CN.mdx +2 -2
  46. package/docs/self-hosting/advanced/auth/next-auth/zitadel.mdx +2 -2
  47. package/docs/self-hosting/advanced/auth/next-auth/zitadel.zh-CN.mdx +2 -2
  48. package/docs/self-hosting/advanced/auth.mdx +32 -21
  49. package/docs/self-hosting/advanced/auth.zh-CN.mdx +30 -19
  50. package/docs/self-hosting/advanced/feature-flags.mdx +0 -1
  51. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +0 -1
  52. package/docs/self-hosting/advanced/online-search.mdx +30 -25
  53. package/docs/self-hosting/advanced/online-search.zh-CN.mdx +25 -23
  54. package/e2e/src/features/discover/smoke.feature +34 -1
  55. package/e2e/src/steps/discover/smoke.steps.ts +116 -4
  56. package/e2e/tsconfig.json +0 -1
  57. package/locales/ar/models.json +15 -6
  58. package/locales/ar/oauth.json +1 -0
  59. package/locales/bg-BG/models.json +15 -6
  60. package/locales/bg-BG/oauth.json +1 -0
  61. package/locales/de-DE/models.json +15 -6
  62. package/locales/de-DE/oauth.json +1 -0
  63. package/locales/en-US/models.json +15 -6
  64. package/locales/en-US/oauth.json +1 -0
  65. package/locales/es-ES/models.json +15 -6
  66. package/locales/es-ES/oauth.json +1 -0
  67. package/locales/fa-IR/models.json +15 -6
  68. package/locales/fa-IR/oauth.json +1 -0
  69. package/locales/fr-FR/models.json +15 -6
  70. package/locales/fr-FR/oauth.json +1 -0
  71. package/locales/it-IT/models.json +15 -6
  72. package/locales/it-IT/oauth.json +1 -0
  73. package/locales/ja-JP/models.json +15 -6
  74. package/locales/ja-JP/oauth.json +1 -0
  75. package/locales/ko-KR/models.json +21 -12
  76. package/locales/ko-KR/oauth.json +1 -0
  77. package/locales/nl-NL/models.json +15 -6
  78. package/locales/nl-NL/oauth.json +1 -0
  79. package/locales/pl-PL/models.json +15 -6
  80. package/locales/pl-PL/oauth.json +1 -0
  81. package/locales/pt-BR/models.json +15 -6
  82. package/locales/pt-BR/oauth.json +1 -0
  83. package/locales/ru-RU/models.json +15 -6
  84. package/locales/ru-RU/oauth.json +1 -0
  85. package/locales/tr-TR/models.json +15 -6
  86. package/locales/tr-TR/oauth.json +1 -0
  87. package/locales/vi-VN/models.json +15 -6
  88. package/locales/vi-VN/oauth.json +1 -0
  89. package/locales/zh-CN/models.json +15 -6
  90. package/locales/zh-CN/oauth.json +1 -0
  91. package/locales/zh-TW/models.json +15 -6
  92. package/locales/zh-TW/oauth.json +1 -0
  93. package/next.config.ts +2 -3
  94. package/package.json +74 -80
  95. package/packages/const/src/index.ts +0 -1
  96. package/packages/const/src/models.ts +13 -0
  97. package/packages/const/src/url.ts +1 -4
  98. package/packages/const/src/version.ts +3 -3
  99. package/packages/context-engine/src/index.ts +1 -6
  100. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
  101. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
  102. package/packages/context-engine/src/providers/index.ts +0 -2
  103. package/packages/database/migrations/0041_improve_index.sql +10 -0
  104. package/packages/database/migrations/meta/0041_snapshot.json +7784 -0
  105. package/packages/database/migrations/meta/_journal.json +7 -0
  106. package/packages/database/package.json +1 -1
  107. package/packages/database/src/core/migrations.json +17 -0
  108. package/packages/database/src/core/web-server.ts +2 -1
  109. package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
  110. package/packages/database/src/models/__tests__/message.test.ts +322 -170
  111. package/packages/database/src/models/message.ts +62 -24
  112. package/packages/database/src/models/session.ts +60 -19
  113. package/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts +2 -1
  114. package/packages/database/src/repositories/dataImporter/deprecated/index.ts +7 -1
  115. package/packages/database/src/schemas/agent.ts +10 -11
  116. package/packages/database/src/schemas/message.ts +5 -1
  117. package/packages/database/src/schemas/relations.ts +6 -4
  118. package/packages/database/src/schemas/session.ts +2 -0
  119. package/packages/database/src/schemas/topic.ts +6 -1
  120. package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
  121. package/packages/database/src/utils/groupMessages.ts +7 -5
  122. package/packages/electron-client-ipc/package.json +4 -1
  123. package/packages/file-loaders/package.json +1 -0
  124. package/packages/memory-extract/package.json +1 -1
  125. package/packages/model-bank/src/aiModels/anthropic.ts +0 -63
  126. package/packages/model-bank/src/aiModels/azure.ts +155 -0
  127. package/packages/model-bank/src/aiModels/bedrock.ts +44 -0
  128. package/packages/model-bank/src/aiModels/higress.ts +0 -55
  129. package/packages/model-bank/src/aiModels/infiniai.ts +21 -0
  130. package/packages/model-bank/src/aiModels/ollamacloud.ts +13 -0
  131. package/packages/model-bank/src/aiModels/siliconcloud.ts +19 -0
  132. package/packages/model-runtime/src/core/parameterResolver.ts +3 -0
  133. package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +0 -38
  134. package/packages/model-runtime/src/providers/azureOpenai/index.ts +2 -1
  135. package/packages/model-runtime/src/providers/minimax/index.ts +5 -5
  136. package/packages/model-runtime/src/providers/search1api/index.test.ts +2 -2
  137. package/packages/model-runtime/src/utils/googleErrorParser.test.ts +125 -0
  138. package/packages/model-runtime/src/utils/googleErrorParser.ts +103 -77
  139. package/packages/types/src/message/common/base.ts +13 -0
  140. package/packages/types/src/message/common/image.ts +8 -0
  141. package/packages/types/src/message/common/metadata.ts +39 -0
  142. package/packages/types/src/message/common/tools.ts +10 -0
  143. package/packages/types/src/message/db/params.ts +47 -1
  144. package/packages/types/src/message/ui/chat.ts +4 -1
  145. package/packages/types/src/message/ui/params.ts +98 -4
  146. package/packages/types/src/search.ts +16 -0
  147. package/packages/types/src/serverConfig.ts +2 -6
  148. package/packages/types/src/user/index.ts +13 -1
  149. package/packages/types/src/user/settings/index.ts +22 -0
  150. package/packages/utils/src/apiKey.test.ts +139 -0
  151. package/packages/utils/src/client/clipboard.ts +2 -2
  152. package/packages/utils/src/client/exportFile.ts +10 -10
  153. package/packages/utils/src/client/parserPlaceholder.ts +18 -18
  154. package/packages/utils/src/client/topic.ts +10 -10
  155. package/packages/utils/src/client/xor-obfuscation.ts +11 -11
  156. package/packages/web-crawler/src/crawImpl/firecrawl.ts +39 -12
  157. package/packages/web-crawler/tsconfig.json +0 -1
  158. package/renovate.json +20 -3
  159. package/scripts/migrateServerDB/errorHint.js +1 -7
  160. package/scripts/migrateServerDB/index.ts +2 -1
  161. package/src/app/(backend)/webapi/revalidate/route.ts +1 -1
  162. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -8
  163. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +9 -0
  164. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -6
  165. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +0 -2
  166. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +1 -4
  167. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
  168. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
  169. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
  170. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +3 -28
  171. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
  172. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +0 -2
  173. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +1 -5
  174. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -62
  175. package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +1 -0
  176. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +1 -1
  177. package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -0
  178. package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -0
  179. package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -0
  180. package/src/app/[variants]/(main)/discover/components/CategoryMenu.tsx +9 -1
  181. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicList.tsx +1 -0
  182. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +1 -1
  183. package/src/app/[variants]/(main)/image/page.tsx +0 -2
  184. package/src/app/[variants]/(main)/labs/components/LabCard.tsx +3 -1
  185. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +23 -24
  186. package/src/app/[variants]/(main)/profile/_layout/Mobile/index.tsx +5 -9
  187. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +0 -2
  188. package/src/app/[variants]/(main)/settings/_layout/Mobile/index.tsx +0 -2
  189. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +0 -3
  190. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +1 -1
  191. package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +5 -7
  192. package/src/app/[variants]/layout.tsx +1 -0
  193. package/src/app/[variants]/loading/index.tsx +1 -10
  194. package/src/app/[variants]/oauth/ResultLayout.tsx +47 -0
  195. package/src/app/[variants]/oauth/callback/error/page.tsx +20 -33
  196. package/src/app/[variants]/oauth/callback/layout.tsx +1 -0
  197. package/src/app/[variants]/oauth/callback/success/page.tsx +8 -22
  198. package/src/app/[variants]/oauth/consent/[uid]/Consent/BuiltinConsent.tsx +47 -0
  199. package/src/app/[variants]/oauth/consent/[uid]/{Consent.tsx → Consent/index.tsx} +12 -1
  200. package/src/app/[variants]/oauth/consent/[uid]/Login.tsx +19 -2
  201. package/src/app/sitemap.tsx +7 -1
  202. package/src/components/InvalidAPIKey/APIKeyForm/Bedrock.tsx +8 -13
  203. package/src/components/Link.tsx +12 -0
  204. package/src/components/Thinking/index.tsx +4 -3
  205. package/src/config/db.ts +0 -5
  206. package/src/config/featureFlags/schema.test.ts +0 -2
  207. package/src/config/featureFlags/schema.ts +0 -6
  208. package/src/config/modelProviders/ai21.ts +1 -16
  209. package/src/config/modelProviders/ai302.ts +1 -128
  210. package/src/config/modelProviders/ai360.ts +1 -32
  211. package/src/config/modelProviders/anthropic.ts +1 -94
  212. package/src/config/modelProviders/azure.ts +1 -51
  213. package/src/config/modelProviders/baichuan.ts +1 -57
  214. package/src/config/modelProviders/bedrock.ts +1 -276
  215. package/src/config/modelProviders/cloudflare.ts +1 -64
  216. package/src/config/modelProviders/deepseek.ts +1 -19
  217. package/src/config/modelProviders/fireworksai.ts +1 -174
  218. package/src/config/modelProviders/giteeai.ts +1 -135
  219. package/src/config/modelProviders/github.ts +1 -254
  220. package/src/config/modelProviders/google.ts +1 -130
  221. package/src/config/modelProviders/groq.ts +1 -119
  222. package/src/config/modelProviders/higress.ts +1 -1736
  223. package/src/config/modelProviders/huggingface.ts +1 -54
  224. package/src/config/modelProviders/hunyuan.ts +1 -83
  225. package/src/config/modelProviders/infiniai.ts +1 -74
  226. package/src/config/modelProviders/internlm.ts +1 -20
  227. package/src/config/modelProviders/minimax.ts +1 -1
  228. package/src/config/modelProviders/mistral.ts +1 -95
  229. package/src/config/modelProviders/modelscope.ts +1 -27
  230. package/src/config/modelProviders/moonshot.ts +1 -29
  231. package/src/config/modelProviders/novita.ts +1 -105
  232. package/src/config/modelProviders/ollama.ts +1 -325
  233. package/src/config/modelProviders/openai.ts +1 -242
  234. package/src/config/modelProviders/openrouter.ts +1 -240
  235. package/src/config/modelProviders/perplexity.ts +1 -45
  236. package/src/config/modelProviders/ppio.ts +1 -152
  237. package/src/config/modelProviders/qiniu.ts +2 -19
  238. package/src/config/modelProviders/qwen.ts +1 -245
  239. package/src/config/modelProviders/search1api.ts +1 -34
  240. package/src/config/modelProviders/sensenova.ts +1 -69
  241. package/src/config/modelProviders/siliconcloud.ts +1 -417
  242. package/src/config/modelProviders/spark.ts +1 -59
  243. package/src/config/modelProviders/stepfun.ts +1 -98
  244. package/src/config/modelProviders/taichu.ts +1 -18
  245. package/src/config/modelProviders/togetherai.ts +1 -274
  246. package/src/config/modelProviders/upstage.ts +1 -28
  247. package/src/config/modelProviders/wenxin.ts +1 -140
  248. package/src/config/modelProviders/xai.ts +1 -38
  249. package/src/config/modelProviders/zeroone.ts +1 -81
  250. package/src/config/modelProviders/zhipu.ts +1 -108
  251. package/src/envs/app.ts +5 -8
  252. package/src/envs/auth.ts +0 -179
  253. package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
  254. package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
  255. package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
  256. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
  257. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
  258. package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
  259. package/src/features/Conversation/Error/index.tsx +15 -5
  260. package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
  261. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
  262. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
  263. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
  264. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
  265. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
  266. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
  267. package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
  268. package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
  269. package/src/features/Conversation/Messages/Default.tsx +2 -2
  270. package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
  271. package/src/features/Conversation/Messages/User/index.tsx +4 -4
  272. package/src/features/Conversation/Messages/index.tsx +3 -3
  273. package/src/features/Conversation/components/AutoScroll.tsx +2 -2
  274. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
  275. package/src/features/DataImporter/index.tsx +15 -60
  276. package/src/features/DevPanel/PostgresViewer/usePgTable.ts +3 -2
  277. package/src/features/PluginTag/index.tsx +1 -3
  278. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
  279. package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
  280. package/src/helpers/isCanUseFC.ts +0 -8
  281. package/src/hooks/useEnabledChatModels.ts +0 -8
  282. package/src/hooks/useInterceptingRoutes.test.ts +21 -3
  283. package/src/hooks/useModelContextWindowTokens.ts +0 -8
  284. package/src/hooks/useModelHasContextWindowToken.ts +1 -10
  285. package/src/hooks/useModelSupportFiles.ts +1 -11
  286. package/src/hooks/useModelSupportReasoning.ts +1 -11
  287. package/src/hooks/useModelSupportToolUse.ts +1 -11
  288. package/src/hooks/useModelSupportVision.ts +1 -11
  289. package/src/layout/AuthProvider/Clerk/index.tsx +2 -16
  290. package/src/libs/next-auth/auth.config.ts +3 -6
  291. package/src/libs/next-auth/sso-providers/auth0.ts +0 -7
  292. package/src/libs/next-auth/sso-providers/authelia.ts +3 -5
  293. package/src/libs/next-auth/sso-providers/authentik.ts +0 -7
  294. package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +3 -6
  295. package/src/libs/next-auth/sso-providers/cognito.ts +1 -5
  296. package/src/libs/next-auth/sso-providers/generic-oidc.ts +3 -5
  297. package/src/libs/next-auth/sso-providers/github.ts +0 -6
  298. package/src/libs/next-auth/sso-providers/google.ts +0 -2
  299. package/src/libs/next-auth/sso-providers/index.ts +0 -2
  300. package/src/libs/next-auth/sso-providers/keycloak.ts +0 -3
  301. package/src/libs/next-auth/sso-providers/logto.ts +3 -5
  302. package/src/libs/next-auth/sso-providers/okta.ts +0 -4
  303. package/src/libs/next-auth/sso-providers/zitadel.ts +0 -7
  304. package/src/libs/oidc-provider/provider.ts +1 -1
  305. package/src/libs/trpc/client/index.ts +0 -1
  306. package/src/libs/trpc/client/lambda.ts +8 -5
  307. package/src/libs/trpc/lambda/context.ts +4 -1
  308. package/src/locales/default/oauth.ts +1 -0
  309. package/src/server/globalConfig/index.ts +0 -23
  310. package/src/server/modules/AssistantStore/index.ts +1 -1
  311. package/src/server/modules/ModelRuntime/trace.ts +11 -4
  312. package/src/server/routers/desktop/mcp.ts +1 -3
  313. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -41
  314. package/src/server/routers/lambda/config/__snapshots__/index.test.ts.snap +175 -12
  315. package/src/server/routers/lambda/config/index.test.ts +38 -30
  316. package/src/server/routers/lambda/message.ts +17 -13
  317. package/src/server/routers/lambda/session.ts +8 -5
  318. package/src/server/routers/lambda/user.ts +24 -25
  319. package/src/server/routers/tools/mcp.ts +2 -3
  320. package/src/server/routers/tools/search.test.ts +1 -7
  321. package/src/server/routers/tools/search.ts +1 -4
  322. package/src/server/services/search/impls/firecrawl/index.ts +51 -11
  323. package/src/server/services/search/impls/firecrawl/type.ts +60 -9
  324. package/src/services/__tests__/tool.test.ts +0 -3
  325. package/src/services/aiModel/index.test.ts +0 -3
  326. package/src/services/aiModel/index.ts +1 -7
  327. package/src/services/aiProvider/index.test.ts +0 -3
  328. package/src/services/aiProvider/index.ts +1 -7
  329. package/src/services/chat/chat.test.ts +13 -40
  330. package/src/services/chat/contextEngineering.test.ts +0 -30
  331. package/src/services/chat/contextEngineering.ts +1 -12
  332. package/src/services/chat/helper.ts +7 -31
  333. package/src/services/chat/index.ts +5 -10
  334. package/src/services/chat/types.ts +1 -1
  335. package/src/services/chatGroup/index.ts +1 -10
  336. package/src/services/config.ts +1 -65
  337. package/src/services/export/index.ts +1 -4
  338. package/src/services/file/index.ts +1 -11
  339. package/src/services/import/index.ts +1 -7
  340. package/src/services/mcp.test.ts +777 -0
  341. package/src/services/message/index.ts +1 -11
  342. package/src/services/message/server.ts +7 -6
  343. package/src/services/message/type.ts +6 -3
  344. package/src/services/models.ts +2 -11
  345. package/src/services/plugin/index.ts +1 -11
  346. package/src/services/session/index.ts +1 -11
  347. package/src/services/tableViewer/client.ts +12 -15
  348. package/src/services/thread/index.ts +1 -7
  349. package/src/services/topic/index.ts +1 -11
  350. package/src/services/user/index.ts +1 -13
  351. package/src/store/chat/helpers.test.ts +99 -0
  352. package/src/store/chat/helpers.ts +21 -2
  353. package/src/store/chat/selectors.ts +1 -1
  354. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -241
  355. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +26 -1
  356. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +3 -1
  357. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +45 -155
  358. package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
  359. package/src/store/chat/slices/message/action.test.ts +5 -1
  360. package/src/store/chat/slices/message/action.ts +102 -14
  361. package/src/store/chat/slices/message/reducer.test.ts +363 -5
  362. package/src/store/chat/slices/message/reducer.ts +87 -3
  363. package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
  364. package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
  365. package/src/store/chat/slices/message/selectors/index.ts +2 -0
  366. package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
  367. package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
  368. package/src/store/chat/slices/plugin/action.test.ts +34 -132
  369. package/src/store/chat/slices/plugin/action.ts +1 -44
  370. package/src/store/global/store.ts +1 -7
  371. package/src/store/tool/selectors/tool.test.ts +1 -1
  372. package/src/store/tool/selectors/tool.ts +6 -8
  373. package/src/store/tool/slices/builtin/action.test.ts +83 -35
  374. package/src/store/tool/slices/builtin/action.ts +0 -9
  375. package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
  376. package/src/store/tool/slices/builtin/selectors.ts +15 -21
  377. package/src/store/user/initialState.ts +1 -7
  378. package/src/store/user/selectors.ts +1 -5
  379. package/src/store/user/slices/common/action.test.ts +1 -4
  380. package/src/store/user/slices/common/action.ts +5 -4
  381. package/src/store/user/slices/settings/selectors/index.ts +1 -0
  382. package/src/store/user/slices/settings/selectors/keyVaults.ts +21 -0
  383. package/src/store/user/store.ts +0 -3
  384. package/src/tools/index.ts +0 -6
  385. package/src/tools/renders.ts +0 -3
  386. package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
  387. package/src/tools/web-browsing/Render/Search/ConfigForm/Form.tsx +1 -1
  388. package/tsconfig.json +9 -3
  389. package/packages/const/src/guide.ts +0 -89
  390. package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
  391. package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
  392. package/packages/utils/src/_deprecated/__snapshots__/parseModels.test.ts.snap +0 -104
  393. package/packages/utils/src/_deprecated/parseModels.test.ts +0 -287
  394. package/packages/utils/src/_deprecated/parseModels.ts +0 -165
  395. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -26
  396. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +0 -48
  397. package/src/app/[variants]/(main)/(mobile)/me/data/features/Header.tsx +0 -33
  398. package/src/app/[variants]/(main)/(mobile)/me/data/layout.tsx +0 -13
  399. package/src/app/[variants]/(main)/(mobile)/me/data/loading.tsx +0 -5
  400. package/src/app/[variants]/(main)/(mobile)/me/data/page.tsx +0 -29
  401. package/src/app/[variants]/(main)/chat/features/Migration/DBReader.ts +0 -290
  402. package/src/app/[variants]/(main)/chat/features/Migration/ExportConfigButton.tsx +0 -35
  403. package/src/app/[variants]/(main)/chat/features/Migration/Failed.tsx +0 -120
  404. package/src/app/[variants]/(main)/chat/features/Migration/Modal.tsx +0 -81
  405. package/src/app/[variants]/(main)/chat/features/Migration/Start.tsx +0 -108
  406. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +0 -71
  407. package/src/app/[variants]/(main)/chat/features/Migration/const.ts +0 -15
  408. package/src/app/[variants]/(main)/chat/features/Migration/index.tsx +0 -50
  409. package/src/app/[variants]/(main)/settings/llm/ProviderList/Azure/index.tsx +0 -93
  410. package/src/app/[variants]/(main)/settings/llm/ProviderList/Bedrock/index.tsx +0 -70
  411. package/src/app/[variants]/(main)/settings/llm/ProviderList/Cloudflare/index.tsx +0 -39
  412. package/src/app/[variants]/(main)/settings/llm/ProviderList/Github/index.tsx +0 -52
  413. package/src/app/[variants]/(main)/settings/llm/ProviderList/HuggingFace/index.tsx +0 -52
  414. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/index.tsx +0 -20
  415. package/src/app/[variants]/(main)/settings/llm/ProviderList/OpenAI/index.tsx +0 -17
  416. package/src/app/[variants]/(main)/settings/llm/ProviderList/providers.tsx +0 -132
  417. package/src/app/[variants]/(main)/settings/llm/components/Checker.tsx +0 -118
  418. package/src/app/[variants]/(main)/settings/llm/components/ProviderConfig/index.tsx +0 -303
  419. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +0 -98
  420. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/Form.tsx +0 -104
  421. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +0 -77
  422. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +0 -105
  423. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/Option.tsx +0 -68
  424. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/index.tsx +0 -146
  425. package/src/app/[variants]/(main)/settings/llm/const.ts +0 -20
  426. package/src/app/[variants]/(main)/settings/llm/features/Footer.tsx +0 -35
  427. package/src/app/[variants]/(main)/settings/llm/index.tsx +0 -30
  428. package/src/app/[variants]/(main)/settings/llm/type.ts +0 -5
  429. package/src/app/[variants]/loading/Client/Content.tsx +0 -48
  430. package/src/app/[variants]/loading/Client/Error.tsx +0 -27
  431. package/src/app/[variants]/loading/Client/Redirect.tsx +0 -47
  432. package/src/app/[variants]/loading/Client/index.tsx +0 -22
  433. package/src/components/InnerLink.tsx +0 -20
  434. package/src/database/_deprecated/core/__tests__/db-upgrade.test.ts +0 -42
  435. package/src/database/_deprecated/core/__tests__/db.test.ts +0 -79
  436. package/src/database/_deprecated/core/__tests__/model.test.ts +0 -55
  437. package/src/database/_deprecated/core/db.ts +0 -246
  438. package/src/database/_deprecated/core/index.ts +0 -2
  439. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/input.json +0 -55
  440. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/output.json +0 -60
  441. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.test.ts +0 -14
  442. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.ts +0 -22
  443. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/type.ts +0 -105
  444. package/src/database/_deprecated/core/model.ts +0 -218
  445. package/src/database/_deprecated/core/schemas.ts +0 -88
  446. package/src/database/_deprecated/core/types/db.ts +0 -15
  447. package/src/database/_deprecated/models/__DEBUG.ts +0 -124
  448. package/src/database/_deprecated/models/__tests__/file.test.ts +0 -83
  449. package/src/database/_deprecated/models/__tests__/message.test.ts +0 -426
  450. package/src/database/_deprecated/models/__tests__/plugin.test.ts +0 -81
  451. package/src/database/_deprecated/models/__tests__/session.test.ts +0 -253
  452. package/src/database/_deprecated/models/__tests__/sessionGroup.test.ts +0 -220
  453. package/src/database/_deprecated/models/__tests__/topic.test.ts +0 -523
  454. package/src/database/_deprecated/models/__tests__/user.test.ts +0 -82
  455. package/src/database/_deprecated/models/file.ts +0 -51
  456. package/src/database/_deprecated/models/message.ts +0 -277
  457. package/src/database/_deprecated/models/plugin.ts +0 -62
  458. package/src/database/_deprecated/models/session.ts +0 -271
  459. package/src/database/_deprecated/models/sessionGroup.ts +0 -93
  460. package/src/database/_deprecated/models/topic.ts +0 -250
  461. package/src/database/_deprecated/models/user.ts +0 -69
  462. package/src/database/_deprecated/schemas/files.ts +0 -39
  463. package/src/database/_deprecated/schemas/message.ts +0 -50
  464. package/src/database/_deprecated/schemas/plugin.ts +0 -12
  465. package/src/database/_deprecated/schemas/session.ts +0 -54
  466. package/src/database/_deprecated/schemas/sessionGroup.ts +0 -8
  467. package/src/database/_deprecated/schemas/topic.ts +0 -12
  468. package/src/database/_deprecated/schemas/user.ts +0 -40
  469. package/src/envs/__tests__/auth.test.ts +0 -200
  470. package/src/features/DataImporter/_deprecated.ts +0 -43
  471. package/src/features/InitClientDB/EnableModal.tsx +0 -118
  472. package/src/features/InitClientDB/ErrorResult.tsx +0 -143
  473. package/src/features/InitClientDB/InitIndicator.tsx +0 -124
  474. package/src/features/InitClientDB/PGliteIcon.tsx +0 -28
  475. package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +0 -75
  476. package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +0 -98
  477. package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +0 -218
  478. package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +0 -91
  479. package/src/features/InitClientDB/index.tsx +0 -37
  480. package/src/hooks/_header.ts +0 -23
  481. package/src/libs/next-auth/sso-providers/azure-ad.ts +0 -33
  482. package/src/libs/trpc/client/edge.ts +0 -26
  483. package/src/libs/trpc/edge/context.ts +0 -71
  484. package/src/libs/trpc/edge/index.ts +0 -45
  485. package/src/libs/trpc/edge/init.ts +0 -26
  486. package/src/libs/trpc/edge/middleware/jwtPayload.test.ts +0 -75
  487. package/src/libs/trpc/edge/middleware/jwtPayload.ts +0 -14
  488. package/src/migrations/FromV0ToV1.ts +0 -10
  489. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +0 -191
  490. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +0 -202
  491. package/src/migrations/FromV1ToV2/index.ts +0 -82
  492. package/src/migrations/FromV1ToV2/migrations.test.ts +0 -224
  493. package/src/migrations/FromV1ToV2/types/v1.ts +0 -78
  494. package/src/migrations/FromV1ToV2/types/v2.ts +0 -52
  495. package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +0 -72
  496. package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +0 -203
  497. package/src/migrations/FromV2ToV3/fixtures/output-v3.json +0 -74
  498. package/src/migrations/FromV2ToV3/index.ts +0 -30
  499. package/src/migrations/FromV2ToV3/migrations.test.ts +0 -42
  500. package/src/migrations/FromV2ToV3/types/v3.ts +0 -27
  501. package/src/migrations/FromV3ToV4/fixtures/azure-input-v3.json +0 -79
  502. package/src/migrations/FromV3ToV4/fixtures/azure-output-v4.json +0 -75
  503. package/src/migrations/FromV3ToV4/fixtures/ollama-input-v3.json +0 -85
  504. package/src/migrations/FromV3ToV4/fixtures/ollama-output-v4.json +0 -86
  505. package/src/migrations/FromV3ToV4/fixtures/openai-input-v3.json +0 -77
  506. package/src/migrations/FromV3ToV4/fixtures/openai-output-v4.json +0 -77
  507. package/src/migrations/FromV3ToV4/fixtures/openrouter-input-v3.json +0 -82
  508. package/src/migrations/FromV3ToV4/fixtures/openrouter-output-v4.json +0 -85
  509. package/src/migrations/FromV3ToV4/fixtures/output-v4-from-v1.json +0 -203
  510. package/src/migrations/FromV3ToV4/index.ts +0 -102
  511. package/src/migrations/FromV3ToV4/migrations.test.ts +0 -195
  512. package/src/migrations/FromV3ToV4/types/v3.ts +0 -52
  513. package/src/migrations/FromV3ToV4/types/v4.ts +0 -37
  514. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +0 -245
  515. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +0 -96
  516. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +0 -120
  517. package/src/migrations/FromV4ToV5/index.ts +0 -58
  518. package/src/migrations/FromV4ToV5/migrations.test.ts +0 -49
  519. package/src/migrations/FromV4ToV5/types/v4.ts +0 -21
  520. package/src/migrations/FromV4ToV5/types/v5.ts +0 -27
  521. package/src/migrations/FromV5ToV6/fixtures/from-v1-to-v6-output.json +0 -247
  522. package/src/migrations/FromV5ToV6/fixtures/session-input-v5.json +0 -81
  523. package/src/migrations/FromV5ToV6/fixtures/session-output-v6.json +0 -85
  524. package/src/migrations/FromV5ToV6/index.ts +0 -61
  525. package/src/migrations/FromV5ToV6/migrations.test.ts +0 -50
  526. package/src/migrations/FromV5ToV6/types/v5.ts +0 -48
  527. package/src/migrations/FromV5ToV6/types/v6.ts +0 -63
  528. package/src/migrations/FromV6ToV7/fixtures/output-v7-from-v1.json +0 -203
  529. package/src/migrations/FromV6ToV7/fixtures/provider-input-v6.json +0 -103
  530. package/src/migrations/FromV6ToV7/fixtures/provider-output-v7.json +0 -118
  531. package/src/migrations/FromV6ToV7/index.ts +0 -101
  532. package/src/migrations/FromV6ToV7/migrations.test.ts +0 -64
  533. package/src/migrations/FromV6ToV7/types/v6.ts +0 -61
  534. package/src/migrations/FromV6ToV7/types/v7.ts +0 -69
  535. package/src/migrations/VersionController.test.ts +0 -88
  536. package/src/migrations/VersionController.ts +0 -67
  537. package/src/migrations/index.ts +0 -61
  538. package/src/server/globalConfig/_deprecated.test.ts +0 -92
  539. package/src/server/globalConfig/_deprecated.ts +0 -41
  540. package/src/server/routers/edge/appStatus.ts +0 -3
  541. package/src/server/routers/edge/index.ts +0 -14
  542. package/src/server/routers/edge/upload.ts +0 -16
  543. package/src/services/aiModel/client.ts +0 -70
  544. package/src/services/aiProvider/client.ts +0 -58
  545. package/src/services/baseClientService/index.ts +0 -9
  546. package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
  547. package/src/services/chatGroup/client.ts +0 -63
  548. package/src/services/export/_deprecated.ts +0 -155
  549. package/src/services/export/client.ts +0 -15
  550. package/src/services/file/_deprecated.test.ts +0 -119
  551. package/src/services/file/_deprecated.ts +0 -80
  552. package/src/services/file/client.test.ts +0 -199
  553. package/src/services/file/client.ts +0 -85
  554. package/src/services/import/_deprecated.ts +0 -115
  555. package/src/services/import/client.test.ts +0 -1015
  556. package/src/services/import/client.ts +0 -64
  557. package/src/services/message/_deprecated.test.ts +0 -398
  558. package/src/services/message/_deprecated.ts +0 -168
  559. package/src/services/message/client.test.ts +0 -410
  560. package/src/services/message/client.ts +0 -186
  561. package/src/services/plugin/_deprecated.test.ts +0 -162
  562. package/src/services/plugin/_deprecated.ts +0 -42
  563. package/src/services/plugin/client.test.ts +0 -177
  564. package/src/services/plugin/client.ts +0 -46
  565. package/src/services/session/_deprecated.test.ts +0 -440
  566. package/src/services/session/_deprecated.ts +0 -190
  567. package/src/services/session/client.test.ts +0 -413
  568. package/src/services/session/client.ts +0 -193
  569. package/src/services/thread/client.ts +0 -51
  570. package/src/services/topic/_deprecated.test.ts +0 -245
  571. package/src/services/topic/_deprecated.ts +0 -75
  572. package/src/services/topic/client.ts +0 -89
  573. package/src/services/topic/pglite.test.ts +0 -212
  574. package/src/services/user/_deprecated.test.ts +0 -101
  575. package/src/services/user/_deprecated.ts +0 -70
  576. package/src/services/user/client.test.ts +0 -108
  577. package/src/services/user/client.ts +0 -104
  578. package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
  579. package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
  580. package/src/store/global/actions/clientDb.ts +0 -67
  581. package/src/store/user/slices/modelList/__snapshots__/action.test.ts.snap +0 -12
  582. package/src/store/user/slices/modelList/action.test.ts +0 -359
  583. package/src/store/user/slices/modelList/action.ts +0 -223
  584. package/src/store/user/slices/modelList/initialState.ts +0 -15
  585. package/src/store/user/slices/modelList/reducers/customModelCard.test.ts +0 -204
  586. package/src/store/user/slices/modelList/reducers/customModelCard.ts +0 -64
  587. package/src/store/user/slices/modelList/selectors/index.ts +0 -3
  588. package/src/store/user/slices/modelList/selectors/keyVaults.test.ts +0 -201
  589. package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -50
  590. package/src/store/user/slices/modelList/selectors/modelConfig.test.ts +0 -219
  591. package/src/store/user/slices/modelList/selectors/modelConfig.ts +0 -95
  592. package/src/store/user/slices/modelList/selectors/modelProvider.test.ts +0 -138
  593. package/src/store/user/slices/modelList/selectors/modelProvider.ts +0 -170
  594. package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
  595. package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
  596. package/src/tools/dalle/Render/Item/Error.tsx +0 -49
  597. package/src/tools/dalle/Render/Item/Image.tsx +0 -44
  598. package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
  599. package/src/tools/dalle/Render/Item/index.tsx +0 -88
  600. package/src/tools/dalle/Render/ToolBar.tsx +0 -56
  601. package/src/tools/dalle/Render/index.tsx +0 -52
  602. package/src/tools/dalle/index.ts +0 -92
  603. /package/src/{middleware.ts → proxy.ts} +0 -0
@@ -0,0 +1,706 @@
1
+ import { DataSyncConfig } from '@lobechat/electron-client-ipc';
2
+ import { BrowserWindow, shell } from 'electron';
3
+ import crypto from 'node:crypto';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import type { App } from '@/core/App';
7
+
8
+ import AuthCtr from '../AuthCtr';
9
+ import RemoteServerConfigCtr from '../RemoteServerConfigCtr';
10
+
11
+ // Mock logger
12
+ vi.mock('@/utils/logger', () => ({
13
+ createLogger: () => ({
14
+ debug: vi.fn(),
15
+ info: vi.fn(),
16
+ warn: vi.fn(),
17
+ error: vi.fn(),
18
+ }),
19
+ }));
20
+
21
+ // Mock electron
22
+ vi.mock('electron', () => ({
23
+ BrowserWindow: {
24
+ getAllWindows: vi.fn(() => []),
25
+ },
26
+ shell: {
27
+ openExternal: vi.fn().mockResolvedValue(undefined),
28
+ },
29
+ safeStorage: {
30
+ isEncryptionAvailable: vi.fn(() => true),
31
+ encryptString: vi.fn((str: string) => Buffer.from(str)),
32
+ decryptString: vi.fn((buffer: Buffer) => buffer.toString()),
33
+ },
34
+ }));
35
+
36
+ // Mock electron-is
37
+ vi.mock('electron-is', () => ({
38
+ macOS: vi.fn(() => false),
39
+ windows: vi.fn(() => false),
40
+ linux: vi.fn(() => false),
41
+ }));
42
+
43
+ // Mock OFFICIAL_CLOUD_SERVER
44
+ vi.mock('@/const/env', () => ({
45
+ OFFICIAL_CLOUD_SERVER: 'https://lobehub-cloud.com',
46
+ isMac: false,
47
+ isWindows: false,
48
+ isLinux: false,
49
+ isDev: false,
50
+ }));
51
+
52
+ // Mock crypto
53
+ let randomBytesCounter = 0;
54
+ vi.mock('node:crypto', () => ({
55
+ default: {
56
+ randomBytes: vi.fn((size: number) => {
57
+ randomBytesCounter++;
58
+ return {
59
+ toString: vi.fn(() => `mock-random-${randomBytesCounter}`),
60
+ };
61
+ }),
62
+ subtle: {
63
+ digest: vi.fn(() => Promise.resolve(new ArrayBuffer(32))),
64
+ },
65
+ },
66
+ }));
67
+
68
+ // Create mock App and RemoteServerConfigCtr
69
+ const mockRemoteServerConfigCtr = {
70
+ clearTokens: vi.fn().mockResolvedValue(undefined),
71
+ getAccessToken: vi.fn().mockResolvedValue('mock-access-token'),
72
+ getRemoteServerConfig: vi.fn().mockResolvedValue({ active: true, storageMode: 'cloud' }),
73
+ getRemoteServerUrl: vi.fn().mockImplementation(async (config?: DataSyncConfig) => {
74
+ if (config?.storageMode === 'selfHost') {
75
+ return config.remoteServerUrl || 'https://mock-server.com';
76
+ }
77
+ return 'https://lobehub-cloud.com'; // OFFICIAL_CLOUD_SERVER
78
+ }),
79
+ getTokenExpiresAt: vi.fn().mockReturnValue(Date.now() + 3600000),
80
+ isTokenExpiringSoon: vi.fn().mockReturnValue(false),
81
+ refreshAccessToken: vi.fn().mockResolvedValue({ success: true }),
82
+ saveTokens: vi.fn().mockResolvedValue(undefined),
83
+ setRemoteServerConfig: vi.fn().mockResolvedValue(true),
84
+ } as unknown as RemoteServerConfigCtr;
85
+
86
+ const mockApp = {
87
+ getController: vi.fn((ControllerClass) => {
88
+ if (ControllerClass === RemoteServerConfigCtr) {
89
+ return mockRemoteServerConfigCtr;
90
+ }
91
+ return null;
92
+ }),
93
+ } as unknown as App;
94
+
95
+ describe('AuthCtr', () => {
96
+ let authCtr: AuthCtr;
97
+ let mockFetch: ReturnType<typeof vi.fn>;
98
+ let mockWindow: any;
99
+
100
+ beforeEach(() => {
101
+ vi.clearAllMocks();
102
+ randomBytesCounter = 0; // Reset counter for each test
103
+
104
+ // Reset shell.openExternal to default successful behavior
105
+ vi.mocked(shell.openExternal).mockResolvedValue(undefined);
106
+
107
+ // Create fresh instance for each test
108
+ authCtr = new AuthCtr(mockApp);
109
+
110
+ // Mock global fetch
111
+ mockFetch = vi.fn();
112
+ global.fetch = mockFetch;
113
+
114
+ // Mock BrowserWindow with send spy
115
+ mockWindow = {
116
+ isDestroyed: vi.fn(() => false),
117
+ webContents: {
118
+ send: vi.fn(),
119
+ },
120
+ };
121
+ vi.mocked(BrowserWindow.getAllWindows).mockReturnValue([mockWindow]);
122
+ });
123
+
124
+ afterEach(() => {
125
+ // Clean up authCtr intervals (using real timers, not fake timers)
126
+ authCtr.cleanup();
127
+ // Clean up any fake timers if used
128
+ vi.clearAllTimers();
129
+ });
130
+
131
+ describe('Basic functionality', () => {
132
+ // Use real timers for all tests since setInterval with async doesn't work well with fake timers
133
+
134
+ describe('requestAuthorization', () => {
135
+ it('should generate PKCE parameters and open authorization URL', async () => {
136
+ const config: DataSyncConfig = {
137
+ active: false,
138
+ storageMode: 'cloud',
139
+ };
140
+
141
+ mockFetch.mockResolvedValue({
142
+ status: 404,
143
+ ok: false,
144
+ });
145
+
146
+ const result = await authCtr.requestAuthorization(config);
147
+
148
+ // Verify success response
149
+ expect(result).toEqual({ success: true });
150
+
151
+ // Verify shell.openExternal was called with correct URL
152
+ expect(shell.openExternal).toHaveBeenCalledWith(
153
+ expect.stringContaining('https://lobehub-cloud.com/oidc/auth'),
154
+ );
155
+
156
+ // Verify URL contains required parameters
157
+ const authUrl = vi.mocked(shell.openExternal).mock.calls[0][0];
158
+ expect(authUrl).toContain('client_id=lobehub-desktop');
159
+ expect(authUrl).toContain('response_type=code');
160
+ expect(authUrl).toContain('code_challenge_method=S256');
161
+ expect(authUrl).toContain('scope=profile%20email%20offline_access');
162
+ });
163
+
164
+ it('should start polling after authorization request', async () => {
165
+ const config: DataSyncConfig = {
166
+ active: false,
167
+ storageMode: 'cloud',
168
+ };
169
+
170
+ mockFetch.mockResolvedValue({
171
+ status: 404,
172
+ ok: false,
173
+ });
174
+
175
+ const result = await authCtr.requestAuthorization(config);
176
+ expect(result.success).toBe(true);
177
+
178
+ // Wait a bit for polling to start
179
+ await new Promise((resolve) => setTimeout(resolve, 3500));
180
+
181
+ // Verify fetch was called for polling
182
+ const pollingCalls = mockFetch.mock.calls.filter((call) =>
183
+ (call[0] as string).includes('/oidc/handoff'),
184
+ );
185
+ expect(pollingCalls.length).toBeGreaterThan(0);
186
+ });
187
+
188
+ it('should use self-hosted server URL when storageMode is selfHost', async () => {
189
+ const config: DataSyncConfig = {
190
+ active: false,
191
+ storageMode: 'selfHost',
192
+ remoteServerUrl: 'https://my-custom-server.com',
193
+ };
194
+
195
+ mockFetch.mockResolvedValue({
196
+ status: 404,
197
+ ok: false,
198
+ });
199
+
200
+ await authCtr.requestAuthorization(config);
201
+
202
+ // Verify shell.openExternal was called with custom URL
203
+ expect(shell.openExternal).toHaveBeenCalledWith(
204
+ expect.stringContaining('https://my-custom-server.com/oidc/auth'),
205
+ );
206
+ });
207
+
208
+ it('should handle authorization request error gracefully', async () => {
209
+ const config: DataSyncConfig = {
210
+ active: false,
211
+ storageMode: 'cloud',
212
+ };
213
+
214
+ vi.mocked(shell.openExternal).mockRejectedValue(new Error('Failed to open browser'));
215
+
216
+ const result = await authCtr.requestAuthorization(config);
217
+
218
+ expect(result.success).toBe(false);
219
+ expect(result.error).toContain('Failed to open browser');
220
+ });
221
+ });
222
+
223
+ describe('polling mechanism', () => {
224
+ it('should poll every 3 seconds', async () => {
225
+ const config: DataSyncConfig = {
226
+ active: false,
227
+ storageMode: 'cloud',
228
+ };
229
+
230
+ mockFetch.mockResolvedValue({
231
+ status: 404,
232
+ ok: false,
233
+ });
234
+
235
+ await authCtr.requestAuthorization(config);
236
+
237
+ // Wait for first poll
238
+ await new Promise((resolve) => setTimeout(resolve, 3100));
239
+
240
+ const firstCallCount = mockFetch.mock.calls.filter((call) =>
241
+ (call[0] as string).includes('/oidc/handoff'),
242
+ ).length;
243
+ expect(firstCallCount).toBeGreaterThanOrEqual(1);
244
+
245
+ // Wait for second poll
246
+ await new Promise((resolve) => setTimeout(resolve, 3000));
247
+
248
+ const secondCallCount = mockFetch.mock.calls.filter((call) =>
249
+ (call[0] as string).includes('/oidc/handoff'),
250
+ ).length;
251
+ expect(secondCallCount).toBeGreaterThanOrEqual(2);
252
+ }, 10000);
253
+
254
+ it('should stop polling when credentials are received', async () => {
255
+ const config: DataSyncConfig = {
256
+ active: false,
257
+ storageMode: 'cloud',
258
+ };
259
+
260
+ let pollCount = 0;
261
+ mockFetch.mockImplementation((url: string) => {
262
+ const urlObj = new URL(url);
263
+
264
+ // Return success on third poll
265
+ if (urlObj.pathname.includes('/oidc/handoff')) {
266
+ pollCount++;
267
+ if (pollCount >= 3) {
268
+ return Promise.resolve({
269
+ status: 200,
270
+ ok: true,
271
+ json: () =>
272
+ Promise.resolve({
273
+ success: true,
274
+ data: {
275
+ payload: {
276
+ code: 'mock-auth-code',
277
+ state: 'mock-random-2', // Second randomBytes call is for state
278
+ },
279
+ },
280
+ }),
281
+ text: () => Promise.resolve('mock response'),
282
+ });
283
+ }
284
+ }
285
+
286
+ // Token exchange endpoint
287
+ if (urlObj.pathname.includes('/oidc/token')) {
288
+ return Promise.resolve({
289
+ status: 200,
290
+ ok: true,
291
+ json: () =>
292
+ Promise.resolve({
293
+ access_token: 'new-access-token',
294
+ refresh_token: 'new-refresh-token',
295
+ expires_in: 3600,
296
+ }),
297
+ text: () => Promise.resolve('mock response'),
298
+ clone: () => ({
299
+ json: () =>
300
+ Promise.resolve({
301
+ access_token: 'new-access-token',
302
+ refresh_token: 'new-refresh-token',
303
+ expires_in: 3600,
304
+ }),
305
+ }),
306
+ });
307
+ }
308
+
309
+ return Promise.resolve({
310
+ status: 404,
311
+ ok: false,
312
+ });
313
+ });
314
+
315
+ await authCtr.requestAuthorization(config);
316
+
317
+ // Wait for polling to complete
318
+ await new Promise((resolve) => setTimeout(resolve, 10000));
319
+
320
+ const pollCountBefore = pollCount;
321
+
322
+ // Wait more time and verify no more polling
323
+ await new Promise((resolve) => setTimeout(resolve, 3500));
324
+ expect(pollCount).toBe(pollCountBefore);
325
+ }, 15000);
326
+
327
+ it('should broadcast authorizationSuccessful when credentials are exchanged', async () => {
328
+ const config: DataSyncConfig = {
329
+ active: false,
330
+ storageMode: 'cloud',
331
+ };
332
+
333
+ mockFetch.mockImplementation((url: string) => {
334
+ const urlObj = new URL(url);
335
+
336
+ if (urlObj.pathname.includes('/oidc/handoff')) {
337
+ return Promise.resolve({
338
+ status: 200,
339
+ ok: true,
340
+ json: () =>
341
+ Promise.resolve({
342
+ success: true,
343
+ data: {
344
+ payload: {
345
+ code: 'mock-auth-code',
346
+ state: 'mock-random-2', // Second randomBytes call is for state
347
+ },
348
+ },
349
+ }),
350
+ text: () => Promise.resolve('mock response'),
351
+ });
352
+ }
353
+
354
+ if (urlObj.pathname.includes('/oidc/token')) {
355
+ return Promise.resolve({
356
+ status: 200,
357
+ ok: true,
358
+ json: () =>
359
+ Promise.resolve({
360
+ access_token: 'new-access-token',
361
+ refresh_token: 'new-refresh-token',
362
+ expires_in: 3600,
363
+ }),
364
+ text: () => Promise.resolve('mock response'),
365
+ clone: () => ({
366
+ json: () =>
367
+ Promise.resolve({
368
+ access_token: 'new-access-token',
369
+ refresh_token: 'new-refresh-token',
370
+ expires_in: 3600,
371
+ }),
372
+ }),
373
+ });
374
+ }
375
+
376
+ return Promise.resolve({ status: 404, ok: false });
377
+ });
378
+
379
+ await authCtr.requestAuthorization(config);
380
+
381
+ // Wait for polling to complete and token exchange
382
+ await new Promise((resolve) => setTimeout(resolve, 4000));
383
+
384
+ // Verify authorizationSuccessful was broadcast
385
+ expect(mockWindow.webContents.send).toHaveBeenCalledWith('authorizationSuccessful');
386
+ }, 6000);
387
+
388
+ it('should validate state parameter and reject mismatched state', async () => {
389
+ const config: DataSyncConfig = {
390
+ active: false,
391
+ storageMode: 'cloud',
392
+ };
393
+
394
+ mockFetch.mockImplementation((url: string) => {
395
+ const urlObj = new URL(url);
396
+
397
+ if (urlObj.pathname.includes('/oidc/handoff')) {
398
+ return Promise.resolve({
399
+ status: 200,
400
+ ok: true,
401
+ json: () =>
402
+ Promise.resolve({
403
+ success: true,
404
+ data: {
405
+ payload: {
406
+ code: 'mock-auth-code',
407
+ state: 'wrong-state', // Mismatched state
408
+ },
409
+ },
410
+ }),
411
+ });
412
+ }
413
+
414
+ return Promise.resolve({ status: 404, ok: false });
415
+ });
416
+
417
+ await authCtr.requestAuthorization(config);
418
+
419
+ // Wait for polling and state validation
420
+ await new Promise((resolve) => setTimeout(resolve, 4000));
421
+
422
+ // Verify authorizationFailed was broadcast with state error
423
+ expect(mockWindow.webContents.send).toHaveBeenCalledWith('authorizationFailed', {
424
+ error: 'Invalid state parameter',
425
+ });
426
+ }, 6000);
427
+ });
428
+
429
+ describe('token refresh', () => {
430
+ it('should start auto-refresh after successful authorization', async () => {
431
+ const config: DataSyncConfig = {
432
+ active: false,
433
+ storageMode: 'cloud',
434
+ };
435
+
436
+ mockFetch.mockImplementation((url: string) => {
437
+ const urlObj = new URL(url);
438
+
439
+ if (urlObj.pathname.includes('/oidc/handoff')) {
440
+ return Promise.resolve({
441
+ status: 200,
442
+ ok: true,
443
+ json: () =>
444
+ Promise.resolve({
445
+ success: true,
446
+ data: {
447
+ payload: {
448
+ code: 'mock-auth-code',
449
+ state: 'mock-random-2', // Second randomBytes call is for state
450
+ },
451
+ },
452
+ }),
453
+ text: () => Promise.resolve('mock response'),
454
+ });
455
+ }
456
+
457
+ if (urlObj.pathname.includes('/oidc/token')) {
458
+ return Promise.resolve({
459
+ status: 200,
460
+ ok: true,
461
+ json: () =>
462
+ Promise.resolve({
463
+ access_token: 'new-access-token',
464
+ refresh_token: 'new-refresh-token',
465
+ expires_in: 3600,
466
+ }),
467
+ text: () => Promise.resolve('mock response'),
468
+ clone: () => ({
469
+ json: () =>
470
+ Promise.resolve({
471
+ access_token: 'new-access-token',
472
+ refresh_token: 'new-refresh-token',
473
+ expires_in: 3600,
474
+ }),
475
+ }),
476
+ });
477
+ }
478
+
479
+ return Promise.resolve({ status: 404, ok: false });
480
+ });
481
+
482
+ await authCtr.requestAuthorization(config);
483
+
484
+ // Wait for polling and token exchange
485
+ await new Promise((resolve) => setTimeout(resolve, 4000));
486
+
487
+ // Verify saveTokens was called
488
+ expect(mockRemoteServerConfigCtr.saveTokens).toHaveBeenCalledWith(
489
+ 'new-access-token',
490
+ 'new-refresh-token',
491
+ 3600,
492
+ );
493
+
494
+ // Verify remote server was set to active
495
+ expect(mockRemoteServerConfigCtr.setRemoteServerConfig).toHaveBeenCalledWith({
496
+ active: true,
497
+ });
498
+ }, 6000);
499
+ });
500
+ });
501
+
502
+ describe('Scenario: Authorization Timeout and Retry', () => {
503
+ // All scenario tests use real timers
504
+
505
+ it('Step 1: User requests authorization but does not complete it within 5 minutes', async () => {
506
+ const config: DataSyncConfig = {
507
+ active: false,
508
+ storageMode: 'cloud',
509
+ };
510
+
511
+ // Mock: User never completes authorization, so polling always returns 404
512
+ mockFetch.mockResolvedValue({
513
+ status: 404,
514
+ ok: false,
515
+ });
516
+
517
+ // User clicks "Connect to Cloud" button
518
+ await authCtr.requestAuthorization(config);
519
+
520
+ // Wait for some polling to happen
521
+ await new Promise((resolve) => setTimeout(resolve, 10000));
522
+
523
+ const handoffCallsBeforeTimeout = mockFetch.mock.calls.filter((call) =>
524
+ (call[0] as string).includes('/oidc/handoff'),
525
+ ).length;
526
+ expect(handoffCallsBeforeTimeout).toBeGreaterThan(0);
527
+
528
+ // Verify polling is active by checking calls increased
529
+ const callsBefore = handoffCallsBeforeTimeout;
530
+ await new Promise((resolve) => setTimeout(resolve, 3500));
531
+ const callsAfter = mockFetch.mock.calls.filter((call) =>
532
+ (call[0] as string).includes('/oidc/handoff'),
533
+ ).length;
534
+ expect(callsAfter).toBeGreaterThan(callsBefore);
535
+ }, 15000); // Increase test timeout
536
+
537
+ it('Step 2: User clicks retry button after previous attempt', async () => {
538
+ const config: DataSyncConfig = {
539
+ active: false,
540
+ storageMode: 'cloud',
541
+ };
542
+
543
+ mockFetch.mockResolvedValue({
544
+ status: 404,
545
+ ok: false,
546
+ });
547
+
548
+ // First attempt
549
+ await authCtr.requestAuthorization(config);
550
+ await new Promise((resolve) => setTimeout(resolve, 3500));
551
+
552
+ // Reset mock to track retry
553
+ mockFetch.mockClear();
554
+
555
+ // User clicks retry button - should start fresh authorization
556
+ await authCtr.requestAuthorization(config);
557
+
558
+ // Verify: New polling started
559
+ await new Promise((resolve) => setTimeout(resolve, 3500));
560
+
561
+ const handoffCalls = mockFetch.mock.calls.filter((call) =>
562
+ (call[0] as string).includes('/oidc/handoff'),
563
+ );
564
+ expect(handoffCalls.length).toBeGreaterThan(0);
565
+ }, 10000);
566
+
567
+ it('Step 3: Retry generates new state parameter (not reusing old state)', async () => {
568
+ const config: DataSyncConfig = {
569
+ active: false,
570
+ storageMode: 'cloud',
571
+ };
572
+
573
+ const capturedStates: string[] = [];
574
+
575
+ mockFetch.mockImplementation((url: string) => {
576
+ const urlObj = new URL(url);
577
+ const stateParam = urlObj.searchParams.get('id');
578
+ if (stateParam && !capturedStates.includes(stateParam)) {
579
+ capturedStates.push(stateParam);
580
+ }
581
+ return Promise.resolve({ status: 404, ok: false });
582
+ });
583
+
584
+ // First authorization attempt
585
+ await authCtr.requestAuthorization(config);
586
+ await new Promise((resolve) => setTimeout(resolve, 3500));
587
+ const firstState = capturedStates[0];
588
+
589
+ // Clear for second attempt tracking
590
+ const firstAttemptStates = [...capturedStates];
591
+ capturedStates.length = 0;
592
+
593
+ // Retry - should generate NEW state
594
+ await authCtr.requestAuthorization(config);
595
+ await new Promise((resolve) => setTimeout(resolve, 3500));
596
+ const secondState = capturedStates[0];
597
+
598
+ // CRITICAL: States must be different
599
+ expect(firstState).toBeDefined();
600
+ expect(secondState).toBeDefined();
601
+ expect(secondState).not.toBe(firstState);
602
+ expect(firstAttemptStates).not.toContain(secondState);
603
+ }, 10000);
604
+
605
+ it('Step 4: User completes authorization on retry successfully', async () => {
606
+ const config: DataSyncConfig = {
607
+ active: false,
608
+ storageMode: 'cloud',
609
+ };
610
+
611
+ // First attempt - incomplete
612
+ mockFetch.mockResolvedValue({ status: 404, ok: false });
613
+ await authCtr.requestAuthorization(config);
614
+ await new Promise((resolve) => setTimeout(resolve, 3500));
615
+
616
+ // Second attempt - user completes it this time
617
+ mockFetch.mockImplementation((url: string) => {
618
+ const urlObj = new URL(url);
619
+
620
+ // Handoff returns credentials immediately
621
+ if (urlObj.pathname.includes('/oidc/handoff')) {
622
+ return Promise.resolve({
623
+ status: 200,
624
+ ok: true,
625
+ json: () =>
626
+ Promise.resolve({
627
+ success: true,
628
+ data: {
629
+ payload: {
630
+ code: 'authorization-code',
631
+ state: 'mock-random-4', // Matches second request's state (3rd and 4th randomBytes calls)
632
+ },
633
+ },
634
+ }),
635
+ text: () => Promise.resolve('mock response'),
636
+ });
637
+ }
638
+
639
+ // Token exchange succeeds
640
+ if (urlObj.pathname.includes('/oidc/token')) {
641
+ return Promise.resolve({
642
+ status: 200,
643
+ ok: true,
644
+ json: () =>
645
+ Promise.resolve({
646
+ access_token: 'access-token',
647
+ refresh_token: 'refresh-token',
648
+ expires_in: 3600,
649
+ }),
650
+ text: () => Promise.resolve('mock response'),
651
+ clone: () => ({
652
+ json: () =>
653
+ Promise.resolve({
654
+ access_token: 'access-token',
655
+ refresh_token: 'refresh-token',
656
+ expires_in: 3600,
657
+ }),
658
+ }),
659
+ });
660
+ }
661
+
662
+ return Promise.resolve({ status: 404, ok: false });
663
+ });
664
+
665
+ await authCtr.requestAuthorization(config);
666
+
667
+ // Wait longer for polling and token exchange
668
+ await new Promise((resolve) => setTimeout(resolve, 4000));
669
+
670
+ // Verify: Success message shown
671
+ const successCall = mockWindow.webContents.send.mock.calls.find(
672
+ (call: any[]) => call[0] === 'authorizationSuccessful',
673
+ );
674
+ expect(successCall).toBeDefined();
675
+
676
+ // Verify: Tokens saved
677
+ expect(mockRemoteServerConfigCtr.saveTokens).toHaveBeenCalled();
678
+ }, 12000);
679
+
680
+ it('Edge case: Rapid retry clicks should not create multiple polling intervals', async () => {
681
+ const config: DataSyncConfig = {
682
+ active: false,
683
+ storageMode: 'cloud',
684
+ };
685
+
686
+ mockFetch.mockResolvedValue({ status: 404, ok: false });
687
+
688
+ // User rapidly clicks retry multiple times
689
+ await authCtr.requestAuthorization(config);
690
+ await authCtr.requestAuthorization(config);
691
+ await authCtr.requestAuthorization(config);
692
+
693
+ // Wait for some polling to happen
694
+ await new Promise((resolve) => setTimeout(resolve, 9000));
695
+
696
+ // Count handoff requests
697
+ const handoffCalls = mockFetch.mock.calls.filter((call) =>
698
+ (call[0] as string).includes('/oidc/handoff'),
699
+ );
700
+
701
+ // Should have ~3 calls (one per 3-second interval), not ~9 (3 intervals running)
702
+ // Allow some tolerance for timing
703
+ expect(handoffCalls.length).toBeLessThanOrEqual(5);
704
+ }, 10000);
705
+ });
706
+ });