Initial commit

This commit is contained in:
2023-10-29 19:28:07 +00:00
commit b0d0353ba7
25 changed files with 7930 additions and 0 deletions

34
src/client/app.tsx Normal file
View File

@ -0,0 +1,34 @@
import { css } from "@emotion/css";
import { Center, Cover, Stack } from "@firebox/components";
const App = (props: { name: string }) => {
const {name} = props;
return (
<Stack>
<div className={css`background-color: floralwhite;`}>
<Cover gap pad>
<Center>
<Stack gap={-1}>
<h1>Hello, {name}!</h1>
<p>Welcome to a website with a certain design philosophy. Tell me how it's working out! I want to see this text wrap a few times. Hopefully this sentence will help.</p>
</Stack>
</Center>
<Cover.Footer>A page by Dylan Pizzo</Cover.Footer>
</Cover>
</div>
<div className={css`background-color: aliceblue;`}>
<Cover gap pad>
<Center>
<Stack gap={-1}>
<h1>Hello, {name}!</h1>
<p>Welcome to a website with a certain design philosophy. Tell me how it's working out! I want to see this text wrap a few times. Hopefully this sentence will help.</p>
</Stack>
</Center>
<Cover.Footer>A page by Dylan Pizzo</Cover.Footer>
</Cover>
</div>
</Stack>
);
};
export const app = <App name="World" />;

6
src/client/index.ts Normal file
View File

@ -0,0 +1,6 @@
import { createRoot } from "react-dom/client";
import { app } from "./app.js";
const domNode = document.getElementById("root")!;
const root = createRoot(domNode);
root.render(app);

28
src/database/db.ts Normal file
View File

@ -0,0 +1,28 @@
import createConnectionPool, {ConnectionPool, ConnectionPoolConfig, sql} from '@databases/pg';
export {sql};
const portString = process.env["DB_PORT"];
const portNumber = portString ? parseInt(portString) : undefined;
const clientConfig: ConnectionPoolConfig = {
host: process.env["DB_HOST"],
user: process.env["DB_USER"],
database: process.env["DB_NAME"],
password: process.env["DB_PASSWORD"],
port: portNumber,
};
// @ts-ignore
const db: ConnectionPool = createConnectionPool({
connectionString: false,
...clientConfig
});
process.once('SIGTERM', () => {
db.dispose().catch((ex) => {
console.error(ex);
});
});
export {db};

View File

@ -0,0 +1,4 @@
CREATE TABLE users (
id text,
username text
)

11
src/server/api.ts Normal file
View File

@ -0,0 +1,11 @@
import type { RouteList } from "./routelist.ts"
type RouteUrl = RouteList[number]["url"];
type HttpMethod = RouteList[number]["method"];
type Route<M extends HttpMethod, U extends RouteUrl> = Extract<RouteList[number], {url: U, method: M}>;
export type RoutePayload<M extends HttpMethod, U extends RouteUrl> = Parameters<Route<M, U>["handler"]>[0]["payload"];
export type RouteResponse<M extends HttpMethod, U extends RouteUrl> = Awaited<ReturnType<Route<M, U>["handler"]>>;

18
src/server/api/echo.ts Normal file
View File

@ -0,0 +1,18 @@
import { Type } from "@sinclair/typebox";
import { FirRouteInput, FirRouteOptions } from "../util/routewrap.js";
const method = "GET";
const url = "/echo";
const payloadT = Type.Any();
const handler = ({payload}: FirRouteInput<typeof payloadT>) => {
return payload;
};
export default {
method,
url,
payloadT,
handler,
} as const satisfies FirRouteOptions<typeof payloadT>;

1
src/server/dbal/dbal.ts Normal file
View File

@ -0,0 +1 @@
// Database Access Layer stuff goes here

31
src/server/index.ts Normal file
View File

@ -0,0 +1,31 @@
// Import the framework and instantiate it
import Fastify from 'fastify'
import fastifyStatic from '@fastify/static'
import { routeList } from "./routelist.ts";
import { route } from "./util/routewrap.ts";
console.log(process.env["DATABASE_URL"]);
const server = Fastify({
logger: true
});
server.register(fastifyStatic, {
root: new URL('public', import.meta.url).toString().slice("file://".length),
prefix: '/',
});
routeList.forEach(firRoute => {
server.route(route(firRoute));
})
// Run the server!
try {
// Note: host needs to be 0.0.0.0 rather than omitted or localhost, otherwise
// it always returns an empty reply when used inside docker...
// See: https://github.com/fastify/fastify/issues/935
await server.listen({ port: parseInt(process.env["PORT"] ?? "3000"), host: "0.0.0.0" })
} catch (err) {
server.log.error(err)
process.exit(1)
}

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
<script src="dist/index.js" type="module"></script>
<style>
:root {
--measure: 64ch;
--ratio: 1.618;
--s-5: calc(var(--s-4) / var(--ratio));
--s-4: calc(var(--s-3) / var(--ratio));
--s-3: calc(var(--s-2) / var(--ratio));
--s-2: calc(var(--s-1) / var(--ratio));
--s-1: calc(var(--s0) / var(--ratio));
--s0: 1rem;
--s1: calc(var(--s0) * var(--ratio));
--s2: calc(var(--s1) * var(--ratio));
--s3: calc(var(--s2) * var(--ratio));
--s4: calc(var(--s3) * var(--ratio));
--s5: calc(var(--s4) * var(--ratio));
--border-radius: 0.5rem;
font-size: calc(1rem + 0.15vw);
font-family: sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
max-inline-size: var(--measure);
}
html,
body,
div,
header,
nav,
main,
footer {
max-inline-size: none;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>

7
src/server/routelist.ts Normal file
View File

@ -0,0 +1,7 @@
import echo from "./api/echo.ts";
export const routeList = [
echo,
];
export type RouteList = typeof routeList;

View File

@ -0,0 +1,47 @@
import { Static, TSchema } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";
import { HTTPMethods } from "fastify"
import { RouteOptions } from "fastify/types/route.js";
type URLString = string;
export type FirRouteInput<TPayloadSchema extends TSchema> = {
payload: Static<TPayloadSchema>,
}
export type FirRouteOptions<TIn extends TSchema = TSchema, TOut extends TSchema = TSchema> = {
method: HTTPMethods,
url: URLString,
payloadT: TIn,
responseT?: TOut,
handler: (input: FirRouteInput<TIn>) => Static<TOut> | Promise<Static<TOut>>,
}
export const route = <TIn extends TSchema, TOut extends TSchema>(routeOptions: FirRouteOptions<TIn, TOut>): RouteOptions => {
const {
method,
url,
payloadT,
handler,
} = routeOptions;
const augmentedHandler = (request: Parameters<RouteOptions["handler"]>[0]) => {
const {
body,
query,
} = request;
const payload = body ?? query;
if (Value.Check(payloadT, payload)) {
return handler({payload});
} else {
throw new Error("Payload wrong shape.");
}
}
return {
method,
url,
handler: augmentedHandler,
}
}