github.com/argoproj/argo-cd@v1.8.7/ui/src/app/app.tsx (about)

     1  import {DataLoader, Layout, NavigationManager, Notifications, NotificationsManager, PageContext, Popup, PopupManager, PopupProps, Tooltip} from 'argo-ui';
     2  import {createBrowserHistory} from 'history';
     3  import * as PropTypes from 'prop-types';
     4  import * as React from 'react';
     5  import {Helmet} from 'react-helmet';
     6  import {Redirect, Route, RouteComponentProps, Router, Switch} from 'react-router';
     7  
     8  import applications from './applications';
     9  import help from './help';
    10  import login from './login';
    11  import settings from './settings';
    12  import {VersionPanel} from './shared/components/version-info/version-info-panel';
    13  import {Provider} from './shared/context';
    14  import {services} from './shared/services';
    15  import requests from './shared/services/requests';
    16  import {hashCode} from './shared/utils';
    17  import userInfo from './user-info';
    18  
    19  services.viewPreferences.init();
    20  const bases = document.getElementsByTagName('base');
    21  const base = bases.length > 0 ? bases[0].getAttribute('href') || '/' : '/';
    22  export const history = createBrowserHistory({basename: base});
    23  requests.setBaseHRef(base);
    24  
    25  const routes: {[path: string]: {component: React.ComponentType<RouteComponentProps<any>>; noLayout?: boolean}} = {
    26      '/login': {component: login.component as any, noLayout: true},
    27      '/applications': {component: applications.component},
    28      '/settings': {component: settings.component},
    29      '/user-info': {component: userInfo.component},
    30      '/help': {component: help.component}
    31  };
    32  
    33  const navItems = [
    34      {
    35          title: 'Manage your applications, and diagnose health problems.',
    36          path: '/applications',
    37          iconClassName: 'argo-icon-application'
    38      },
    39      {
    40          title: 'Manage your repositories, projects, settings',
    41          path: '/settings',
    42          iconClassName: 'argo-icon-settings'
    43      },
    44      {
    45          title: 'User Info',
    46          path: '/user-info',
    47          iconClassName: 'fa fa-user-circle'
    48      },
    49      {
    50          title: 'Read the documentation, and get help and assistance.',
    51          path: '/help',
    52          iconClassName: 'argo-icon-docs'
    53      }
    54  ];
    55  
    56  const versionLoader = services.version.version();
    57  
    58  async function isExpiredSSO() {
    59      try {
    60          const {loggedIn, iss} = await services.users.get();
    61          if (loggedIn && iss !== 'argocd') {
    62              const authSettings = await services.authService.settings();
    63              return ((authSettings.dexConfig && authSettings.dexConfig.connectors) || []).length > 0 || authSettings.oidcConfig;
    64          }
    65      } catch {
    66          return false;
    67      }
    68      return false;
    69  }
    70  
    71  requests.onError.subscribe(async err => {
    72      if (err.status === 401) {
    73          if (history.location.pathname.startsWith('/login')) {
    74              return;
    75          }
    76  
    77          const isSSO = await isExpiredSSO();
    78          // location might change after async method call, so we need to check again.
    79          if (history.location.pathname.startsWith('/login')) {
    80              return;
    81          }
    82          // Query for basehref and remove trailing /.
    83          // If basehref is the default `/` it will become an empty string.
    84          const basehref = document
    85              .querySelector('head > base')
    86              .getAttribute('href')
    87              .replace(/\/$/, '');
    88          if (isSSO) {
    89              window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
    90          } else {
    91              history.push(`/login?return_url=${encodeURIComponent(location.href)}`);
    92          }
    93      }
    94  });
    95  
    96  export class App extends React.Component<{}, {popupProps: PopupProps; showVersionPanel: boolean; error: Error}> {
    97      public static childContextTypes = {
    98          history: PropTypes.object,
    99          apis: PropTypes.object
   100      };
   101  
   102      public static getDerivedStateFromError(error: Error) {
   103          return {error};
   104      }
   105  
   106      private popupManager: PopupManager;
   107      private notificationsManager: NotificationsManager;
   108      private navigationManager: NavigationManager;
   109  
   110      constructor(props: {}) {
   111          super(props);
   112          this.state = {popupProps: null, error: null, showVersionPanel: false};
   113          this.popupManager = new PopupManager();
   114          this.notificationsManager = new NotificationsManager();
   115          this.navigationManager = new NavigationManager(history);
   116      }
   117  
   118      public async componentDidMount() {
   119          this.popupManager.popupProps.subscribe(popupProps => this.setState({popupProps}));
   120          const authSettings = await services.authService.settings();
   121          const {trackingID, anonymizeUsers} = authSettings.googleAnalytics || {trackingID: '', anonymizeUsers: true};
   122          const {loggedIn, username} = await services.users.get();
   123          if (trackingID) {
   124              const ga = await import('react-ga');
   125              ga.initialize(trackingID);
   126              const trackPageView = () => {
   127                  if (loggedIn && username) {
   128                      const userId = !anonymizeUsers ? username : hashCode(username).toString();
   129                      ga.set({userId});
   130                  }
   131                  ga.pageview(location.pathname + location.search);
   132              };
   133              trackPageView();
   134              history.listen(trackPageView);
   135          }
   136          if (authSettings.uiCssURL) {
   137              const link = document.createElement('link');
   138              link.href = authSettings.uiCssURL;
   139              link.rel = 'stylesheet';
   140              link.type = 'text/css';
   141              document.head.appendChild(link);
   142          }
   143      }
   144  
   145      public render() {
   146          if (this.state.error != null) {
   147              const stack = this.state.error.stack;
   148              const url = 'https://github.com/argoproj/argo-cd/issues/new?labels=bug&template=bug_report.md';
   149  
   150              return (
   151                  <React.Fragment>
   152                      <p>Something went wrong!</p>
   153                      <p>
   154                          Consider submitting an issue <a href={url}>here</a>.
   155                      </p>
   156                      <br />
   157                      <p>Stacktrace:</p>
   158                      <pre>{stack}</pre>
   159                  </React.Fragment>
   160              );
   161          }
   162  
   163          return (
   164              <React.Fragment>
   165                  <Helmet>
   166                      <link rel='icon' type='image/png' href={`${base}assets/favicon/favicon-32x32.png`} sizes='32x32' />
   167                      <link rel='icon' type='image/png' href={`${base}assets/favicon/favicon-16x16.png`} sizes='16x16' />
   168                  </Helmet>
   169                  <PageContext.Provider value={{title: 'Argo CD'}}>
   170                      <Provider value={{history, popup: this.popupManager, notifications: this.notificationsManager, navigation: this.navigationManager, baseHref: base}}>
   171                          {this.state.popupProps && <Popup {...this.state.popupProps} />}
   172                          <Router history={history}>
   173                              <Switch>
   174                                  <Redirect exact={true} path='/' to='/applications' />
   175                                  {Object.keys(routes).map(path => {
   176                                      const route = routes[path];
   177                                      return (
   178                                          <Route
   179                                              key={path}
   180                                              path={path}
   181                                              render={routeProps =>
   182                                                  route.noLayout ? (
   183                                                      <div>
   184                                                          <route.component {...routeProps} />
   185                                                      </div>
   186                                                  ) : (
   187                                                      <Layout
   188                                                          navItems={navItems}
   189                                                          version={() => (
   190                                                              <DataLoader load={() => versionLoader}>
   191                                                                  {version => (
   192                                                                      <React.Fragment>
   193                                                                          <Tooltip content={version.Version}>
   194                                                                              <a style={{color: 'white'}} onClick={() => this.setState({showVersionPanel: true})}>
   195                                                                                  {version.Version}
   196                                                                              </a>
   197                                                                          </Tooltip>
   198                                                                      </React.Fragment>
   199                                                                  )}
   200                                                              </DataLoader>
   201                                                          )}>
   202                                                          <route.component {...routeProps} />
   203                                                      </Layout>
   204                                                  )
   205                                              }
   206                                          />
   207                                      );
   208                                  })}
   209                                  <Redirect path='*' to='/' />
   210                              </Switch>
   211                          </Router>
   212                          <DataLoader load={() => services.authService.settings()}>
   213                              {s =>
   214                                  (s.help && s.help.chatUrl && (
   215                                      <div style={{position: 'fixed', right: 10, bottom: 10}}>
   216                                          <a href={s.help.chatUrl} className='argo-button argo-button--special'>
   217                                              <i className='fas fa-comment-alt' /> {s.help.chatText}
   218                                          </a>
   219                                      </div>
   220                                  )) ||
   221                                  null
   222                              }
   223                          </DataLoader>
   224                      </Provider>
   225                  </PageContext.Provider>
   226                  <Notifications notifications={this.notificationsManager.notifications} />
   227                  <VersionPanel version={versionLoader} isShown={this.state.showVersionPanel} onClose={() => this.setState({showVersionPanel: false})} />
   228              </React.Fragment>
   229          );
   230      }
   231  
   232      public getChildContext() {
   233          return {history, apis: {popup: this.popupManager, notifications: this.notificationsManager, navigation: this.navigationManager}};
   234      }
   235  }