Remix migration
This commit is contained in:
parent
5d2774fb2e
commit
04dcbb4181
5
.gitignore
vendored
5
.gitignore
vendored
@ -24,3 +24,8 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
# Remix files
|
||||||
|
.cache
|
||||||
|
functions/\[\[path\]\].js
|
||||||
|
public/build
|
||||||
|
@ -1 +1 @@
|
|||||||
v16.19.0
|
v18.14.2
|
19
app/context.tsx
Normal file
19
app/context.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export interface ServerStyleContextData {
|
||||||
|
key: string;
|
||||||
|
ids: Array<string>;
|
||||||
|
css: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerStyleContext = createContext<
|
||||||
|
ServerStyleContextData[] | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
export interface ClientStyleContextData {
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ClientStyleContext = createContext<ClientStyleContextData | null>(
|
||||||
|
null
|
||||||
|
);
|
7
app/createEmotionCache.ts
Normal file
7
app/createEmotionCache.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import createCache from "@emotion/cache";
|
||||||
|
|
||||||
|
export const defaultCache = createEmotionCache();
|
||||||
|
|
||||||
|
export default function createEmotionCache() {
|
||||||
|
return createCache.default({ key: "cha" });
|
||||||
|
}
|
39
app/entry.client.tsx
Normal file
39
app/entry.client.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { CacheProvider } from "@emotion/react";
|
||||||
|
import { ClientStyleContext } from "./context.js";
|
||||||
|
import createEmotionCache, { defaultCache } from "./createEmotionCache.js";
|
||||||
|
import { hydrateRoot } from "react-dom/client";
|
||||||
|
import { Integrations } from "@sentry/tracing";
|
||||||
|
import { RemixBrowser } from "@remix-run/react";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
import { type ReactNode, StrictMode, useState } from "react";
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
dsn:
|
||||||
|
document.querySelector("meta[name='dsn']")?.getAttribute("content") ??
|
||||||
|
undefined,
|
||||||
|
integrations: [new Integrations.BrowserTracing()],
|
||||||
|
tracesSampleRate: 0.1,
|
||||||
|
});
|
||||||
|
|
||||||
|
function ClientCacheProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [cache, setCache] = useState(defaultCache);
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
setCache(createEmotionCache());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClientStyleContext.Provider value={{ reset }}>
|
||||||
|
<CacheProvider value={cache}>{children}</CacheProvider>
|
||||||
|
</ClientStyleContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hydrateRoot(
|
||||||
|
document,
|
||||||
|
<StrictMode>
|
||||||
|
<ClientCacheProvider>
|
||||||
|
<RemixBrowser />
|
||||||
|
</ClientCacheProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
41
app/entry.server.tsx
Normal file
41
app/entry.server.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { CacheProvider } from "@emotion/react";
|
||||||
|
import createEmotionCache from "./createEmotionCache.js";
|
||||||
|
import { createEmotionServer } from "../emotion-server.js";
|
||||||
|
import { type EntryContext } from "@remix-run/cloudflare";
|
||||||
|
import { RemixServer } from "@remix-run/react";
|
||||||
|
import { renderToString } from "react-dom/server";
|
||||||
|
import { ServerStyleContext } from "./context.js";
|
||||||
|
|
||||||
|
export default function handleRequest(
|
||||||
|
request: Request,
|
||||||
|
responseStatusCode: number,
|
||||||
|
responseHeaders: Headers,
|
||||||
|
remixContext: EntryContext
|
||||||
|
) {
|
||||||
|
const cache = createEmotionCache();
|
||||||
|
const { extractCriticalToChunks } = createEmotionServer(cache);
|
||||||
|
const html = renderToString(
|
||||||
|
<ServerStyleContext.Provider value={null}>
|
||||||
|
<CacheProvider value={cache}>
|
||||||
|
<RemixServer context={remixContext} url={request.url} />
|
||||||
|
</CacheProvider>
|
||||||
|
</ServerStyleContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const chunks = extractCriticalToChunks(html);
|
||||||
|
|
||||||
|
const markup = renderToString(
|
||||||
|
<ServerStyleContext.Provider value={chunks.styles}>
|
||||||
|
<CacheProvider value={cache}>
|
||||||
|
<RemixServer context={remixContext} url={request.url} />
|
||||||
|
</CacheProvider>
|
||||||
|
</ServerStyleContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
responseHeaders.set("content-type", "text/html;charset=utf-8");
|
||||||
|
|
||||||
|
return new Response("<!DOCTYPE html>" + markup, {
|
||||||
|
headers: responseHeaders,
|
||||||
|
status: responseStatusCode,
|
||||||
|
});
|
||||||
|
}
|
177
app/root.tsx
Normal file
177
app/root.tsx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import {
|
||||||
|
ChakraProvider,
|
||||||
|
Container,
|
||||||
|
cookieStorageManagerSSR,
|
||||||
|
Heading,
|
||||||
|
Link,
|
||||||
|
Text,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { ClientStyleContext, ServerStyleContext } from "./context.js";
|
||||||
|
import fontStyle from "@fontsource/plus-jakarta-sans/index.css";
|
||||||
|
import Forbidden from "../components/Forbidden.js";
|
||||||
|
import globalStyles from "../index.css";
|
||||||
|
import { HelmetProvider } from "react-helmet-async";
|
||||||
|
import {
|
||||||
|
Links,
|
||||||
|
LiveReload,
|
||||||
|
Outlet,
|
||||||
|
Scripts,
|
||||||
|
useCatch,
|
||||||
|
useLoaderData,
|
||||||
|
} from "@remix-run/react";
|
||||||
|
import { LinksFunction } from "@remix-run/cloudflare";
|
||||||
|
import Login from "../components/Login.js";
|
||||||
|
import Navigation from "../components/Navigation.js";
|
||||||
|
|
||||||
|
import { type ReactNode, StrictMode, useContext, useEffect } from "react";
|
||||||
|
import theme from "../theme.js";
|
||||||
|
import { withEmotionCache } from "@emotion/react";
|
||||||
|
|
||||||
|
export function CatchBoundary() {
|
||||||
|
const { status } = useCatch();
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 303:
|
||||||
|
return "";
|
||||||
|
|
||||||
|
case 401:
|
||||||
|
return getMarkup({ hide: true }, <Login />);
|
||||||
|
|
||||||
|
case 403:
|
||||||
|
return getMarkup({ hide: true }, <Forbidden />);
|
||||||
|
|
||||||
|
case 404:
|
||||||
|
return getMarkup(
|
||||||
|
{ hide: true },
|
||||||
|
<Container maxW="container.lg" pt="8vh" textAlign="left">
|
||||||
|
<Heading size="4xl">404</Heading>
|
||||||
|
<br />
|
||||||
|
<Text fontSize="xl">There is nothing to find here.</Text>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<Link color="#646cff" onClick={() => history.go(-1)}>
|
||||||
|
Go back
|
||||||
|
</Link>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return getMarkup(
|
||||||
|
{ hide: true },
|
||||||
|
<Container maxW="container.lg" pt="8vh" textAlign="left">
|
||||||
|
<Heading size="4xl">500</Heading>
|
||||||
|
<br />
|
||||||
|
<Text fontSize="xl">S̶̡͈̠̗̠͖͙̭o̶̶͕͚̥͍̪̤m̸̨͏͈͔̖͚̖̰̱͞e҉̵͖͚͇̀t̕͟͠͏͎̺̯̲̱̣̤̠̟͙̠̙̫̬ḩ̸̭͓̬͎̙̀į̞̮͉͖̰̥̹͚̫̙̪̗̜̳̕ͅn҉͔̯̪̗̝̝͖̲͇͍͎̲̲̤̖̫͈̪͡g̴̰̻̙̝͉̭͇̖̰̝̙͕̼͙͘͜ ̵̶̫̥̳̲̘̻̗͈͕̭̲͇̘̜̺̟̥̖̥b̴̙̭̹͕̞͠r̞͎̠̩͈̖̰̞̯̯͢͢͠ͅo̝̯̗̹̳͍̰͉͕̘̰̠̺̥̰͔̕ͅk̵̸̻̠͕̺̦̦͖̲̺̦̞̝̞͞͡e̶͏̤̼̼͔̘̰̰̭͈̀͞͡</Text>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<Link color="#646cff" onClick={() => location.reload()}>
|
||||||
|
Reload
|
||||||
|
</Link>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const links: LinksFunction = () => {
|
||||||
|
return [
|
||||||
|
{ href: "/favicon.ico", rel: "icon" },
|
||||||
|
{ href: "/files/logo192.png", rel: "apple-touch-icon", type: "image/png" },
|
||||||
|
{ href: fontStyle, rel: "stylesheet " },
|
||||||
|
{ href: globalStyles, rel: "stylesheet" },
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function loader({
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
context: RequestContext;
|
||||||
|
}): Promise<{ [k: string]: any }> {
|
||||||
|
let data: { [k: string]: string } = {};
|
||||||
|
|
||||||
|
if (context.data.current_user) data = { ...context.data.current_user };
|
||||||
|
if (context.env.DSN) data.dsn = context.env.DSN;
|
||||||
|
if (context.data.theme) data.theme = context.data.theme;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMarkup(
|
||||||
|
loaderData: { [k: string]: any },
|
||||||
|
child: ReactNode
|
||||||
|
): JSX.Element {
|
||||||
|
const Document = withEmotionCache(
|
||||||
|
({ children }: { children: ReactNode }, emotionCache) => {
|
||||||
|
const serverStyleData = useContext(ServerStyleContext);
|
||||||
|
const clientStyleData = useContext(ClientStyleContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
emotionCache.sheet.container = document.head;
|
||||||
|
const tags = emotionCache.sheet.tags;
|
||||||
|
|
||||||
|
emotionCache.sheet.flush();
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
(emotionCache.sheet as any)._insertTag(tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
clientStyleData?.reset();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const helmetContext: { [k: string]: any } = {};
|
||||||
|
|
||||||
|
const body = (
|
||||||
|
<StrictMode>
|
||||||
|
<ChakraProvider
|
||||||
|
colorModeManager={cookieStorageManagerSSR(
|
||||||
|
typeof document === "undefined" ? "" : document.cookie
|
||||||
|
)}
|
||||||
|
theme={theme}
|
||||||
|
>
|
||||||
|
<HelmetProvider>
|
||||||
|
<div className="App">
|
||||||
|
<Navigation {...loaderData} />
|
||||||
|
{children}
|
||||||
|
<Scripts />
|
||||||
|
<LiveReload />
|
||||||
|
</div>
|
||||||
|
</HelmetProvider>
|
||||||
|
</ChakraProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
const { helmet } = helmetContext;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<Links />
|
||||||
|
{serverStyleData?.map(({ key, ids, css }) => (
|
||||||
|
<style
|
||||||
|
key={key}
|
||||||
|
data-emotion={`${key} ${ids.join(" ")}`}
|
||||||
|
dangerouslySetInnerHTML={{ __html: css }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<meta charSet="UTF-8" />
|
||||||
|
{loaderData.dsn ? (
|
||||||
|
<meta name="dsn" content={loaderData.dsn} />
|
||||||
|
) : null}
|
||||||
|
<meta name="theme-color" content="#00a8f8" />
|
||||||
|
{helmet.meta?.toString()}
|
||||||
|
{helmet.title?.toString() ?? <title>Car Crushers</title>}
|
||||||
|
</head>
|
||||||
|
<body>{body}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return <Document>{child}</Document>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const loaderData = useLoaderData<typeof loader>();
|
||||||
|
|
||||||
|
return getMarkup(loaderData, <Outlet />);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Container, Text } from "@chakra-ui/react";
|
import { Box, Container, Text } from "@chakra-ui/react";
|
||||||
|
|
||||||
export function Page() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box alignContent="left">
|
<Box alignContent="left">
|
||||||
@ -14,5 +14,3 @@ export function Page() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const title = "Home - Car Crushers";
|
|
@ -21,13 +21,39 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
|
import { useLoaderData } from "@remix-run/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Login from "../components/Login";
|
import Success from "../../components/Success.js";
|
||||||
import Success from "../components/Success";
|
|
||||||
|
|
||||||
export function Page(pageProps: { [p: string]: any }) {
|
export async function loader({ context }: { context: RequestContext }) {
|
||||||
if (!pageProps.logged_in) return <Login />;
|
if (!context.data.current_user)
|
||||||
|
throw new Response(null, {
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { current_user: currentUser } = context.data;
|
||||||
|
const dataKV = context.env.DATA;
|
||||||
|
const disabled = await dataKV.get("appeal_disabled");
|
||||||
|
|
||||||
|
return {
|
||||||
|
can_appeal:
|
||||||
|
!Boolean(disabled) &&
|
||||||
|
!Boolean(await dataKV.get(`blockedappeal_${currentUser.id}`)) &&
|
||||||
|
!Boolean(
|
||||||
|
(
|
||||||
|
await dataKV.list({
|
||||||
|
prefix: `appeal_${currentUser.id}`,
|
||||||
|
})
|
||||||
|
).keys.find((appeal) => (appeal.metadata as { [k: string]: any }).open)
|
||||||
|
),
|
||||||
|
can_toggle:
|
||||||
|
currentUser.permissions & (1 << 0) || currentUser.permissions & (1 << 11),
|
||||||
|
disabled: Boolean(disabled),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const pageProps = useLoaderData<typeof loader>();
|
||||||
const { isOpen, onClose, onOpen } = useDisclosure();
|
const { isOpen, onClose, onOpen } = useDisclosure();
|
||||||
const [showSuccess, setShowSuccess] = useState(false);
|
const [showSuccess, setShowSuccess] = useState(false);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@ -195,7 +221,7 @@ export function Page(pageProps: { [p: string]: any }) {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<Button
|
<Button
|
||||||
disabled={pageProps.disabled || pageProps.already_submitted}
|
disabled={pageProps.can_appeal}
|
||||||
onClick={async () => await submit()}
|
onClick={async () => await submit()}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
@ -8,13 +8,68 @@ import {
|
|||||||
VStack,
|
VStack,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { lazy, useState } from "react";
|
import { lazy, useState } from "react";
|
||||||
const AppealCard = lazy(() => import("../components/AppealCard"));
|
import AppealCard from "../../components/AppealCard.js";
|
||||||
import Login from "../components/Login";
|
import Login from "../../components/Login.js";
|
||||||
|
import { useLoaderData } from "@remix-run/react";
|
||||||
|
|
||||||
export function Page(pageProps: { [p: string]: any }) {
|
export async function loader({ context }: { context: RequestContext }) {
|
||||||
if (!pageProps.logged_in)
|
const { current_user: currentUser } = context.data;
|
||||||
return <Login />;
|
|
||||||
|
|
||||||
|
if (!currentUser)
|
||||||
|
throw new Response(null, {
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newItemPermissions = {
|
||||||
|
game_ban: [1 << 5],
|
||||||
|
inactivity: [1 << 2, 1 << 9, 1 << 10],
|
||||||
|
infraction: [1 << 0, 1 << 2, 1 << 6, 1 << 7],
|
||||||
|
};
|
||||||
|
|
||||||
|
const newItemNames: { [k: string]: string } = {
|
||||||
|
game_ban: "Game Ban",
|
||||||
|
inactivity: "Inactivity Notice",
|
||||||
|
infraction: "Infraction",
|
||||||
|
};
|
||||||
|
|
||||||
|
const typePermissions = {
|
||||||
|
appeal: [1 << 0, 1 << 1],
|
||||||
|
gma: [1 << 5],
|
||||||
|
report: [1 << 5],
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeNames: { [k: string]: string } = {
|
||||||
|
appeal: "Discord Appeals",
|
||||||
|
gma: "Game Appeals",
|
||||||
|
report: "Game Reports",
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowedNewItems = [];
|
||||||
|
const allowedTypes = [];
|
||||||
|
|
||||||
|
for (const [item, ints] of Object.entries(newItemPermissions)) {
|
||||||
|
if (ints.find((i) => currentUser.permissions & i))
|
||||||
|
allowedNewItems.push({ name: newItemNames[item], value: item });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [type, ints] of Object.entries(typePermissions)) {
|
||||||
|
if (ints.find((i) => currentUser.permissions & i))
|
||||||
|
allowedTypes.push({ name: typeNames[type], value: type });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowedTypes.length)
|
||||||
|
throw new Response(null, {
|
||||||
|
status: 403,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
entry_types: allowedTypes,
|
||||||
|
item_types: allowedNewItems,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const pageProps = useLoaderData<typeof loader>();
|
||||||
const isDesktop = useBreakpointValue({ base: false, lg: true });
|
const isDesktop = useBreakpointValue({ base: false, lg: true });
|
||||||
const entryTypes = [];
|
const entryTypes = [];
|
||||||
const [entries, setEntries] = useState([] as JSX.Element[]);
|
const [entries, setEntries] = useState([] as JSX.Element[]);
|
||||||
@ -83,4 +138,3 @@ export function Page(pageProps: { [p: string]: any }) {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export const title = "Mod Queue - Car Crushers";
|
|
@ -1,6 +1,6 @@
|
|||||||
import { Container, Heading, Link, Text } from "@chakra-ui/react";
|
import { Container, Heading, Link, Text } from "@chakra-ui/react";
|
||||||
|
|
||||||
export function Page() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<Container maxW="container.lg" pb="8vh" pt="4vh" textAlign="start">
|
<Container maxW="container.lg" pb="8vh" pt="4vh" textAlign="start">
|
||||||
<Heading>Privacy Policy</Heading>
|
<Heading>Privacy Policy</Heading>
|
||||||
@ -19,7 +19,7 @@ export function Page() {
|
|||||||
authorize requests. A list of information available can be found at{" "}
|
authorize requests. A list of information available can be found at{" "}
|
||||||
<Link
|
<Link
|
||||||
color="#646cff"
|
color="#646cff"
|
||||||
href="https://discord.com/developers/docs/resources/use"
|
href="https://discord.com/developers/docs/resources/user"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Discord's Developer Portal
|
Discord's Developer Portal
|
||||||
@ -198,7 +198,9 @@ export function Page() {
|
|||||||
<Text>You have the right to:</Text>
|
<Text>You have the right to:</Text>
|
||||||
<br />
|
<br />
|
||||||
<ul>
|
<ul>
|
||||||
<li>Access your personal information</li>
|
<li>
|
||||||
|
Access your personal information (or export it in a portable format)
|
||||||
|
</li>
|
||||||
<li>Delete your personal information</li>
|
<li>Delete your personal information</li>
|
||||||
<li>
|
<li>
|
||||||
Request restrictions on the processing of your personal information
|
Request restrictions on the processing of your personal information
|
||||||
@ -234,5 +236,3 @@ export function Page() {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const title = "Privacy - Car Crushers";
|
|
@ -13,12 +13,21 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Login from "../components/Login";
|
import { useLoaderData } from "@remix-run/react";
|
||||||
import Success from "../components/Success";
|
import Success from "../../components/Success.js";
|
||||||
|
|
||||||
export function Page(pageProps: { [p: string]: any }) {
|
export async function loader({
|
||||||
if (!pageProps.logged_in) return <Login />;
|
context,
|
||||||
|
}: {
|
||||||
|
context: RequestContext;
|
||||||
|
}): Promise<{ logged_in: boolean; site_key: string }> {
|
||||||
|
return {
|
||||||
|
logged_in: Boolean(context.data.current_user),
|
||||||
|
site_key: context.env.TURNSTILE_SITEKEY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
const [fileProgress, setFileProgress] = useState(0);
|
const [fileProgress, setFileProgress] = useState(0);
|
||||||
const [showSuccess, setShowSuccess] = useState(false);
|
const [showSuccess, setShowSuccess] = useState(false);
|
||||||
const [supportsRequestStreams, setSupportsRequestStreams] = useState(false);
|
const [supportsRequestStreams, setSupportsRequestStreams] = useState(false);
|
||||||
@ -165,66 +174,81 @@ export function Page(pageProps: { [p: string]: any }) {
|
|||||||
sessionStorage.setItem("REPORT_SUCCESS", "1");
|
sessionStorage.setItem("REPORT_SUCCESS", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { logged_in, site_key } = useLoaderData<typeof loader>();
|
||||||
|
|
||||||
return showSuccess ? (
|
return showSuccess ? (
|
||||||
<Success
|
<Success
|
||||||
heading="Report Submitted"
|
heading="Report Submitted"
|
||||||
message="We will review it as soon as possible."
|
message="We will review it as soon as possible."
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Container maxW="container.md" pt="4vh" textAlign="start">
|
<>
|
||||||
<Heading mb="4vh">Report an Exploiter</Heading>
|
<Container maxW="container.md" pt="4vh" textAlign="start">
|
||||||
<br />
|
<Heading mb="4vh">Report an Exploiter</Heading>
|
||||||
<FormControl isRequired>
|
<br />
|
||||||
<FormLabel>
|
<FormControl isRequired>
|
||||||
Username(s) - To specify more than one, provide a comma-delimited list
|
<FormLabel>
|
||||||
(User1, User2, User3...)
|
Username(s) - To specify more than one, provide a comma-delimited
|
||||||
</FormLabel>
|
list (User1, User2, User3...)
|
||||||
<Input id="usernames" placeholder="builderman" />
|
</FormLabel>
|
||||||
</FormControl>
|
<Input id="usernames" placeholder="builderman" />
|
||||||
<br />
|
</FormControl>
|
||||||
<FormControl isRequired>
|
<br />
|
||||||
<FormLabel>Your Evidence (Max Size: 512MB)</FormLabel>
|
<FormControl isRequired>
|
||||||
<Button
|
<FormLabel>Your Evidence (Max Size: 512MB)</FormLabel>
|
||||||
colorScheme="blue"
|
<Button
|
||||||
mr="8px"
|
colorScheme="blue"
|
||||||
onClick={() => document.getElementById("evidence")?.click()}
|
mr="8px"
|
||||||
>
|
onClick={() => document.getElementById("evidence")?.click()}
|
||||||
Select File
|
>
|
||||||
</Button>
|
Select File
|
||||||
<input id="evidence" type="file" />
|
</Button>
|
||||||
</FormControl>
|
<input id="evidence" type="file" />
|
||||||
<br />
|
</FormControl>
|
||||||
<br />
|
<br />
|
||||||
<Text>
|
<br />
|
||||||
By submitting this form, you agree to the{" "}
|
<Text>
|
||||||
<Link color="#646cff" href="/terms">
|
By submitting this form, you agree to the{" "}
|
||||||
Terms of Service
|
<Link color="#646cff" href="/terms">
|
||||||
</Link>{" "}
|
Terms of Service
|
||||||
and{" "}
|
</Link>{" "}
|
||||||
<Link color="#646cff" href="/privacy">
|
and{" "}
|
||||||
Privacy Policy
|
<Link color="#646cff" href="/privacy">
|
||||||
</Link>
|
Privacy Policy
|
||||||
.
|
</Link>
|
||||||
</Text>
|
.
|
||||||
<br />
|
</Text>
|
||||||
<HStack>
|
<br />
|
||||||
<Button
|
<HStack>
|
||||||
disabled={uploading}
|
<Button
|
||||||
mr="8px"
|
disabled={uploading}
|
||||||
onClick={async () => await submit()}
|
mr="8px"
|
||||||
>
|
onClick={async () => await submit()}
|
||||||
Submit
|
>
|
||||||
</Button>
|
Submit
|
||||||
<CircularProgress
|
</Button>
|
||||||
display={uploading ? "" : "none"}
|
<CircularProgress
|
||||||
isIndeterminate={!supportsRequestStreams}
|
display={uploading ? "" : "none"}
|
||||||
value={supportsRequestStreams ? fileProgress : undefined}
|
isIndeterminate={!supportsRequestStreams}
|
||||||
>
|
value={supportsRequestStreams ? fileProgress : undefined}
|
||||||
{supportsRequestStreams ? (
|
>
|
||||||
<CircularProgressLabel>{fileProgress}%</CircularProgressLabel>
|
{supportsRequestStreams ? (
|
||||||
) : null}
|
<CircularProgressLabel>{fileProgress}%</CircularProgressLabel>
|
||||||
</CircularProgress>
|
) : null}
|
||||||
</HStack>
|
</CircularProgress>
|
||||||
</Container>
|
</HStack>
|
||||||
|
<div
|
||||||
|
className="cf-turnstile"
|
||||||
|
data-sitekey={useLoaderData<typeof loader>()}
|
||||||
|
></div>
|
||||||
|
</Container>
|
||||||
|
{logged_in ? null : (
|
||||||
|
<script
|
||||||
|
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ import {
|
|||||||
VStack,
|
VStack,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
|
|
||||||
export function Page() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
borderRadius="12px"
|
borderRadius="12px"
|
||||||
@ -134,5 +134,3 @@ export function Page() {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const title = "Support - Car Crushers";
|
|
@ -9,9 +9,9 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import team from "../data/team.json";
|
import team from "../../data/team.json";
|
||||||
|
|
||||||
export function Page() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<Container maxW="container.xl" pt="4vh">
|
<Container maxW="container.xl" pt="4vh">
|
||||||
<Heading textAlign="start">Our Team</Heading>
|
<Heading textAlign="start">Our Team</Heading>
|
||||||
@ -52,5 +52,3 @@ export function Page() {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const title = "Team - Car Crushers";
|
|
@ -1,6 +1,6 @@
|
|||||||
import { Container, Heading, Link, Text } from "@chakra-ui/react";
|
import { Container, Heading, Link, Text } from "@chakra-ui/react";
|
||||||
|
|
||||||
export function Page() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<Container maxW="container.lg" pb="8vh" pt="4vh" textAlign="start">
|
<Container maxW="container.lg" pb="8vh" pt="4vh" textAlign="start">
|
||||||
<Heading>Terms and Conditions</Heading>
|
<Heading>Terms and Conditions</Heading>
|
||||||
@ -118,5 +118,3 @@ export function Page() {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const title = "Terms - Car Crushers";
|
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, type ReactNode } from "react";
|
import { Component, type ReactNode } from "react";
|
||||||
import Navigation from "./Navigation";
|
import Navigation from "./Navigation.js";
|
||||||
import { Code, Container, Heading, Text } from "@chakra-ui/react";
|
import { Code, Container, Heading, Text } from "@chakra-ui/react";
|
||||||
|
|
||||||
interface ErrorState {
|
interface ErrorState {
|
||||||
|
@ -32,6 +32,7 @@ export default function (props: {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
discriminator?: string;
|
discriminator?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
hide?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
permissions?: number;
|
permissions?: number;
|
||||||
username?: string;
|
username?: string;
|
||||||
@ -105,7 +106,7 @@ export default function (props: {
|
|||||||
</Center>
|
</Center>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
{props.id ? (
|
{props.hide ? null : props.id ? (
|
||||||
<HStack spacing="3">
|
<HStack spacing="3">
|
||||||
<Avatar
|
<Avatar
|
||||||
display={props.id ? "flex" : "none"}
|
display={props.id ? "flex" : "none"}
|
||||||
|
79
emotion-server.js
Normal file
79
emotion-server.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// @emotion/server is known to not work correctly in the Cloudflare Workers environment
|
||||||
|
// See https://github.com/emotion-js/emotion/issues/2446#issuecomment-1372440174
|
||||||
|
|
||||||
|
function createExtractCriticalToChunks(cache) {
|
||||||
|
return function (html) {
|
||||||
|
const RGX = new RegExp(`${cache.key}-([a-zA-Z0-9-_]+)`, "gm");
|
||||||
|
|
||||||
|
const o = { html, styles: [] };
|
||||||
|
let match;
|
||||||
|
const ids = {};
|
||||||
|
while ((match = RGX.exec(html)) !== null) {
|
||||||
|
if (ids[match[1]] === undefined) {
|
||||||
|
ids[match[1]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const regularCssIds = [];
|
||||||
|
let regularCss = "";
|
||||||
|
|
||||||
|
Object.keys(cache.inserted).forEach((id) => {
|
||||||
|
if (
|
||||||
|
(ids[id] !== undefined ||
|
||||||
|
cache.registered[`${cache.key}-${id}`] === undefined) &&
|
||||||
|
cache.inserted[id] !== true
|
||||||
|
) {
|
||||||
|
if (cache.registered[`${cache.key}-${id}`]) {
|
||||||
|
regularCssIds.push(id);
|
||||||
|
regularCss += cache.inserted[id];
|
||||||
|
} else {
|
||||||
|
o.styles.push({
|
||||||
|
key: `${cache.key}-global`,
|
||||||
|
ids: [id],
|
||||||
|
css: cache.inserted[id],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
o.styles.push({ key: cache.key, ids: regularCssIds, css: regularCss });
|
||||||
|
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateStyleTag(cssKey, ids, styles, nonceString) {
|
||||||
|
return `<style data-emotion="${cssKey} ${ids}"${nonceString}>${styles}</style>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConstructStyleTagsFromChunks(cache, nonceString) {
|
||||||
|
return function (criticalData) {
|
||||||
|
let styleTagsString = "";
|
||||||
|
|
||||||
|
criticalData.styles.forEach((item) => {
|
||||||
|
styleTagsString += generateStyleTag(
|
||||||
|
item.key,
|
||||||
|
item.ids.join(" "),
|
||||||
|
item.css,
|
||||||
|
nonceString
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return styleTagsString;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEmotionServer(cache) {
|
||||||
|
if (cache.compat !== true) {
|
||||||
|
cache.compat = true;
|
||||||
|
}
|
||||||
|
const nonceString =
|
||||||
|
cache.nonce !== undefined ? ` nonce="${cache.nonce}"` : "";
|
||||||
|
return {
|
||||||
|
extractCriticalToChunks: createExtractCriticalToChunks(cache),
|
||||||
|
constructStyleTagsFromChunks: createConstructStyleTagsFromChunks(
|
||||||
|
cache,
|
||||||
|
nonceString
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
import { renderPage } from "vite-plugin-ssr";
|
|
||||||
|
|
||||||
async function constructHTML(context: RequestContext) {
|
async function constructHTML(context: RequestContext) {
|
||||||
const { pathname } = new URL(context.request.url);
|
const { pathname } = new URL(context.request.url);
|
||||||
|
|
||||||
@ -12,21 +10,7 @@ async function constructHTML(context: RequestContext) {
|
|||||||
)
|
)
|
||||||
return await context.env.ASSETS.fetch(context.request);
|
return await context.env.ASSETS.fetch(context.request);
|
||||||
|
|
||||||
const { httpResponse, status } = await renderPage({
|
return await context.next();
|
||||||
current_user: context.data.current_user,
|
|
||||||
kv: context.env.DATA,
|
|
||||||
status: 200,
|
|
||||||
urlOriginal: context.request.url,
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(httpResponse?.getReadableWebStream(), {
|
|
||||||
headers: {
|
|
||||||
"content-type": httpResponse?.contentType ?? "text/html;charset=utf-8",
|
|
||||||
},
|
|
||||||
status: [200, 404, 500].includes(status)
|
|
||||||
? httpResponse?.statusCode
|
|
||||||
: status,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateTokenHash(token: string) {
|
async function generateTokenHash(token: string) {
|
||||||
|
27
index.d.ts
vendored
27
index.d.ts
vendored
@ -1,8 +1,8 @@
|
|||||||
/// <reference types="vite/client" />
|
import type { EmotionCache } from "@emotion/utils";
|
||||||
|
|
||||||
import { type PageContextBuiltIn } from "vite-plugin-ssr";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
module "*.css";
|
||||||
|
|
||||||
interface Env {
|
interface Env {
|
||||||
ASSETS: Fetcher;
|
ASSETS: Fetcher;
|
||||||
DATA: KVNamespace;
|
DATA: KVNamespace;
|
||||||
@ -10,15 +10,20 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RequestContext = EventContext<Env, string, { [k: string]: any }>;
|
type RequestContext = EventContext<Env, string, { [k: string]: any }>;
|
||||||
interface PageContext extends PageContextBuiltIn {
|
|
||||||
current_user?: { [k: string]: any };
|
interface EmotionCriticalToChunks {
|
||||||
kv: KVNamespace;
|
html: string;
|
||||||
pageProps: {
|
styles: { key: string; ids: string[]; css: string }[];
|
||||||
[k: string]: any;
|
|
||||||
};
|
|
||||||
requireAuth?: boolean;
|
|
||||||
status: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EmotionServer {
|
||||||
|
constructStyleTagsFromChunks: (
|
||||||
|
criticalData: EmotionCriticalToChunks
|
||||||
|
) => string;
|
||||||
|
extractCriticalToChunks: (html: string) => EmotionCriticalToChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEmotionServer(cache: EmotionCache): EmotionServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
17334
package-lock.json
generated
17334
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@ -13,21 +13,25 @@
|
|||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
"@fontsource/plus-jakarta-sans": "^4.5.11",
|
"@fontsource/plus-jakarta-sans": "^4.5.11",
|
||||||
"@sentry/react": "^7.38.0",
|
"@remix-run/cloudflare": "^1.14.0",
|
||||||
"@sentry/tracing": "^7.38.0",
|
"@remix-run/cloudflare-pages": "^1.14.0",
|
||||||
"framer-motion": "^9.0.4",
|
"@remix-run/react": "^1.14.0",
|
||||||
|
"@sentry/react": "^7.40.0",
|
||||||
|
"@sentry/tracing": "^7.40.0",
|
||||||
|
"framer-motion": "^10.0.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"react-helmet-async": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20230215.0",
|
"@cloudflare/workers-types": "^4.20230215.0",
|
||||||
|
"@remix-run/dev": "^1.14.0",
|
||||||
|
"@types/node": "^18.14.6",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@vitejs/plugin-react": "^3.1.0",
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
"esbuild": "^0.17.8",
|
"esbuild": "^0.17.8",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5"
|
||||||
"vite": "^4.1.2",
|
|
||||||
"vite-plugin-ssr": "^0.4.84"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
export async function onBeforeRender(pageContext: PageContext) {
|
|
||||||
if (!pageContext.current_user)
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
logged_in: false,
|
|
||||||
},
|
|
||||||
status: 401,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const blockedAppeal = await pageContext.kv?.get(
|
|
||||||
`blockedappeal_${pageContext.current_user.id}`
|
|
||||||
);
|
|
||||||
const disabledStatus = await pageContext.kv?.get("appeal_disabled");
|
|
||||||
const openAppeals = await pageContext.kv?.list({
|
|
||||||
prefix: `appeal_${pageContext.current_user.id}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
can_appeal:
|
|
||||||
!Boolean(disabledStatus) &&
|
|
||||||
!Boolean(blockedAppeal) &&
|
|
||||||
!Boolean(
|
|
||||||
openAppeals.keys.find(
|
|
||||||
(appeal) => (appeal.metadata as { [k: string]: any }).open
|
|
||||||
)
|
|
||||||
),
|
|
||||||
can_toggle:
|
|
||||||
pageContext.current_user?.permissions & (1 << 0) ||
|
|
||||||
pageContext.current_user?.permissions & (1 << 11),
|
|
||||||
disabled: Boolean(disabledStatus),
|
|
||||||
logged_in: true,
|
|
||||||
},
|
|
||||||
status: pageContext.current_user ? 200 : 401,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const description = "Appeal your Discord ban here.";
|
|
||||||
export const title = "Appeals - Car Crushers";
|
|
@ -1,72 +0,0 @@
|
|||||||
export async function onBeforeRender(pageContext: PageContext) {
|
|
||||||
const { current_user: currentUser } = pageContext;
|
|
||||||
|
|
||||||
if (!currentUser)
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
logged_in: false,
|
|
||||||
},
|
|
||||||
status: 401,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const newItemPermissions = {
|
|
||||||
game_ban: [1 << 5],
|
|
||||||
inactivity: [1 << 2, 1 << 9, 1 << 10],
|
|
||||||
infraction: [1 << 0, 1 << 2, 1 << 6, 1 << 7]
|
|
||||||
};
|
|
||||||
|
|
||||||
const newItemNames: { [k: string]: string } = {
|
|
||||||
game_ban: "Game Ban",
|
|
||||||
inactivity: "Inactivity Notice",
|
|
||||||
infraction: "Infraction",
|
|
||||||
};
|
|
||||||
|
|
||||||
const typePermissions = {
|
|
||||||
appeal: [1 << 0, 1 << 1],
|
|
||||||
gma: [1 << 5],
|
|
||||||
report: [1 << 5],
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeNames: { [k: string]: string } = {
|
|
||||||
appeal: "Discord Appeals",
|
|
||||||
gma: "Game Appeals",
|
|
||||||
report: "Game Reports",
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowedNewItems = [];
|
|
||||||
const allowedTypes = [];
|
|
||||||
|
|
||||||
for (const [item, ints] of Object.entries(newItemPermissions)) {
|
|
||||||
if (ints.find((i) => currentUser.permissions & i))
|
|
||||||
allowedNewItems.push({ name: newItemNames[item], value: item })
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [type, ints] of Object.entries(typePermissions)) {
|
|
||||||
if (ints.find((i) => currentUser.permissions & i))
|
|
||||||
allowedTypes.push({ name: typeNames[type], value: type });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allowedTypes.length)
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
entry_types: [],
|
|
||||||
item_types: [],
|
|
||||||
logged_in: true,
|
|
||||||
},
|
|
||||||
status: 403,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
entry_types: allowedTypes,
|
|
||||||
item_types: allowedNewItems,
|
|
||||||
logged_in: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export async function onBeforeRender(pageContext: PageContext) {
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps: {
|
|
||||||
logged_in: Boolean(pageContext.current_user),
|
|
||||||
},
|
|
||||||
status: pageContext.current_user ? 200 : 401,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const description = "Found a cheater in Car Crushers 2? Report them here.";
|
|
||||||
export const title = "Report - Car Crushers";
|
|
10
remix.config.js
Normal file
10
remix.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default {
|
||||||
|
future: {
|
||||||
|
v2_routeConvention: true,
|
||||||
|
},
|
||||||
|
server: "./server.ts",
|
||||||
|
serverBuildTarget: "cloudflare-pages",
|
||||||
|
serverMinify: true,
|
||||||
|
serverModuleFormat: "esm",
|
||||||
|
serverPlatform: "neutral",
|
||||||
|
};
|
@ -1,41 +0,0 @@
|
|||||||
import { StrictMode } from "react";
|
|
||||||
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
||||||
import { ChakraProvider } from "@chakra-ui/react";
|
|
||||||
import "../index.css";
|
|
||||||
import "@fontsource/plus-jakarta-sans";
|
|
||||||
import theme from "../theme";
|
|
||||||
import * as Sentry from "@sentry/react";
|
|
||||||
import { Integrations } from "@sentry/tracing";
|
|
||||||
import Fallback from "../components/Fallback";
|
|
||||||
import Navigation from "../components/Navigation";
|
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
dsn: import.meta.env.VITE_DSN,
|
|
||||||
integrations: [new Integrations.BrowserTracing()],
|
|
||||||
tracesSampleRate: import.meta.env.VITE_SAMPLE_RATE
|
|
||||||
? parseFloat(import.meta.env.VITE_SAMPLE_RATE)
|
|
||||||
: 0.1,
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function render(pageContext: PageContext) {
|
|
||||||
const { Page, pageProps } = pageContext;
|
|
||||||
const root = document.getElementById("root") as HTMLElement;
|
|
||||||
const reactRoot = (
|
|
||||||
<StrictMode>
|
|
||||||
<ChakraProvider theme={theme}>
|
|
||||||
<div className="App">
|
|
||||||
<Fallback>
|
|
||||||
<Navigation {...pageContext.current_user} />
|
|
||||||
<Page {...pageProps} />
|
|
||||||
</Fallback>
|
|
||||||
</div>
|
|
||||||
</ChakraProvider>
|
|
||||||
</StrictMode>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (root.innerHTML === "") {
|
|
||||||
createRoot(root).render(reactRoot);
|
|
||||||
} else {
|
|
||||||
hydrateRoot(root, reactRoot);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import ReactDOMServer from "react-dom/server";
|
|
||||||
import { StrictMode } from "react";
|
|
||||||
import { dangerouslySkipEscape, escapeInject } from "vite-plugin-ssr";
|
|
||||||
import theme from "../theme";
|
|
||||||
import { ChakraProvider } from "@chakra-ui/react";
|
|
||||||
import Fallback from "../components/Fallback";
|
|
||||||
import Navigation from "../components/Navigation";
|
|
||||||
import Login from "../components/Login";
|
|
||||||
import Forbidden from "../components/Forbidden";
|
|
||||||
|
|
||||||
export const passToClient = ["current_user", "pageProps"];
|
|
||||||
|
|
||||||
export async function render(
|
|
||||||
pageContext: PageContext & { pageProps: { [k: string]: any } }
|
|
||||||
) {
|
|
||||||
const { exports, Page, pageProps, status } = pageContext;
|
|
||||||
|
|
||||||
const reactHTML = Page
|
|
||||||
? ReactDOMServer.renderToString(
|
|
||||||
<StrictMode>
|
|
||||||
<ChakraProvider theme={theme}>
|
|
||||||
<div className="App">
|
|
||||||
<Fallback>
|
|
||||||
<Navigation {...pageContext.current_user} />
|
|
||||||
{status === 200 ? (
|
|
||||||
Page ? (
|
|
||||||
<Page {...pageProps} />
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
{ 401: <Login />, 403: <Forbidden /> }[status]
|
|
||||||
)}
|
|
||||||
</Fallback>
|
|
||||||
</div>
|
|
||||||
</ChakraProvider>
|
|
||||||
</StrictMode>
|
|
||||||
)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return escapeInject`<!DOCTYPE html>
|
|
||||||
<html lang="en-US">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="theme-color" content="#00a8f8" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
<link rel="apple-touch-icon" type="image/png" href="/files/logo192.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="description" content="${
|
|
||||||
(exports.description as string) ?? "Car Crushers Website"
|
|
||||||
}" />
|
|
||||||
<meta property="og:description" content="${
|
|
||||||
(exports.description as string | null) ?? "Car Crushers Website"
|
|
||||||
}" />
|
|
||||||
<title>${(exports.title as string | null) ?? "Car Crushers"}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root">${dangerouslySkipEscape(reactHTML)}</div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { Container, Heading, Link, Text } from "@chakra-ui/react";
|
|
||||||
import { PageContextBuiltIn } from "vite-plugin-ssr";
|
|
||||||
|
|
||||||
export function Page(pageProps: PageContextBuiltIn) {
|
|
||||||
if (pageProps.is404)
|
|
||||||
return (
|
|
||||||
<Container maxW="container.lg" pt="8vh" textAlign="left">
|
|
||||||
<Heading size="4xl">404</Heading>
|
|
||||||
<br />
|
|
||||||
<Text fontSize="xl">There is nothing to find here.</Text>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<Link color="#646cff" onClick={() => history.go(-1)}>
|
|
||||||
Go back
|
|
||||||
</Link>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container maxW="container.lg" pt="8vh" textAlign="left">
|
|
||||||
<Heading size="4xl">500</Heading>
|
|
||||||
<br />
|
|
||||||
<Text fontSize="xl">S̶̡͈̠̗̠͖͙̭o̶̶͕͚̥͍̪̤m̸̨͏͈͔̖͚̖̰̱͞e҉̵͖͚͇̀t̕͟͠͏͎̺̯̲̱̣̤̠̟͙̠̙̫̬ḩ̸̭͓̬͎̙̀į̞̮͉͖̰̥̹͚̫̙̪̗̜̳̕ͅn҉͔̯̪̗̝̝͖̲͇͍͎̲̲̤̖̫͈̪͡g̴̰̻̙̝͉̭͇̖̰̝̙͕̼͙͘͜ ̵̶̫̥̳̲̘̻̗͈͕̭̲͇̘̜̺̟̥̖̥b̴̙̭̹͕̞͠r̞͎̠̩͈̖̰̞̯̯͢͢͠ͅo̝̯̗̹̳͍̰͉͕̘̰̠̺̥̰͔̕ͅk̵̸̻̠͕̺̦̦͖̲̺̦̞̝̞͞͡e̶͏̤̼̼͔̘̰̰̭͈̀͞͡</Text>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<Link color="#646cff" onClick={() => location.reload()}>
|
|
||||||
Reload
|
|
||||||
</Link>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
12
server.ts
Normal file
12
server.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
|
||||||
|
import * as build from "@remix-run/dev/server-build.js";
|
||||||
|
|
||||||
|
const handleRequest = createPagesFunctionHandler({
|
||||||
|
build,
|
||||||
|
mode: process.env.NODE_ENV,
|
||||||
|
getLoadContext: (context) => context,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function onRequest(context: RequestContext) {
|
||||||
|
return handleRequest(context);
|
||||||
|
}
|
@ -1,21 +1,22 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ES2020",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"allowJs": false,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": false,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node16",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"types": ["@cloudflare/workers-types"]
|
"types": ["@cloudflare/workers-types", "@types/node"],
|
||||||
|
"baseUrl": "."
|
||||||
},
|
},
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"include": ["**/*.ts", "**/*.tsx"]
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import react from "@vitejs/plugin-react";
|
|
||||||
import ssr from "vite-plugin-ssr/plugin";
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react(), ssr({ prerender: true })],
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user