App.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  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 <http://www.gnu.org/licenses/>.
  13. */
  14. import { hot } from "react-hot-loader/root";
  15. import React from "react";
  16. import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
  17. import styled, { createGlobalStyle } from "styled-components";
  18. import { observe } from "mobx";
  19. import Fonts from "@src/components/ui/Fonts";
  20. import NotificationsModule from "@src/components/modules/NotificationsModule";
  21. import LoginPage from "@src/components/smart/LoginPage";
  22. import TransfersPage from "@src/components/smart/TransfersPage";
  23. import MessagePage from "@src/components/smart/MessagePage";
  24. import TransferDetailsPage from "@src/components/smart/TransferDetailsPage";
  25. import DeploymentsPage from "@src/components/smart/DeploymentsPage";
  26. import DeploymentDetailsPage from "@src/components/smart/DeploymentDetailsPage";
  27. import MetalHubServersPage from "@src/components/smart/MetalHubServersPage";
  28. import EndpointsPage from "@src/components/smart/EndpointsPage";
  29. import EndpointDetailsPage from "@src/components/smart/EndpointDetailsPage";
  30. import UsersPage from "@src/components/smart/UsersPage";
  31. import UserDetailsPage from "@src/components/smart/UserDetailsPage";
  32. import ProjectsPage from "@src/components/smart/ProjectsPage";
  33. import ProjectDetailsPage from "@src/components/smart/ProjectDetailsPage";
  34. import DashboardPage from "@src/components/smart/DashboardPage";
  35. import LogsPage from "@src/components/smart/LogsPage";
  36. import LogStreamPage from "@src/components/smart/LogStreamPage";
  37. import WizardPage from "@src/components/smart/WizardPage";
  38. import Tooltip from "@src/components/ui/Tooltip";
  39. import MinionPoolsPage from "@src/components/smart/MinionPoolsPage";
  40. import MinionPoolDetailsPage from "@src/components/smart/MinionPoolDetailsPage";
  41. import { ThemePalette, ThemeProps } from "@src/components/Theme";
  42. import configLoader from "@src/utils/Config";
  43. import { navigationMenu } from "@src/constants";
  44. import userStore from "@src/stores/UserStore";
  45. import SetupPage from "@src/components/smart/SetupPage";
  46. import MetalHubServerDetailsPage from "@src/components/smart/MetalHubServerDetailsPage";
  47. const GlobalStyle = createGlobalStyle`
  48. ${Fonts}
  49. html, body, main {
  50. height: 100%;
  51. display: flex;
  52. flex-direction: column;
  53. min-height: 0;
  54. }
  55. body {
  56. margin: 0;
  57. color: ${ThemePalette.black};
  58. font-family: Rubik;
  59. font-size: 14px;
  60. font-weight: ${ThemeProps.fontWeights.regular};
  61. -webkit-font-smoothing: antialiased;
  62. -moz-osx-font-smoothing: grayscale;
  63. }
  64. `;
  65. const Wrapper = styled.div`
  66. height: 100%;
  67. min-height: 0;
  68. display: flex;
  69. flex-direction: column;
  70. > div:first-child {
  71. height: 100%;
  72. }
  73. `;
  74. type State = {
  75. isConfigReady: boolean;
  76. };
  77. class App extends React.Component<Record<string, unknown>, State> {
  78. state = {
  79. isConfigReady: false,
  80. };
  81. async componentDidMount() {
  82. observe(userStore, "loggedUser", () => {
  83. this.setState({});
  84. });
  85. await configLoader.load();
  86. if (configLoader.isFirstLaunch && window.location.pathname !== "/login") {
  87. if (window.location.pathname !== "/") {
  88. window.location.href = "/";
  89. return;
  90. }
  91. } else {
  92. userStore.tokenLogin();
  93. }
  94. this.setState({ isConfigReady: true });
  95. }
  96. render() {
  97. if (!this.state.isConfigReady) {
  98. return null;
  99. }
  100. const renderMessagePage = (options: {
  101. path: string;
  102. exact?: boolean;
  103. title: string;
  104. subtitle: string;
  105. showAuthAnimation?: boolean;
  106. showDenied?: boolean;
  107. }) => (
  108. <Route
  109. path={options.path}
  110. // @ts-ignore
  111. exact={options.exact}
  112. render={() => (
  113. <MessagePage
  114. title={options.title}
  115. subtitle={options.subtitle}
  116. showAuthAnimation={options.showAuthAnimation}
  117. showDenied={options.showDenied}
  118. />
  119. )}
  120. />
  121. );
  122. const renderRoute = (
  123. path: string,
  124. component: React.ReactNode,
  125. exact?: boolean
  126. ) => {
  127. if (!userStore.loggedUser) {
  128. return renderMessagePage({
  129. path,
  130. exact,
  131. title: "Authenticating...",
  132. subtitle: "Please wait while authenticating user.",
  133. showAuthAnimation: true,
  134. });
  135. }
  136. // @ts-ignore
  137. return <Route path={path} component={component} exact={exact} />;
  138. };
  139. const renderOptionalRoute = (opts: {
  140. name: string;
  141. component: React.ReactNode;
  142. path?: string;
  143. exact?: boolean;
  144. }) => {
  145. const { name, component, path, exact } = opts;
  146. if (configLoader.config.disabledPages.find(p => p === name)) {
  147. return null;
  148. }
  149. const actualPath = `${path || `/${name}`}`;
  150. const requiresAdmin = Boolean(
  151. navigationMenu.find(n => n.value === name && n.requiresAdmin)
  152. );
  153. if (!requiresAdmin) {
  154. return renderRoute(actualPath, component, exact);
  155. }
  156. if (!userStore.loggedUser || userStore.loggedUser.isAdmin == null) {
  157. return renderMessagePage({
  158. path: actualPath,
  159. exact,
  160. title: "Checking permissions...",
  161. subtitle: "Please wait while checking user's permissions.",
  162. showAuthAnimation: true,
  163. });
  164. }
  165. if (userStore.loggedUser?.isAdmin === false) {
  166. return renderMessagePage({
  167. path: actualPath,
  168. exact,
  169. title: "User doesn't have permissions to view this page",
  170. subtitle:
  171. "Please login in with an administrator acount to view this page.",
  172. showDenied: true,
  173. });
  174. }
  175. if (userStore.loggedUser?.isAdmin) {
  176. // @ts-ignore
  177. return <Route path={actualPath} exact={exact} component={component} />;
  178. }
  179. return null;
  180. };
  181. return (
  182. <Wrapper>
  183. <GlobalStyle />
  184. <Router>
  185. <Switch>
  186. {configLoader.isFirstLaunch ? (
  187. // @ts-ignore
  188. <Route path="/" component={SetupPage} exact />
  189. ) : (
  190. // @ts-ignore
  191. renderRoute("/", DashboardPage, true)
  192. )}
  193. {
  194. // @ts-ignore
  195. <Route path="/login" component={LoginPage} />
  196. }
  197. {renderRoute("/dashboard", DashboardPage)}
  198. {renderRoute("/transfers", TransfersPage, true)}
  199. {renderRoute("/transfers/:id", TransferDetailsPage, true)}
  200. {renderRoute("/transfers/:id/:page", TransferDetailsPage)}
  201. {renderRoute("/deployments", DeploymentsPage, true)}
  202. {renderRoute("/deployments/:id", DeploymentDetailsPage, true)}
  203. {renderRoute("/deployments/:id/:page", DeploymentDetailsPage)}
  204. {renderRoute("/endpoints", EndpointsPage, true)}
  205. {renderRoute("/endpoints/:id", EndpointDetailsPage)}
  206. {renderRoute("/minion-pools", MinionPoolsPage, true)}
  207. {renderRoute("/minion-pools/:id", MinionPoolDetailsPage, true)}
  208. {renderRoute("/minion-pools/:id/:page", MinionPoolDetailsPage)}
  209. {renderRoute("/bare-metal-servers", MetalHubServersPage, true)}
  210. {renderRoute("/bare-metal-servers/:id", MetalHubServerDetailsPage)}
  211. {renderRoute("/wizard/:type", WizardPage)}
  212. {renderOptionalRoute({
  213. name: "users",
  214. component: UsersPage,
  215. exact: true,
  216. })}
  217. {renderOptionalRoute({
  218. name: "users",
  219. component: UserDetailsPage,
  220. path: "/users/:id",
  221. })}
  222. {renderOptionalRoute({
  223. name: "projects",
  224. component: ProjectsPage,
  225. exact: true,
  226. })}
  227. {renderOptionalRoute({
  228. name: "projects",
  229. component: ProjectDetailsPage,
  230. path: "/projects/:id",
  231. })}
  232. {renderOptionalRoute({ name: "logging", component: LogsPage })}
  233. {renderRoute("/streamlog", LogStreamPage)}
  234. {
  235. // @ts-ignore
  236. <Route component={MessagePage} />
  237. }
  238. </Switch>
  239. </Router>
  240. <NotificationsModule />
  241. <Tooltip />
  242. </Wrapper>
  243. );
  244. }
  245. }
  246. export default hot(App);