App.jsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React, { lazy, Suspense, useContext, useMemo } from 'react';
  16. import { Route, Routes, useLocation, useParams } from 'react-router-dom';
  17. import Loading from './components/common/ui/Loading';
  18. import User from './pages/User';
  19. import { AuthRedirect, PrivateRoute, AdminRoute } from './helpers';
  20. import RegisterForm from './components/auth/RegisterForm';
  21. import LoginForm from './components/auth/LoginForm';
  22. import NotFound from './pages/NotFound';
  23. import Forbidden from './pages/Forbidden';
  24. import Setting from './pages/Setting';
  25. import { StatusContext } from './context/Status';
  26. import PasswordResetForm from './components/auth/PasswordResetForm';
  27. import PasswordResetConfirm from './components/auth/PasswordResetConfirm';
  28. import Channel from './pages/Channel';
  29. import Token from './pages/Token';
  30. import Redemption from './pages/Redemption';
  31. import TopUp from './pages/TopUp';
  32. import Log from './pages/Log';
  33. import Chat from './pages/Chat';
  34. import Chat2Link from './pages/Chat2Link';
  35. import Midjourney from './pages/Midjourney';
  36. import Pricing from './pages/Pricing';
  37. import Task from './pages/Task';
  38. import ModelPage from './pages/Model';
  39. import ModelDeploymentPage from './pages/ModelDeployment';
  40. import Playground from './pages/Playground';
  41. import Subscription from './pages/Subscription';
  42. import OAuth2Callback from './components/auth/OAuth2Callback';
  43. import PersonalSetting from './components/settings/PersonalSetting';
  44. import Setup from './pages/Setup';
  45. import SetupCheck from './components/layout/SetupCheck';
  46. const Home = lazy(() => import('./pages/Home'));
  47. const Dashboard = lazy(() => import('./pages/Dashboard'));
  48. const About = lazy(() => import('./pages/About'));
  49. const UserAgreement = lazy(() => import('./pages/UserAgreement'));
  50. const PrivacyPolicy = lazy(() => import('./pages/PrivacyPolicy'));
  51. function DynamicOAuth2Callback() {
  52. const { provider } = useParams();
  53. return <OAuth2Callback type={provider} />;
  54. }
  55. function App() {
  56. const location = useLocation();
  57. const [statusState] = useContext(StatusContext);
  58. // 获取模型广场权限配置
  59. const pricingRequireAuth = useMemo(() => {
  60. const headerNavModulesConfig = statusState?.status?.HeaderNavModules;
  61. if (headerNavModulesConfig) {
  62. try {
  63. const modules = JSON.parse(headerNavModulesConfig);
  64. // 处理向后兼容性:如果pricing是boolean,默认不需要登录
  65. if (typeof modules.pricing === 'boolean') {
  66. return false; // 默认不需要登录鉴权
  67. }
  68. // 如果是对象格式,使用requireAuth配置
  69. return modules.pricing?.requireAuth === true;
  70. } catch (error) {
  71. console.error('解析顶栏模块配置失败:', error);
  72. return false; // 默认不需要登录
  73. }
  74. }
  75. return false; // 默认不需要登录
  76. }, [statusState?.status?.HeaderNavModules]);
  77. return (
  78. <SetupCheck>
  79. <Routes>
  80. <Route
  81. path='/'
  82. element={
  83. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  84. <Home />
  85. </Suspense>
  86. }
  87. />
  88. <Route
  89. path='/setup'
  90. element={
  91. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  92. <Setup />
  93. </Suspense>
  94. }
  95. />
  96. <Route path='/forbidden' element={<Forbidden />} />
  97. <Route
  98. path='/console/models'
  99. element={
  100. <AdminRoute>
  101. <ModelPage />
  102. </AdminRoute>
  103. }
  104. />
  105. <Route
  106. path='/console/deployment'
  107. element={
  108. <AdminRoute>
  109. <ModelDeploymentPage />
  110. </AdminRoute>
  111. }
  112. />
  113. <Route
  114. path='/console/subscription'
  115. element={
  116. <AdminRoute>
  117. <Subscription />
  118. </AdminRoute>
  119. }
  120. />
  121. <Route
  122. path='/console/channel'
  123. element={
  124. <AdminRoute>
  125. <Channel />
  126. </AdminRoute>
  127. }
  128. />
  129. <Route
  130. path='/console/token'
  131. element={
  132. <PrivateRoute>
  133. <Token />
  134. </PrivateRoute>
  135. }
  136. />
  137. <Route
  138. path='/console/playground'
  139. element={
  140. <PrivateRoute>
  141. <Playground />
  142. </PrivateRoute>
  143. }
  144. />
  145. <Route
  146. path='/console/redemption'
  147. element={
  148. <AdminRoute>
  149. <Redemption />
  150. </AdminRoute>
  151. }
  152. />
  153. <Route
  154. path='/console/user'
  155. element={
  156. <AdminRoute>
  157. <User />
  158. </AdminRoute>
  159. }
  160. />
  161. <Route
  162. path='/user/reset'
  163. element={
  164. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  165. <PasswordResetConfirm />
  166. </Suspense>
  167. }
  168. />
  169. <Route
  170. path='/login'
  171. element={
  172. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  173. <AuthRedirect>
  174. <LoginForm />
  175. </AuthRedirect>
  176. </Suspense>
  177. }
  178. />
  179. <Route
  180. path='/register'
  181. element={
  182. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  183. <AuthRedirect>
  184. <RegisterForm />
  185. </AuthRedirect>
  186. </Suspense>
  187. }
  188. />
  189. <Route
  190. path='/reset'
  191. element={
  192. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  193. <PasswordResetForm />
  194. </Suspense>
  195. }
  196. />
  197. <Route
  198. path='/oauth/github'
  199. element={
  200. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  201. <OAuth2Callback type='github'></OAuth2Callback>
  202. </Suspense>
  203. }
  204. />
  205. <Route
  206. path='/oauth/discord'
  207. element={
  208. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  209. <OAuth2Callback type='discord'></OAuth2Callback>
  210. </Suspense>
  211. }
  212. />
  213. <Route
  214. path='/oauth/oidc'
  215. element={
  216. <Suspense fallback={<Loading></Loading>}>
  217. <OAuth2Callback type='oidc'></OAuth2Callback>
  218. </Suspense>
  219. }
  220. />
  221. <Route
  222. path='/oauth/linuxdo'
  223. element={
  224. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  225. <OAuth2Callback type='linuxdo'></OAuth2Callback>
  226. </Suspense>
  227. }
  228. />
  229. <Route
  230. path='/oauth/:provider'
  231. element={
  232. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  233. <DynamicOAuth2Callback />
  234. </Suspense>
  235. }
  236. />
  237. <Route
  238. path='/console/setting'
  239. element={
  240. <AdminRoute>
  241. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  242. <Setting />
  243. </Suspense>
  244. </AdminRoute>
  245. }
  246. />
  247. <Route
  248. path='/console/personal'
  249. element={
  250. <PrivateRoute>
  251. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  252. <PersonalSetting />
  253. </Suspense>
  254. </PrivateRoute>
  255. }
  256. />
  257. <Route
  258. path='/console/topup'
  259. element={
  260. <PrivateRoute>
  261. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  262. <TopUp />
  263. </Suspense>
  264. </PrivateRoute>
  265. }
  266. />
  267. <Route
  268. path='/console/log'
  269. element={
  270. <PrivateRoute>
  271. <Log />
  272. </PrivateRoute>
  273. }
  274. />
  275. <Route
  276. path='/console'
  277. element={
  278. <PrivateRoute>
  279. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  280. <Dashboard />
  281. </Suspense>
  282. </PrivateRoute>
  283. }
  284. />
  285. <Route
  286. path='/console/midjourney'
  287. element={
  288. <PrivateRoute>
  289. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  290. <Midjourney />
  291. </Suspense>
  292. </PrivateRoute>
  293. }
  294. />
  295. <Route
  296. path='/console/task'
  297. element={
  298. <PrivateRoute>
  299. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  300. <Task />
  301. </Suspense>
  302. </PrivateRoute>
  303. }
  304. />
  305. <Route
  306. path='/pricing'
  307. element={
  308. pricingRequireAuth ? (
  309. <PrivateRoute>
  310. <Suspense
  311. fallback={<Loading></Loading>}
  312. key={location.pathname}
  313. >
  314. <Pricing />
  315. </Suspense>
  316. </PrivateRoute>
  317. ) : (
  318. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  319. <Pricing />
  320. </Suspense>
  321. )
  322. }
  323. />
  324. <Route
  325. path='/about'
  326. element={
  327. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  328. <About />
  329. </Suspense>
  330. }
  331. />
  332. <Route
  333. path='/user-agreement'
  334. element={
  335. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  336. <UserAgreement />
  337. </Suspense>
  338. }
  339. />
  340. <Route
  341. path='/privacy-policy'
  342. element={
  343. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  344. <PrivacyPolicy />
  345. </Suspense>
  346. }
  347. />
  348. <Route
  349. path='/console/chat/:id?'
  350. element={
  351. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  352. <Chat />
  353. </Suspense>
  354. }
  355. />
  356. {/* 方便使用chat2link直接跳转聊天... */}
  357. <Route
  358. path='/chat2link'
  359. element={
  360. <PrivateRoute>
  361. <Suspense fallback={<Loading></Loading>} key={location.pathname}>
  362. <Chat2Link />
  363. </Suspense>
  364. </PrivateRoute>
  365. }
  366. />
  367. <Route path='*' element={<NotFound />} />
  368. </Routes>
  369. </SetupCheck>
  370. );
  371. }
  372. export default App;