argo-cd

Форк
0
/
app.tsx 
274 строки · 11.8 Кб
1
import {DataLoader, NavigationManager, Notifications, NotificationsManager, PageContext, Popup, PopupManager, PopupProps} 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
import applications from './applications';
8
import help from './help';
9
import login from './login';
10
import settings from './settings';
11
import {Layout} from './shared/components/layout/layout';
12
import {Page} from './shared/components/page/page';
13
import {VersionPanel} from './shared/components/version-info/version-info-panel';
14
import {AuthSettingsCtx, Provider} from './shared/context';
15
import {services} from './shared/services';
16
import requests from './shared/services/requests';
17
import {hashCode} from './shared/utils';
18
import {Banner} from './ui-banner/ui-banner';
19
import userInfo from './user-info';
20
import {AuthSettings} from './shared/models';
21
import {PKCEVerification} from './login/components/pkce-verify';
22

23
services.viewPreferences.init();
24
const bases = document.getElementsByTagName('base');
25
const base = bases.length > 0 ? bases[0].getAttribute('href') || '/' : '/';
26
export const history = createBrowserHistory({basename: base});
27
requests.setBaseHRef(base);
28

29
type Routes = {[path: string]: {component: React.ComponentType<RouteComponentProps<any>>; noLayout?: boolean; extension?: boolean}};
30

31
const routes: Routes = {
32
    '/login': {component: login.component as any, noLayout: true},
33
    '/applications': {component: applications.component},
34
    '/settings': {component: settings.component},
35
    '/user-info': {component: userInfo.component},
36
    '/help': {component: help.component},
37
    '/pkce/verify': {component: PKCEVerification, noLayout: true}
38
};
39

40
interface NavItem {
41
    title: string;
42
    tooltip?: string;
43
    path: string;
44
    iconClassName: string;
45
}
46

47
const navItems: NavItem[] = [
48
    {
49
        title: 'Applications',
50
        tooltip: 'Manage your applications, and diagnose health problems.',
51
        path: '/applications',
52
        iconClassName: 'argo-icon argo-icon-application'
53
    },
54
    {
55
        title: 'Settings',
56
        tooltip: 'Manage your repositories, projects, settings',
57
        path: '/settings',
58
        iconClassName: 'argo-icon argo-icon-settings'
59
    },
60
    {
61
        title: 'User Info',
62
        path: '/user-info',
63
        iconClassName: 'fa fa-user-circle'
64
    },
65
    {
66
        title: 'Documentation',
67
        tooltip: 'Read the documentation, and get help and assistance.',
68
        path: '/help',
69
        iconClassName: 'argo-icon argo-icon-docs'
70
    }
71
];
72

73
const versionLoader = services.version.version();
74

75
async function isExpiredSSO() {
76
    try {
77
        const {iss} = await services.users.get();
78
        const authSettings = await services.authService.settings();
79
        if (iss && iss !== 'argocd') {
80
            return ((authSettings.dexConfig && authSettings.dexConfig.connectors) || []).length > 0 || authSettings.oidcConfig;
81
        }
82
    } catch {
83
        return false;
84
    }
85
    return false;
86
}
87

88
requests.onError.subscribe(async err => {
89
    if (err.status === 401) {
90
        if (history.location.pathname.startsWith('/login')) {
91
            return;
92
        }
93

94
        const isSSO = await isExpiredSSO();
95
        // location might change after async method call, so we need to check again.
96
        if (history.location.pathname.startsWith('/login')) {
97
            return;
98
        }
99
        // Query for basehref and remove trailing /.
100
        // If basehref is the default `/` it will become an empty string.
101
        const basehref = document
102
            .querySelector('head > base')
103
            .getAttribute('href')
104
            .replace(/\/$/, '');
105
        if (isSSO) {
106
            window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
107
        } else {
108
            history.push(`/login?return_url=${encodeURIComponent(location.href)}`);
109
        }
110
    }
111
});
112

113
export class App extends React.Component<
114
    {},
115
    {popupProps: PopupProps; showVersionPanel: boolean; error: Error; navItems: NavItem[]; routes: Routes; extensionsLoaded: boolean; authSettings: AuthSettings}
116
> {
117
    public static childContextTypes = {
118
        history: PropTypes.object,
119
        apis: PropTypes.object
120
    };
121

122
    public static getDerivedStateFromError(error: Error) {
123
        return {error};
124
    }
125

126
    private popupManager: PopupManager;
127
    private notificationsManager: NotificationsManager;
128
    private navigationManager: NavigationManager;
129
    private navItems: NavItem[];
130
    private routes: Routes;
131

132
    constructor(props: {}) {
133
        super(props);
134
        this.state = {popupProps: null, error: null, showVersionPanel: false, navItems: [], routes: null, extensionsLoaded: false, authSettings: null};
135
        this.popupManager = new PopupManager();
136
        this.notificationsManager = new NotificationsManager();
137
        this.navigationManager = new NavigationManager(history);
138
        this.navItems = navItems;
139
        this.routes = routes;
140
    }
141

142
    public async componentDidMount() {
143
        this.popupManager.popupProps.subscribe(popupProps => this.setState({popupProps}));
144
        const authSettings = await services.authService.settings();
145
        const {trackingID, anonymizeUsers} = authSettings.googleAnalytics || {trackingID: '', anonymizeUsers: true};
146
        const {loggedIn, username} = await services.users.get();
147
        if (trackingID) {
148
            const ga = await import('react-ga');
149
            ga.initialize(trackingID);
150
            const trackPageView = () => {
151
                if (loggedIn && username) {
152
                    const userId = !anonymizeUsers ? username : hashCode(username).toString();
153
                    ga.set({userId});
154
                }
155
                ga.pageview(location.pathname + location.search);
156
            };
157
            trackPageView();
158
            history.listen(trackPageView);
159
        }
160
        if (authSettings.uiCssURL) {
161
            const link = document.createElement('link');
162
            link.href = authSettings.uiCssURL;
163
            link.rel = 'stylesheet';
164
            link.type = 'text/css';
165
            document.head.appendChild(link);
166
        }
167

168
        const systemExtensions = services.extensions.getSystemExtensions();
169
        const extendedNavItems = this.navItems;
170
        const extendedRoutes = this.routes;
171
        for (const extension of systemExtensions) {
172
            extendedNavItems.push({
173
                title: extension.title,
174
                path: extension.path,
175
                iconClassName: `fa ${extension.icon}`
176
            });
177
            const component = () => (
178
                <>
179
                    <Helmet>
180
                        <title>{extension.title} - Argo CD</title>
181
                    </Helmet>
182
                    <Page title={extension.title}>
183
                        <extension.component />
184
                    </Page>
185
                </>
186
            );
187
            extendedRoutes[extension.path] = {
188
                component: component as React.ComponentType<React.ComponentProps<any>>,
189
                extension: true
190
            };
191
        }
192

193
        this.setState({...this.state, navItems: extendedNavItems, routes: extendedRoutes, extensionsLoaded: true, authSettings});
194
    }
195

196
    public render() {
197
        if (this.state.error != null) {
198
            const stack = this.state.error.stack;
199
            const url = 'https://github.com/argoproj/argo-cd/issues/new?labels=bug&template=bug_report.md';
200

201
            return (
202
                <React.Fragment>
203
                    <p>Something went wrong!</p>
204
                    <p>
205
                        Consider submitting an issue <a href={url}>here</a>.
206
                    </p>
207
                    <br />
208
                    <p>Stacktrace:</p>
209
                    <pre>{stack}</pre>
210
                </React.Fragment>
211
            );
212
        }
213

214
        return (
215
            <React.Fragment>
216
                <Helmet>
217
                    <link rel='icon' type='image/png' href={`${base}assets/favicon/favicon-32x32.png`} sizes='32x32' />
218
                    <link rel='icon' type='image/png' href={`${base}assets/favicon/favicon-16x16.png`} sizes='16x16' />
219
                </Helmet>
220
                <PageContext.Provider value={{title: 'Argo CD'}}>
221
                    <Provider value={{history, popup: this.popupManager, notifications: this.notificationsManager, navigation: this.navigationManager, baseHref: base}}>
222
                        <DataLoader load={() => services.viewPreferences.getPreferences()}>
223
                            {pref => <div className={pref.theme ? 'theme-' + pref.theme : 'theme-light'}>{this.state.popupProps && <Popup {...this.state.popupProps} />}</div>}
224
                        </DataLoader>
225
                        <AuthSettingsCtx.Provider value={this.state.authSettings}>
226
                            <Router history={history}>
227
                                <Switch>
228
                                    <Redirect exact={true} path='/' to='/applications' />
229
                                    {Object.keys(this.routes).map(path => {
230
                                        const route = this.routes[path];
231
                                        return (
232
                                            <Route
233
                                                key={path}
234
                                                path={path}
235
                                                render={routeProps =>
236
                                                    route.noLayout ? (
237
                                                        <div>
238
                                                            <route.component {...routeProps} />
239
                                                        </div>
240
                                                    ) : (
241
                                                        <DataLoader load={() => services.viewPreferences.getPreferences()}>
242
                                                            {pref => (
243
                                                                <Layout
244
                                                                    onVersionClick={() => this.setState({showVersionPanel: true})}
245
                                                                    navItems={this.navItems}
246
                                                                    pref={pref}
247
                                                                    isExtension={route.extension}>
248
                                                                    <Banner>
249
                                                                        <route.component {...routeProps} />
250
                                                                    </Banner>
251
                                                                </Layout>
252
                                                            )}
253
                                                        </DataLoader>
254
                                                    )
255
                                                }
256
                                            />
257
                                        );
258
                                    })}
259
                                    {this.state.extensionsLoaded && <Redirect path='*' to='/' />}
260
                                </Switch>
261
                            </Router>
262
                        </AuthSettingsCtx.Provider>
263
                    </Provider>
264
                </PageContext.Provider>
265
                <Notifications notifications={this.notificationsManager.notifications} />
266
                <VersionPanel version={versionLoader} isShown={this.state.showVersionPanel} onClose={() => this.setState({showVersionPanel: false})} />
267
            </React.Fragment>
268
        );
269
    }
270

271
    public getChildContext() {
272
        return {history, apis: {popup: this.popupManager, notifications: this.notificationsManager, navigation: this.navigationManager}};
273
    }
274
}
275

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.