rapid patch: add event info banner (related #69)

This commit is contained in:
Grant 2024-07-05 23:06:26 -06:00
parent 4e8ac29cb0
commit 924815e629
10 changed files with 154 additions and 23 deletions

View File

@ -51,6 +51,7 @@ RUN sed -i -e 's/"main": ".*"/"main": ".\/dist\/index.js"/' packages/lib/package
# --- build main client ---
ARG VITE_INCLUDE_EVENT_INFO
RUN npm -w packages/client run build
# --- build admin ---

View File

@ -0,0 +1,84 @@
import React, { useEffect, useRef } from "react";
import { useAppContext } from "../contexts/AppContext";
import { Button } from "@nextui-org/react";
const EVENT_START = 1720756800000; // midnight 7/12/2024 eastern
/**
* *oh god the terrible code*
*
* not sure of another clean way to do this
*
* This is used to show details about the event, immediately on page load
*
* used by the canvas preview page to get people hyped up for the event (<7 days before)
*/
export const EventInfoOverlay = ({ children }: React.PropsWithChildren) => {
const { setInfoSidebar, setSettingsSidebar } = useAppContext();
const $countdown = useRef<HTMLElement | null>(null);
const getCountdown = () => {
// date math always confuses me...
// https://stackoverflow.com/a/7709819
const now = Date.now();
const ms = EVENT_START - now;
const days = Math.floor(ms / (1000 * 60 * 60 * 24));
const hours = Math.floor((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor(
((ms % (1000 * 60 * 60 * 24)) % (1000 * 60 * 60)) / (1000 * 60)
);
const seconds = Math.round(
(((ms % (1000 * 60 * 60 * 24)) % (1000 * 60 * 60)) % (1000 * 60)) / 1000
);
return [days, hours, minutes, seconds];
};
const updateTime = () => {
if (!$countdown.current) return;
const [days, hours, minutes, seconds] = getCountdown();
$countdown.current.innerText = [
days && days + "d",
hours && hours + "h",
minutes && minutes + "m",
seconds && seconds + "s",
]
.filter((a) => a)
.join(" ");
$countdown.current.title = `${days} days ${hours} hours ${minutes} minutes ${seconds} seconds`;
};
useEffect(() => {
var interval = setInterval(updateTime, 1000);
updateTime();
return () => {
clearInterval(interval);
};
}, []);
return (
<div
className="bg-black text-white p-4 fixed top-0 left-0 w-full z-[9999] flex flex-row"
style={{
pointerEvents: "initial",
}}
>
<div>
<h1 className="text-4xl font-bold">Canvas 2024</h1>
<h2 ref={(r) => ($countdown.current = r)} className="text-3xl"></h2>
</div>
<div className="flex-grow" />
<div>
<Button onPress={() => setInfoSidebar(true)}>Info</Button>
<Button onPress={() => setSettingsSidebar(true)}>Settings</Button>
</div>
</div>
);
};

View File

@ -4,6 +4,7 @@ import { User } from "./User";
import { Debug } from "@sc07-canvas/lib/src/debug";
import React, { lazy } from "react";
import { AccountStanding } from "./AccountStanding";
import { EventInfoOverlay } from "../EventInfoOverlay";
const OpenChatButton = lazy(() => import("../Chat/OpenChatButton"));
@ -18,6 +19,7 @@ export const Header = () => {
return (
<header id="main-header">
{import.meta.env.VITE_INCLUDE_EVENT_INFO && <EventInfoOverlay />}
<HeaderLeft />
<div className="spacer"></div>
{!connected && (

View File

@ -45,12 +45,23 @@ export const InfoSidebar = () => {
Discord
</Button>
</div>
<Button as={Link} href="https://toast.ooo/c/canvas">
<Button as={Link} href="https://toast.ooo/c/canvas" target="_blank">
<div className="flex flex-col text-center">
<span>Lemmy</span>
<span className="text-xs">!canvas@toast.ooo</span>
</div>
</Button>
<Button
as={Link}
href="https://social.fediverse.events/@canvas"
target="_blank"
>
<div className="flex flex-col text-center">
<span>Mastodon</span>
<span className="text-xs">@canvas@fediverse.events</span>
</div>
</Button>
<b>Build {__COMMIT_HASH__}</b>
</section>
</div>
);

View File

@ -67,10 +67,16 @@ export const Palette = () => {
{!user && (
<div className="pallete-user-overlay">
You are not logged in
<a href="/api/login" className="user-login">
Login
</a>
{import.meta.env.VITE_INCLUDE_EVENT_INFO ? (
<>The event hasn't started yet</>
) : (
<>
You are not logged in
<a href="/api/login" className="user-login">
Login
</a>
</>
)}
</div>
)}
</div>

View File

@ -4,12 +4,15 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>canvas</title>
<title>Canvas</title>
<link rel="stylesheet" href="./style.scss" />
</head>
<body>
<!-- Mastodon verification -->
<a href="https://social.fediverse.events/@canvas" rel="me"></a>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>

View File

@ -1,8 +1,7 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_HOST: string;
readonly VITE_MATRIX_HOST: string;
readonly VITE_INCLUDE_EVENT_INFO: boolean;
}
interface ImportMeta {

View File

@ -31,6 +31,14 @@ const app = Router();
* Redirect to actual authorization page
*/
app.get("/login", (req, res) => {
if (process.env.INHIBIT_LOGINS) {
res.status(400).json({
success: false,
error: "Login is not permitted.",
});
return;
}
res.redirect(
OpenID.client.authorizationUrl({
prompt: "consent",
@ -57,6 +65,14 @@ app.post("/logout", (req, res) => {
* This executes multiple database queries and should be ratelimited
*/
app.get("/callback", RateLimiter.HIGH, async (req, res) => {
if (process.env.INHIBIT_LOGINS) {
res.status(400).json({
success: false,
error: "Login is not permitted.",
});
return;
}
let exchange: TokenSet;
try {

View File

@ -58,24 +58,26 @@ if (!process.env.REDIS_RATELIMIT_PREFIX) {
);
}
if (!process.env.AUTH_ENDPOINT) {
Logger.error("AUTH_ENDPOINT is not defined");
process.exit(1);
}
if (!process.env.INHIBIT_LOGIN) {
if (!process.env.AUTH_ENDPOINT) {
Logger.error("AUTH_ENDPOINT is not defined");
process.exit(1);
}
if (!process.env.AUTH_CLIENT) {
Logger.error("AUTH_CLIENT is not defined");
process.exit(1);
}
if (!process.env.AUTH_CLIENT) {
Logger.error("AUTH_CLIENT is not defined");
process.exit(1);
}
if (!process.env.AUTH_SECRET) {
Logger.error("AUTH_SECRET is not defined");
process.exit(1);
}
if (!process.env.AUTH_SECRET) {
Logger.error("AUTH_SECRET is not defined");
process.exit(1);
}
if (!process.env.OIDC_CALLBACK_HOST) {
Logger.error("OIDC_CALLBACK_HOST is not defined");
process.exit(1);
if (!process.env.OIDC_CALLBACK_HOST) {
Logger.error("OIDC_CALLBACK_HOST is not defined");
process.exit(1);
}
}
// run startup tasks, all of these need to be completed to serve

View File

@ -5,6 +5,13 @@ class OpenID_ {
client: BaseClient = {} as any;
async setup() {
if (process.env.INHIBIT_LOGIN) {
console.warn(
"OpenID is not setup; INHIBIT_LOGIN environment variable set! Proceed with caution!"
);
return;
}
const { AUTH_ENDPOINT, AUTH_CLIENT, AUTH_SECRET } = process.env;
this.issuer = await Issuer.discover(AUTH_ENDPOINT);