Skip to content

Commit 022d267

Browse files
authored
Health routes for status page (#188)
* some health changes * example frontend page for health check * fixed lint issue * frontend health route * backend health check * backend + k8s update * fixed frontend/backend check issues * restored yarn lock * pls work * more linting 😭 * bro * auth type fix * pls fix lint * attempt #2 * attempt #2 * attempt #3 * healthbackend:health renamed to healthcheck:backend * brought back extra line at the end in yarn.lock
1 parent d11857a commit 022d267

File tree

15 files changed

+125
-13
lines changed

15 files changed

+125
-13
lines changed

backend/Platform/middleware.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.http import HttpResponse
2+
3+
4+
class HealthCheckMiddleware:
5+
def __init__(self, get_response):
6+
self.get_response = get_response
7+
8+
def __call__(self, request):
9+
if request.path == "/health":
10+
return HttpResponse("ok")
11+
return self.get_response(request)

backend/Platform/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
path("accounts/", include("accounts.urls", namespace="oauth2_provider")),
1414
path("options/", include("options.urls", namespace="options")),
1515
path("identity/", include("identity.urls", namespace="identity")),
16+
path("healthcheck/", include("health.urls", namespace="healthcheck")),
1617
path("s/", include("shortener.urls", namespace="shortener")),
1718
path(
1819
"openapi/",

backend/health/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class HealthConfig(AppConfig):
5+
name = "health"

backend/health/apps.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class HealthConfig(AppConfig):
5+
name = "health"

backend/health/urls.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.urls import path
2+
from health.views import HealthView
3+
4+
5+
app_name = "health"
6+
7+
urlpatterns = [
8+
path("backend/", HealthView.as_view(), name="backend"),
9+
]

backend/health/views.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from http import HTTPStatus
2+
3+
from django.http import JsonResponse
4+
from django.views.generic import View
5+
6+
7+
class HealthView(View):
8+
def get(self, request):
9+
"""
10+
Health check endpoint to confirm the backend is running.
11+
---
12+
summary: Health Check
13+
responses:
14+
"200":
15+
content:
16+
application/json:
17+
schema:
18+
type: object
19+
properties:
20+
message:
21+
type: string
22+
enum: ["OK"]
23+
---
24+
"""
25+
return JsonResponse({"message": "OK"}, status=HTTPStatus.OK)

backend/tests/identity/test_views.py

+11
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,14 @@ def test_garbage_bearer(self):
179179
self.assertIsInstance(content, dict)
180180
self.assertEqual(HTTPStatus.BAD_REQUEST, response.status_code)
181181
self.assertNotIn("access", content)
182+
183+
184+
class HealthTestCase(TestCase):
185+
def setUp(self):
186+
self.client = Client()
187+
188+
def test_health(self):
189+
url = reverse("healthcheck:backend")
190+
resp = self.client.get(url)
191+
self.assertEqual(resp.status_code, 200)
192+
self.assertEqual(resp.json(), {"message": "OK"})

frontend/components/accounts/modals/verification.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Modal } from "react-bulma-components";
33

44
import { mutateResourceListFunction } from "@pennlabs/rest-hooks/dist/types";
55
import toast from "react-hot-toast";
6-
import styles from "../../../styles/Verification.module.css";
76
import { verifyContact } from "../../../data-fetching/accounts";
87
import { ContactType, ContactInfo } from "../../../types";
98
import { logException } from "../../../utils/sentry";
@@ -38,9 +37,9 @@ const VerificationForm = (props: VerificationFormProps) => {
3837
onChange={handleInputChange}
3938
validChars="0-9"
4039
classNames={{
41-
container: styles.container,
42-
character: styles.character,
43-
characterSelected: styles["character--selected"],
40+
container: "verification-modal-container",
41+
character: "verification-modal-character",
42+
characterSelected: "verification-modal-character-selected",
4443
}}
4544
removeDefaultStyles
4645
/>
@@ -59,7 +58,7 @@ const VerificationModal = (props: VerificationModalProps) => {
5958
const { show, closeFunc, type, contact, id, mutate } = props;
6059
const prettyType = type === ContactType.Email ? "Email" : "Phone Number";
6160
return (
62-
<Modal show={show} onClose={closeFunc}>
61+
<Modal show={show} onClose={closeFunc} className="verification-modal">
6362
<Modal.Card>
6463
<Modal.Card.Header onClose={closeFunc}>
6564
<Modal.Card.Title>

frontend/next-env.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
/// <reference types="next" />
2-
/// <reference types="next/types/global" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

frontend/pages/_app.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "bulma/css/bulma.min.css";
22
import "../styles/globals.css";
3+
import "../styles/Verification.module.css";
34

45
const MyApp = ({ Component, pageProps }) => {
56
return <Component {...pageProps} />;

frontend/pages/health.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { GetServerSideProps } from "next";
2+
3+
const HealthPage = () => {
4+
return <div>OK</div>;
5+
};
6+
7+
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
8+
const userAgent = req.headers["user-agent"] || "";
9+
10+
if (userAgent !== "service-status") {
11+
return {
12+
redirect: {
13+
destination: "/",
14+
permanent: false,
15+
},
16+
};
17+
}
18+
19+
return {
20+
props: {},
21+
};
22+
};
23+
24+
export default HealthPage;
+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
.container {
1+
.verification-modal-container {
22
height: 50px;
33
width: 300px;
44
margin-top: 10px;
55
}
66

7-
.character {
7+
.verification-modal-character {
88
line-height: 50px;
99
font-size: 36px;
1010
color: black;
@@ -14,10 +14,10 @@
1414
margin-left: 8px;
1515
}
1616

17-
.character:first-child {
17+
.verification-modal-character:first-child {
1818
margin-left: 0;
1919
}
2020

21-
.character--selected {
21+
.verification-modal-character-selected {
2222
border: 1px solid #b0b0b0;
23-
}
23+
}

frontend/tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"include": [
2626
"next-env.d.ts",
2727
"**/*.ts",
28-
"**/*.tsx"
28+
"**/*.tsx",
29+
"**/*.css"
2930
]
3031
}

frontend/utils/auth.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { doApiRequest } from "./fetch";
77
import { User } from "../types";
88

99
export interface AuthProps {
10-
user: User;
10+
user: User | null;
1111
}
1212

1313
type GetServerSidePropsResultDiscUnion<T> =
@@ -39,6 +39,21 @@ export function withAuth<T>(getServerSidePropsFunc: GetServerSidePropsFunc<T>) {
3939
return async (
4040
ctx: GetServerSidePropsContext
4141
): Promise<GetServerSidePropsResult<T & AuthProps>> => {
42+
if (ctx.resolvedUrl === "/health") {
43+
const wrapped = await getServerSidePropsFunc(ctx);
44+
const casted = convertGetServerSidePropsResult(wrapped);
45+
46+
if (casted.tag === "props") {
47+
return {
48+
props: { ...casted.props, user: null } as T & AuthProps,
49+
};
50+
} else if (casted.tag === "notFound") {
51+
return { notFound: casted.notFound };
52+
} else {
53+
return { redirect: casted.redirect };
54+
}
55+
}
56+
4257
const headers = {
4358
credentials: "include",
4459
headers: { cookie: ctx.req.headers.cookie },

k8s/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ export class MyChart extends PennLabsChart {
4848
"/openapi",
4949
"/documentation",
5050
"/Shibboleth.sso",
51+
"/healthcheck",
5152
],
5253
isSubdomain: true,
5354
}],
55+
5456
ingressProps: {
5557
annotations: {
5658
["ingress.kubernetes.io/protocol"]: "https",

0 commit comments

Comments
 (0)