App.tsx 9.1 KB

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