Initial commit
This commit is contained in:
11
src/server/api.ts
Normal file
11
src/server/api.ts
Normal 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
18
src/server/api/echo.ts
Normal 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
1
src/server/dbal/dbal.ts
Normal file
@ -0,0 +1 @@
|
||||
// Database Access Layer stuff goes here
|
31
src/server/index.ts
Normal file
31
src/server/index.ts
Normal 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)
|
||||
}
|
48
src/server/public/index.html
Normal file
48
src/server/public/index.html
Normal 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
7
src/server/routelist.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import echo from "./api/echo.ts";
|
||||
|
||||
export const routeList = [
|
||||
echo,
|
||||
];
|
||||
|
||||
export type RouteList = typeof routeList;
|
47
src/server/util/routewrap.ts
Normal file
47
src/server/util/routewrap.ts
Normal 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,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user