Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/index.tsx
3608 views
1
/**
2
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
// this should stay at the top to enable monitoring as soon as possible
8
import "./service/metrics";
9
10
import "setimmediate"; // important!, required by vscode-jsonrpc
11
import dayjs from "dayjs";
12
import duration from "dayjs/plugin/duration";
13
import relativeTime from "dayjs/plugin/relativeTime";
14
import utc from "dayjs/plugin/utc";
15
import React from "react";
16
import ReactDOM from "react-dom";
17
import { BrowserRouter } from "react-router-dom";
18
import { RootAppRouter } from "./App";
19
import { QueryErrorBoundary } from "./components/error-boundaries/QueryErrorBoundary";
20
import { ReloadPageErrorBoundary } from "./components/error-boundaries/ReloadPageErrorBoundary";
21
import { ToastContextProvider } from "./components/toasts/Toasts";
22
import { ConfettiContextProvider } from "./contexts/ConfettiContext";
23
import { setupQueryClientProvider } from "./data/setup";
24
import "./index.css";
25
import { PaymentContextProvider } from "./payment-context";
26
import { ThemeContextProvider } from "./theme-context";
27
import { UserContextProvider } from "./user-context";
28
import { getURLHash, isWebsiteSlug } from "./utils";
29
// Import the minimal login HTML template at build time
30
import minimalLoginHtml from "./minimal-login.html";
31
32
const MINIMAL_MODE_OVERRIDE_KEY = "minimal_gitpod_io_mode";
33
34
/**
35
* Check if the pathname is a known app route that should show the minimal login page
36
*/
37
function isAppRoute(pathname: string): boolean {
38
const appRoutes = [
39
"/workspaces",
40
"/new",
41
"/start",
42
"/settings",
43
"/billing",
44
"/members",
45
"/sso",
46
"/orgs",
47
"/repositories",
48
"/prebuilds",
49
"/admin",
50
"/login",
51
"/oauth-approval",
52
"/blocked",
53
"/from-referrer",
54
"/usage",
55
"/insights",
56
"/org-admin",
57
"/quickstart",
58
// Legacy routes that redirect in full app
59
"/account",
60
"/integrations",
61
"/notifications",
62
"/preferences",
63
"/variables",
64
"/tokens",
65
"/keys",
66
"/open",
67
"/sorry",
68
"/t/",
69
"/projects/",
70
"/user/",
71
"/access-control",
72
"/old-team-plans",
73
"/teams",
74
"/subscription",
75
"/upgrade-subscription",
76
"/plans",
77
];
78
79
return appRoutes.some((route) => pathname === route || pathname.startsWith(route + "/"));
80
}
81
82
/**
83
* Handle requests in minimal gitpod.io mode.
84
* This runs instead of the full React app when minimal mode is enabled.
85
*/
86
function handleMinimalGitpodIoMode(): void {
87
const pathname = window.location.pathname;
88
const search = window.location.search;
89
const hash = window.location.hash;
90
const hashContent = hash.replace(/^#\/?/, "");
91
92
// 1. Website slugs -> www.gitpod.io
93
if (isWebsiteSlug(pathname)) {
94
window.location.href = `https://www.gitpod.io${pathname}${search}`;
95
return;
96
}
97
98
// 2. Hash-based workspace creation -> app.ona.com
99
if (hashContent !== "") {
100
// Normalize github.dev to github.com first
101
let normalizedHash = hash;
102
if (/^#\/?((https:\/\/)?github\.dev\/)/i.test(hash)) {
103
normalizedHash = hash.replace(/github\.dev\//gi, "github.com/");
104
}
105
window.location.href = `https://app.ona.com/${normalizedHash}`;
106
return;
107
}
108
109
// 3. Legacy URL formats (/github.com/..., /gitlab.com/..., /bitbucket.org/...)
110
if (/^\/(github|gitlab|bitbucket)\.(com|org)\//.test(pathname)) {
111
window.location.href = `https://app.ona.com/#${pathname.slice(1)}${search}`;
112
return;
113
}
114
115
// 4. App routes -> render minimal login page
116
if (isAppRoute(pathname)) {
117
renderMinimalLoginPage();
118
return;
119
}
120
121
// 5. Root path -> render minimal login page
122
if (pathname === "/" || pathname === "") {
123
renderMinimalLoginPage();
124
return;
125
}
126
127
// 6. Unknown paths -> www.gitpod.io (let website handle 404)
128
window.location.href = `https://www.gitpod.io${pathname}${search}`;
129
}
130
131
/**
132
* Extract body content from a full HTML document string.
133
* This allows keeping the complete HTML structure in the source file for easier editing.
134
*/
135
function extractBodyContent(html: string): string {
136
const parser = new DOMParser();
137
const doc = parser.parseFromString(html, "text/html");
138
return doc.body.innerHTML;
139
}
140
141
/**
142
* Render a minimal static login page without React.
143
* Uses innerHTML instead of document.write() to avoid deprecation issues.
144
*/
145
function renderMinimalLoginPage(): void {
146
const bodyContent = extractBodyContent(minimalLoginHtml);
147
const root = document.getElementById("root");
148
if (root) {
149
root.innerHTML = bodyContent;
150
} else {
151
// Fallback if root doesn't exist
152
document.body.innerHTML = bodyContent;
153
}
154
}
155
156
/**
157
* Boot the full React application
158
*/
159
function bootFullApp(): void {
160
// Normalize github.dev urls to github.com
161
const hash = getURLHash();
162
if (/^(https:\/\/)?github\.dev\//i.test(hash)) {
163
window.location.hash = hash.replace(/^(https:\/\/)?github\.dev\//i, "https://github.com/");
164
} else if (/^([^/]+?=[^/]*?|prebuild)\/(https:\/\/)?github\.dev\//i.test(hash)) {
165
window.location.hash = hash.replace(
166
/^([^/]+?=[^/]*?|prebuild)\/(https:\/\/)?github\.dev\//i,
167
"$1/https://github.com/",
168
);
169
}
170
171
const GitpodQueryClientProvider = setupQueryClientProvider();
172
173
// Configure libraries
174
dayjs.extend(relativeTime);
175
dayjs.extend(utc);
176
dayjs.extend(duration);
177
178
// Render the App
179
ReactDOM.render(
180
<React.StrictMode>
181
<ThemeContextProvider>
182
<ReloadPageErrorBoundary>
183
<BrowserRouter>
184
<GitpodQueryClientProvider>
185
{/* This needs to be inside of the GitpodQueryClientProvider so it can reset queries if needed */}
186
<QueryErrorBoundary>
187
<ConfettiContextProvider>
188
<ToastContextProvider>
189
<UserContextProvider>
190
<PaymentContextProvider>
191
<RootAppRouter />
192
</PaymentContextProvider>
193
</UserContextProvider>
194
</ToastContextProvider>
195
</ConfettiContextProvider>
196
</QueryErrorBoundary>
197
</GitpodQueryClientProvider>
198
</BrowserRouter>
199
</ReloadPageErrorBoundary>
200
</ThemeContextProvider>
201
</React.StrictMode>,
202
document.getElementById("root"),
203
);
204
}
205
206
/**
207
* Check if this is exactlly gitpod.io
208
*/
209
function isExactGitpodIo(): boolean {
210
return window.location.hostname === "gitpod.io";
211
}
212
213
/**
214
* Main boot function
215
*
216
* Minimal mode is enabled when:
217
* - localStorage override is "true" (for testing in preview environments)
218
* - Host is exactly "gitpod.io" AND path is not a website slug
219
*/
220
const bootApp = () => {
221
let minimalMode = false;
222
223
// Handle website slugs on gitpod.io - redirect to www.gitpod.io
224
if (isExactGitpodIo()) {
225
const pathname = window.location.pathname;
226
if (isWebsiteSlug(pathname)) {
227
window.location.href = `https://www.gitpod.io${pathname}${window.location.search}`;
228
return;
229
}
230
minimalMode = true;
231
}
232
233
// Check local storage override
234
try {
235
if (localStorage.getItem(MINIMAL_MODE_OVERRIDE_KEY) === "true") {
236
minimalMode = true;
237
}
238
} catch {
239
// localStorage might not be available
240
}
241
242
if (minimalMode) {
243
handleMinimalGitpodIoMode();
244
return;
245
}
246
247
// Boot full React app
248
bootFullApp();
249
};
250
251
bootApp();
252
253