@justdanielndev/status-page 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc.cjs +1 -0
- package/.upptimerc.yml +42 -0
- package/CHANGELOG.md +791 -0
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/integration/incident.spec.js +9 -0
- package/cypress/integration/live-status.spec.js +12 -0
- package/cypress/plugins/index.js +17 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/index.js +20 -0
- package/cypress.json +4 -0
- package/i18n.yml +61 -0
- package/init-tests.ts +12 -0
- package/jest.config.js +4 -0
- package/package.json +78 -0
- package/post-process.ts +61 -0
- package/pre-process.ts +32 -0
- package/release.config.js +1 -0
- package/rollup.config.js +116 -0
- package/src/client.js +18 -0
- package/src/components/ActiveIncidents.svelte +79 -0
- package/src/components/ActiveScheduled.svelte +96 -0
- package/src/components/Graph.svelte +76 -0
- package/src/components/History.svelte +84 -0
- package/src/components/Incident.svelte +161 -0
- package/src/components/Incidents.svelte +83 -0
- package/src/components/LiveStatus.svelte +190 -0
- package/src/components/Loading.svelte +37 -0
- package/src/components/Nav.svelte +88 -0
- package/src/components/Scheduled.svelte +72 -0
- package/src/components/Summary.svelte +54 -0
- package/src/routes/_error.svelte +41 -0
- package/src/routes/_layout.svelte +110 -0
- package/src/routes/error.svelte +27 -0
- package/src/routes/history/[number].svelte +17 -0
- package/src/routes/incident/[number].svelte +13 -0
- package/src/routes/index.svelte +48 -0
- package/src/routes/rate-limit-exceeded.svelte +88 -0
- package/src/server.js +25 -0
- package/src/service-worker.js +15 -0
- package/src/template.html +34 -0
- package/src/utils/createOctokit.js +67 -0
- package/static/global.css +203 -0
- package/static/logo-192.png +0 -0
- package/static/logo-512.png +0 -0
- package/static/manifest.json +20 -0
- package/static/themes/dark.css +26 -0
- package/static/themes/light.css +26 -0
- package/static/themes/night.css +26 -0
- package/static/themes/ocean.css +26 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import config from "../data/config.json";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<style>
|
|
6
|
+
.loading {
|
|
7
|
+
text-align: center;
|
|
8
|
+
margin: 2.5rem 0;
|
|
9
|
+
}
|
|
10
|
+
svg {
|
|
11
|
+
zoom: 1.5;
|
|
12
|
+
}
|
|
13
|
+
</style>
|
|
14
|
+
|
|
15
|
+
<div class="loading">
|
|
16
|
+
<svg
|
|
17
|
+
width="38"
|
|
18
|
+
height="38"
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
stroke="#aaa"
|
|
21
|
+
title={config.loading}>
|
|
22
|
+
<g fill="none" fill-rule="evenodd">
|
|
23
|
+
<g transform="translate(1 1)" stroke-width="2">
|
|
24
|
+
<circle stroke-opacity=".5" cx="18" cy="18" r="18" />
|
|
25
|
+
<path d="M36 18c0-9.94-8.06-18-18-18">
|
|
26
|
+
<animateTransform
|
|
27
|
+
attributeName="transform"
|
|
28
|
+
type="rotate"
|
|
29
|
+
from="0 18 18"
|
|
30
|
+
to="360 18 18"
|
|
31
|
+
dur="1s"
|
|
32
|
+
repeatCount="indefinite" />
|
|
33
|
+
</path>
|
|
34
|
+
</g>
|
|
35
|
+
</g>
|
|
36
|
+
</svg>
|
|
37
|
+
</div>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import config from "../data/config.json";
|
|
3
|
+
export let segment;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<nav>
|
|
7
|
+
<div class="container">
|
|
8
|
+
{#if config["status-website"] && config["status-website"].logoUrl}
|
|
9
|
+
<div>
|
|
10
|
+
<a href={config["status-website"].logoHref || config.path} class="logo">
|
|
11
|
+
{#if config["status-website"] && !config["status-website"].hideNavLogo}
|
|
12
|
+
<img alt="" src={config["status-website"].logoUrl} />
|
|
13
|
+
{/if}
|
|
14
|
+
{#if config["status-website"] && !config["status-website"].hideNavTitle}
|
|
15
|
+
<div>{config["status-website"].name}</div>
|
|
16
|
+
{/if}
|
|
17
|
+
</a>
|
|
18
|
+
</div>
|
|
19
|
+
{/if}
|
|
20
|
+
<ul>
|
|
21
|
+
{#if config["status-website"] && config["status-website"].navbar}
|
|
22
|
+
{#each config["status-website"].navbar as item}
|
|
23
|
+
<li>
|
|
24
|
+
<a
|
|
25
|
+
aria-current={segment === (item.href === "/" ? undefined : item.href)
|
|
26
|
+
? "page"
|
|
27
|
+
: undefined}
|
|
28
|
+
href={item.href.replace("$OWNER", config.owner).replace("$REPO", config.repo)}
|
|
29
|
+
target={item.target || "_self"}>
|
|
30
|
+
{item.title}
|
|
31
|
+
</a>
|
|
32
|
+
</li>
|
|
33
|
+
{/each}
|
|
34
|
+
{/if}
|
|
35
|
+
{#if config["status-website"] && config["status-website"].navbarGitHub && !config["status-website"].navbar}
|
|
36
|
+
<li>
|
|
37
|
+
<a href={`https://github.com/${config.owner}/${config.repo}`}>
|
|
38
|
+
{config.i18n.navGitHub}
|
|
39
|
+
</a>
|
|
40
|
+
</li>
|
|
41
|
+
{/if}
|
|
42
|
+
</ul>
|
|
43
|
+
</div>
|
|
44
|
+
</nav>
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
nav {
|
|
48
|
+
font-weight: 300;
|
|
49
|
+
padding: 0 1em;
|
|
50
|
+
margin-bottom: 2rem;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
overflow-x: auto;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
ul {
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0;
|
|
58
|
+
display: flex;
|
|
59
|
+
list-style: none;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
a {
|
|
65
|
+
text-decoration: none;
|
|
66
|
+
padding: 1.5rem 2rem;
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.logo {
|
|
71
|
+
float: left;
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
padding: 0.5rem 0;
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
margin-right: 2rem;
|
|
77
|
+
}
|
|
78
|
+
.logo img {
|
|
79
|
+
margin-right: 1rem;
|
|
80
|
+
height: 3rem;
|
|
81
|
+
border-radius: 0.2rem;
|
|
82
|
+
}
|
|
83
|
+
.container {
|
|
84
|
+
display: flex;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
align-items: center;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Loading from "../components/Loading.svelte";
|
|
3
|
+
import { onMount } from "svelte";
|
|
4
|
+
import config from "../data/config.json";
|
|
5
|
+
import { cachedResponse, createOctokit, handleError } from "../utils/createOctokit";
|
|
6
|
+
|
|
7
|
+
let loading = true;
|
|
8
|
+
const octokit = createOctokit();
|
|
9
|
+
const owner = config.owner;
|
|
10
|
+
const repo = config.repo;
|
|
11
|
+
let incidents = [];
|
|
12
|
+
|
|
13
|
+
onMount(async () => {
|
|
14
|
+
try {
|
|
15
|
+
incidents = (
|
|
16
|
+
await cachedResponse(`maintenance-issues-${owner}-${repo}`, () =>
|
|
17
|
+
octokit.issues.listForRepo({
|
|
18
|
+
owner,
|
|
19
|
+
repo,
|
|
20
|
+
state: "closed",
|
|
21
|
+
filter: "all",
|
|
22
|
+
sort: "created",
|
|
23
|
+
direction: "desc",
|
|
24
|
+
labels: "maintenance",
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
).data;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
handleError(error);
|
|
30
|
+
}
|
|
31
|
+
incidents = incidents.map((incident, index) => {
|
|
32
|
+
incident.showHeading =
|
|
33
|
+
index === 0 ||
|
|
34
|
+
new Date(incidents[index - 1].created_at).toLocaleDateString() !==
|
|
35
|
+
new Date(incident.created_at).toLocaleDateString();
|
|
36
|
+
return incident;
|
|
37
|
+
});
|
|
38
|
+
loading = false;
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<section>
|
|
43
|
+
{#if loading}
|
|
44
|
+
<Loading />
|
|
45
|
+
{:else if incidents.length}
|
|
46
|
+
<h2>{config.i18n.pastScheduledMaintenance}</h2>
|
|
47
|
+
{#each incidents as incident}
|
|
48
|
+
{#if incident.showHeading}
|
|
49
|
+
<h3>{new Date(incident.created_at).toLocaleDateString(config.i18n.locale)}</h3>
|
|
50
|
+
{/if}
|
|
51
|
+
<article class="link degraded">
|
|
52
|
+
<div class="f">
|
|
53
|
+
<div>
|
|
54
|
+
<h4>{incident.title.replace("🛑", "").replace("⚠️", "").trim()}</h4>
|
|
55
|
+
<div>Completed</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="f r">
|
|
58
|
+
<a href={`${config.path}/incident/${incident.number}`}>
|
|
59
|
+
{config.i18n.incidentReport.replace(/\$NUMBER/g, incident.number)}
|
|
60
|
+
</a>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</article>
|
|
64
|
+
{/each}
|
|
65
|
+
{/if}
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<style>
|
|
69
|
+
h2 {
|
|
70
|
+
margin-top: 2rem;
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Loading from "../components/Loading.svelte";
|
|
3
|
+
import { onMount } from "svelte";
|
|
4
|
+
import config from "../data/config.json";
|
|
5
|
+
import { handleError } from "../utils/createOctokit";
|
|
6
|
+
|
|
7
|
+
export let slug;
|
|
8
|
+
let loading = true;
|
|
9
|
+
|
|
10
|
+
let { apiBaseUrl,userContentBaseUrl } = config["status-website"] || {};
|
|
11
|
+
if (!apiBaseUrl) apiBaseUrl = "https://api.github.com";
|
|
12
|
+
if (!userContentBaseUrl) userContentBaseUrl = "https://raw.githubusercontent.com";
|
|
13
|
+
|
|
14
|
+
const owner = config.owner;
|
|
15
|
+
const repo = config.repo;
|
|
16
|
+
let summary = null;
|
|
17
|
+
|
|
18
|
+
onMount(async () => {
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch(`${userContentBaseUrl}/${owner}/${repo}/master/history/summary.json`);
|
|
21
|
+
summary = (await res.json()).find((item) => item.slug === slug);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
handleError(error);
|
|
24
|
+
}
|
|
25
|
+
loading = false;
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.no-underline {
|
|
31
|
+
text-decoration: none;
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
34
|
+
|
|
35
|
+
<section>
|
|
36
|
+
{#if loading}
|
|
37
|
+
<Loading />
|
|
38
|
+
{:else if summary}
|
|
39
|
+
<h1>
|
|
40
|
+
<a class="no-underline" href={summary.url.startsWith('$') ? '#' : summary.url}>{summary.name}</a>
|
|
41
|
+
<span class={`tag ${summary.status}`}>
|
|
42
|
+
{summary.status === 'up' ? config.i18n.up : config.i18n.down}
|
|
43
|
+
</span>
|
|
44
|
+
</h1>
|
|
45
|
+
<dl>
|
|
46
|
+
<dt>{config.i18n.overallUptimeTitle}</dt>
|
|
47
|
+
<dd>{summary.uptime}</dd>
|
|
48
|
+
{#if summary.showAverageResponseTime === undefined || summary.showAverageResponseTime}
|
|
49
|
+
<dt>{config.i18n.averageResponseTimeTitle}</dt>
|
|
50
|
+
<dd>{summary.time}{config.i18n.ms}</dd>
|
|
51
|
+
{/if}
|
|
52
|
+
</dl>
|
|
53
|
+
{/if}
|
|
54
|
+
</section>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let status;
|
|
3
|
+
export let error;
|
|
4
|
+
|
|
5
|
+
const dev = process.env.NODE_ENV === "development";
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<style>
|
|
9
|
+
h1,
|
|
10
|
+
p {
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
h1 {
|
|
15
|
+
font-size: 2.8em;
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
margin: 0 0 0.5em 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
p {
|
|
21
|
+
margin: 1em auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@media (min-width: 480px) {
|
|
25
|
+
h1 {
|
|
26
|
+
font-size: 4em;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
30
|
+
|
|
31
|
+
<svelte:head>
|
|
32
|
+
<title>{status}</title>
|
|
33
|
+
</svelte:head>
|
|
34
|
+
|
|
35
|
+
<h1>{status}</h1>
|
|
36
|
+
|
|
37
|
+
<p>{error.message}</p>
|
|
38
|
+
|
|
39
|
+
{#if dev && error.stack}
|
|
40
|
+
<pre>{error.stack}</pre>
|
|
41
|
+
{/if}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Nav from "../components/Nav.svelte";
|
|
3
|
+
import config from "../data/config.json";
|
|
4
|
+
import snarkdown from "snarkdown";
|
|
5
|
+
export let segment;
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<svelte:head>
|
|
9
|
+
{#if (config["status-website"] || {}).customHeadHtml}
|
|
10
|
+
{@html (config["status-website"] || {}).customHeadHtml}
|
|
11
|
+
{/if}
|
|
12
|
+
{#if (config["status-website"] || {}).themeUrl}
|
|
13
|
+
<link rel="stylesheet" href={(config["status-website"] || {}).themeUrl} />
|
|
14
|
+
{:else if (config["status-website"] || {}).theme}
|
|
15
|
+
<link
|
|
16
|
+
rel="stylesheet"
|
|
17
|
+
href={`${config.path}/themes/${config["status-website"].theme}.css`}
|
|
18
|
+
/>
|
|
19
|
+
{:else}
|
|
20
|
+
<!-- https://caniuse.com/prefers-color-scheme -->
|
|
21
|
+
<!-- https://web.dev/prefers-color-scheme/ -->
|
|
22
|
+
<script>
|
|
23
|
+
// If `prefers-color-scheme` is not supported, fall back to light mode.
|
|
24
|
+
// In this case, light.css will be downloaded with `highest` priority.
|
|
25
|
+
if (typeof window !== "undefined" && typeof document !== "undefined" && "matchMedia" in window && window.matchMedia('(prefers-color-scheme: dark)').media === 'not all') {
|
|
26
|
+
document.documentElement.style.display = 'none';
|
|
27
|
+
document.head.insertAdjacentHTML(
|
|
28
|
+
'beforeend',
|
|
29
|
+
'<link rel="stylesheet" href={`${config.path}/themes/light.css`} onload="document.documentElement.style.display = \'\'">',
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
<link
|
|
34
|
+
rel="stylesheet"
|
|
35
|
+
href={`${config.path}/themes/light.css`}
|
|
36
|
+
media="(prefers-color-scheme: light)"
|
|
37
|
+
/>
|
|
38
|
+
<link
|
|
39
|
+
rel="stylesheet"
|
|
40
|
+
href={`${config.path}/themes/dark.css`}
|
|
41
|
+
media="(prefers-color-scheme: dark)"
|
|
42
|
+
/>
|
|
43
|
+
{/if}
|
|
44
|
+
<link rel="stylesheet" href={`${config.path}/global.css`} />
|
|
45
|
+
<link
|
|
46
|
+
rel="icon"
|
|
47
|
+
type="image/svg"
|
|
48
|
+
href={(config["status-website"] || {}).faviconSvg ||
|
|
49
|
+
(config["status-website"] || {}).favicon ||
|
|
50
|
+
`https://raw.githubusercontent.com/upptime/upptime/master/assets/upptime-icon.svg`}
|
|
51
|
+
/>
|
|
52
|
+
<link
|
|
53
|
+
rel="icon"
|
|
54
|
+
type="image/png"
|
|
55
|
+
href={(config["status-website"] || {}).favicon || `/logo-192.png`}
|
|
56
|
+
/>
|
|
57
|
+
{#if (config["status-website"] || {}).scripts}
|
|
58
|
+
{#each (config["status-website"] || {}).scripts as script}<script
|
|
59
|
+
src={script.src}
|
|
60
|
+
async={!!script.async}
|
|
61
|
+
defer={!!script.async}>
|
|
62
|
+
</script>{/each}
|
|
63
|
+
{/if}
|
|
64
|
+
{#if (config["status-website"] || {}).links}
|
|
65
|
+
{#each (config["status-website"] || {}).links as link}
|
|
66
|
+
<link rel={link.rel} href={link.href} media={link.media} />
|
|
67
|
+
{/each}
|
|
68
|
+
{/if}
|
|
69
|
+
{#if (config["status-website"] || {}).metaTags}
|
|
70
|
+
{#each (config["status-website"] || {}).metaTags as link}
|
|
71
|
+
<meta name={link.name} content={link.content} />
|
|
72
|
+
{/each}
|
|
73
|
+
{/if}
|
|
74
|
+
{#if config['status-website'].css}
|
|
75
|
+
{@html `<style>${config['status-website'].css}</style>`}
|
|
76
|
+
{/if}
|
|
77
|
+
{#if config['status-website'].js}
|
|
78
|
+
{@html `<script>${config['status-website'].js}</script>`}
|
|
79
|
+
{/if}
|
|
80
|
+
</svelte:head>
|
|
81
|
+
|
|
82
|
+
{#if (config["status-website"] || {}).customBodyHtml}
|
|
83
|
+
{@html (config["status-website"] || {}).customBodyHtml}
|
|
84
|
+
{/if}
|
|
85
|
+
|
|
86
|
+
<Nav {segment} />
|
|
87
|
+
|
|
88
|
+
<main class="container">
|
|
89
|
+
<slot />
|
|
90
|
+
</main>
|
|
91
|
+
|
|
92
|
+
<footer>
|
|
93
|
+
<p>
|
|
94
|
+
{@html snarkdown(
|
|
95
|
+
config.i18n.footer.replace(/\$REPO/, `https://github.com/${config.owner}/${config.repo}`)
|
|
96
|
+
)}
|
|
97
|
+
</p>
|
|
98
|
+
</footer>
|
|
99
|
+
|
|
100
|
+
<style>
|
|
101
|
+
footer {
|
|
102
|
+
text-align: center;
|
|
103
|
+
opacity: 0.75;
|
|
104
|
+
margin-top: 3rem;
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
107
|
+
|
|
108
|
+
{#if (config["status-website"] || {}).customFootHtml}
|
|
109
|
+
{@html (config["status-website"] || {}).customFootHtml}
|
|
110
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import config from "../data/config.json";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<svelte:head>
|
|
6
|
+
<title>{config.i18n.errorTitle}</title>
|
|
7
|
+
</svelte:head>
|
|
8
|
+
|
|
9
|
+
<h1>{config.i18n.errorTitle}</h1>
|
|
10
|
+
|
|
11
|
+
<p class="lead">{config.i18n.errorIntro}</p>
|
|
12
|
+
|
|
13
|
+
<p>{config.i18n.errorText}</p>
|
|
14
|
+
|
|
15
|
+
<a href={config.path} class="error-button">{config.i18n.errorHome}</a>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
p.lead {
|
|
19
|
+
font-size: 110%;
|
|
20
|
+
}
|
|
21
|
+
a.error-button {
|
|
22
|
+
font: inherit;
|
|
23
|
+
padding: 0.5rem 1rem;
|
|
24
|
+
border-radius: 0.2rem;
|
|
25
|
+
text-decoration: none;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script context="module">
|
|
2
|
+
export async function preload(page) {
|
|
3
|
+
const { number } = page.params;
|
|
4
|
+
return { slug: number };
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import Summary from "../../components/Summary.svelte";
|
|
10
|
+
import History from "../../components/History.svelte";
|
|
11
|
+
import Graph from "../../components/Graph.svelte";
|
|
12
|
+
export let slug;
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<Summary {slug} />
|
|
16
|
+
<Graph {slug} />
|
|
17
|
+
<History {slug} />
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script context="module">
|
|
2
|
+
export async function preload(page) {
|
|
3
|
+
const { number } = page.params;
|
|
4
|
+
return { number };
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import Incident from "../../components/Incident.svelte";
|
|
10
|
+
export let number;
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Incident {number} />
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import snarkdown from "snarkdown";
|
|
3
|
+
import ActiveIncidents from "../components/ActiveIncidents.svelte";
|
|
4
|
+
import ActiveScheduled from "../components/ActiveScheduled.svelte";
|
|
5
|
+
import Incidents from "../components/Incidents.svelte";
|
|
6
|
+
import LiveStatus from "../components/LiveStatus.svelte";
|
|
7
|
+
import Scheduled from "../components/Scheduled.svelte";
|
|
8
|
+
import config from "../data/config.json";
|
|
9
|
+
|
|
10
|
+
let title = "Status";
|
|
11
|
+
try {
|
|
12
|
+
title = config["status-website"].name;
|
|
13
|
+
} catch (error) {}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<svelte:head>
|
|
17
|
+
<title>{title}</title>
|
|
18
|
+
</svelte:head>
|
|
19
|
+
|
|
20
|
+
<header>
|
|
21
|
+
{#if config["status-website"]}
|
|
22
|
+
{#if config["status-website"].introTitle}
|
|
23
|
+
<h1>
|
|
24
|
+
{@html snarkdown(config["status-website"].introTitle)}
|
|
25
|
+
</h1>
|
|
26
|
+
{/if}
|
|
27
|
+
{#if config["status-website"].introMessage}
|
|
28
|
+
<p class="lead">
|
|
29
|
+
{@html snarkdown(config["status-website"].introMessage)}
|
|
30
|
+
</p>
|
|
31
|
+
{/if}
|
|
32
|
+
{/if}
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
<ActiveIncidents />
|
|
36
|
+
<ActiveScheduled />
|
|
37
|
+
<LiveStatus />
|
|
38
|
+
<Scheduled />
|
|
39
|
+
<Incidents />
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
p.lead {
|
|
43
|
+
font-size: 110%;
|
|
44
|
+
}
|
|
45
|
+
header {
|
|
46
|
+
margin-bottom: 2rem;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { goto } from "@sapper/app";
|
|
4
|
+
import config from "../data/config.json";
|
|
5
|
+
|
|
6
|
+
let token = "";
|
|
7
|
+
let localStorageToken = false;
|
|
8
|
+
const save = () => {
|
|
9
|
+
if (typeof window !== "undefined" && "localStorage" in window)
|
|
10
|
+
window.localStorage.setItem("personal-access-token", token);
|
|
11
|
+
goto(config.path);
|
|
12
|
+
};
|
|
13
|
+
const remove = () => {
|
|
14
|
+
token = "";
|
|
15
|
+
localStorageToken = false;
|
|
16
|
+
if (typeof window !== "undefined" && "localStorage" in window)
|
|
17
|
+
window.localStorage.removeItem("personal-access-token");
|
|
18
|
+
};
|
|
19
|
+
onMount(() => {
|
|
20
|
+
if (
|
|
21
|
+
typeof window !== "undefined" &&
|
|
22
|
+
"localStorage" in window &&
|
|
23
|
+
localStorage.getItem("personal-access-token")
|
|
24
|
+
)
|
|
25
|
+
localStorageToken = true;
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<svelte:head>
|
|
30
|
+
<title>{config.i18n.rateLimitExceededTitle}</title>
|
|
31
|
+
</svelte:head>
|
|
32
|
+
|
|
33
|
+
<h1>{config.i18n.rateLimitExceededTitle}</h1>
|
|
34
|
+
|
|
35
|
+
<p class="lead">{config.i18n.rateLimitExceededIntro}</p>
|
|
36
|
+
|
|
37
|
+
<h2>{config.i18n.rateLimitExceededWhatDoesErrorMean}</h2>
|
|
38
|
+
|
|
39
|
+
<p>{config.i18n.rateLimitExceededErrorMeaning}</p>
|
|
40
|
+
|
|
41
|
+
<h2>{config.i18n.rateLimitExceededErrorHowCanFix}</h2>
|
|
42
|
+
|
|
43
|
+
<p>
|
|
44
|
+
{config.i18n.rateLimitExceededErrorFix}
|
|
45
|
+
<a
|
|
46
|
+
href="https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token"
|
|
47
|
+
target="_blank">{config.i18n.rateLimitExceededGeneratePAT}</a
|
|
48
|
+
>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
{#if localStorageToken}
|
|
52
|
+
<p>{config.i18n.rateLimitExceededHasSet}</p>
|
|
53
|
+
<button on:click={remove}>{config.i18n.rateLimitExceededRemoveToken}</button>
|
|
54
|
+
{:else}
|
|
55
|
+
<form on:submit|preventDefault={save}>
|
|
56
|
+
<label>
|
|
57
|
+
<span>{config.i18n.rateLimitExceededGitHubPAT}</span>
|
|
58
|
+
<input
|
|
59
|
+
type="text"
|
|
60
|
+
bind:value={token}
|
|
61
|
+
placeholder={config.i18n.rateLimitExceededCopyPastePAT}
|
|
62
|
+
/>
|
|
63
|
+
</label>
|
|
64
|
+
<button class="submit-button" type="submit">{config.i18n.rateLimitExceededSaveToken}</button>
|
|
65
|
+
</form>
|
|
66
|
+
{/if}
|
|
67
|
+
|
|
68
|
+
<style>
|
|
69
|
+
p.lead {
|
|
70
|
+
font-size: 110%;
|
|
71
|
+
}
|
|
72
|
+
label span {
|
|
73
|
+
display: block;
|
|
74
|
+
font-weight: bold;
|
|
75
|
+
margin-bottom: 0.5rem;
|
|
76
|
+
}
|
|
77
|
+
input,
|
|
78
|
+
button {
|
|
79
|
+
font: inherit;
|
|
80
|
+
padding: 0.5rem 1rem;
|
|
81
|
+
border: 0.1rem solid rgba(0, 0, 0, 0.25);
|
|
82
|
+
border-radius: 0.2rem;
|
|
83
|
+
}
|
|
84
|
+
input {
|
|
85
|
+
width: 15rem;
|
|
86
|
+
max-width: 100%;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
package/src/server.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as sapper from "@sapper/server";
|
|
2
|
+
import compression from "compression";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import polka from "polka";
|
|
5
|
+
import sirv from "sirv";
|
|
6
|
+
import { load } from "js-yaml";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
|
|
9
|
+
const { PORT, NODE_ENV } = process.env;
|
|
10
|
+
const dev = NODE_ENV === "development";
|
|
11
|
+
|
|
12
|
+
let config
|
|
13
|
+
if(fs.existsSync(join("..", ".uclirc.yml"))){
|
|
14
|
+
config = load(fs.readFileSync(join("..", ".uclirc.yml"), "utf8"));
|
|
15
|
+
}else{
|
|
16
|
+
config = load(fs.readFileSync(join("..", ".upptimerc.yml"), "utf8"));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseUrl = (config["status-website"] || {}).baseUrl || "/";
|
|
20
|
+
|
|
21
|
+
polka()
|
|
22
|
+
.use(baseUrl, compression({ threshold: 0 }), sirv("static", { dev }), sapper.middleware())
|
|
23
|
+
.listen(PORT, (err) => {
|
|
24
|
+
if (err) console.log("error", err);
|
|
25
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
self.addEventListener("install", function () {
|
|
2
|
+
self.skipWaiting();
|
|
3
|
+
});
|
|
4
|
+
|
|
5
|
+
self.addEventListener("activate", function () {
|
|
6
|
+
self.registration
|
|
7
|
+
.unregister()
|
|
8
|
+
.then(function () {
|
|
9
|
+
return self.clients.matchAll();
|
|
10
|
+
})
|
|
11
|
+
.then(function (clients) {
|
|
12
|
+
clients.forEach((client) => client.navigate(client.url));
|
|
13
|
+
})
|
|
14
|
+
.catch(function () {});
|
|
15
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
|
6
|
+
<meta name="theme-color" content="#333333" />
|
|
7
|
+
%sapper.base%
|
|
8
|
+
<link rel="manifest" href="manifest.json" crossorigin="use-credentials" />
|
|
9
|
+
%sapper.scripts% %sapper.styles% %sapper.head%
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="sapper">%sapper.html%</div>
|
|
13
|
+
<script>
|
|
14
|
+
try {
|
|
15
|
+
if (typeof window !== "undefined" && window.navigator && navigator.serviceWorker) {
|
|
16
|
+
navigator.serviceWorker.getRegistrations().then(function (registrations) {
|
|
17
|
+
for (let registration of registrations) {
|
|
18
|
+
registration.unregister();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (typeof window !== "undefined" && "caches" in window) {
|
|
23
|
+
caches.keys().then(function (keyList) {
|
|
24
|
+
return Promise.all(
|
|
25
|
+
keyList.map(function (key) {
|
|
26
|
+
return caches.delete(key);
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {}
|
|
32
|
+
</script>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|