0
0
Fork 0

Initial commit

This commit is contained in:
Oliver 2022-12-23 15:19:37 -06:00 committed by GitHub
commit 56f2195962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1121 additions and 0 deletions

10
src/endpoints/ping.ts Normal file
View file

@ -0,0 +1,10 @@
import { ServerRoute } from "@hapi/hapi";
const route: ServerRoute = {
method: `*`, path: `/ping`,
options: {},
async handler() {
return `pong!`;
},
};
export default route;

60
src/main.ts Normal file
View file

@ -0,0 +1,60 @@
// Filepath aliasing to avoid relative-imports whenever possible, this must stay
// at the top of this file as the first statement
import "module-alias/register";
import { JSONDatabase } from "./utils/database/json";
import { Server, Request } from "@hapi/hapi";
import { loadConfig } from "./utils/config";
import inert from "@hapi/inert";
import { Logger } from "tslog";
import path from "path";
import glob from "glob";
export const isDev = process.env.NODE_ENV?.startsWith(`dev`);
export const config = loadConfig();
export const database = new JSONDatabase(config.database);
export const log = new Logger({
displayFilePath: `hidden`,
displayFunctionName: false,
minLevel: isDev ? `silly` : `info`
});
// Handle the system exiting so we can cleanup before shutting down
import { cleanExit } from "./utils/cleanExit";
process.on(`uncaughtException`, cleanExit);
process.on(`SIGTERM`, cleanExit);
process.on(`SIGINT`, cleanExit);
async function init() {
const server = new Server({
port: config.server.port,
routes: {
files: {
relativeTo: path.join(__dirname, `../site`),
},
cors: true,
},
});
await server.register(inert);
// Register all the routes
let files = glob.sync(
`endpoints/**/!(*.map)`,
{ cwd: __dirname, nodir: true}
);
for (var file of files) {
let route = (await import(path.join(__dirname, file))).default;
console.log(`Registering route: ${route.method} ${route.path}`);
server.route(route);
};
server.start().then(() => {
console.log(`Server listening on ${server.info.uri}`);
});
};
init();

30
src/schemas/config.ts Normal file
View file

@ -0,0 +1,30 @@
import Joi from "joi";
export const serverOptionsSchema = Joi.object({
port: Joi
.number()
.port()
.required()
.description(`The port the server will run on`),
})
.meta({ className: `serverOptions` })
.description(`The web server options`);
export const databaseOptionsSchema = Joi.object({
uri: Joi
.string()
.required()
.description(`The location indicator where the database is. This can be a filepath or a socket URI, depending on what database is being used.`),
})
.meta({ className: `databaseOptions` })
.description(`The database specific options`);
export const configSchema = Joi.object({
server: serverOptionsSchema.required(),
database: databaseOptionsSchema.required(),
})
.meta({ className: `config` })
.description(`The configuration format for the server`);

3
src/utils/cleanExit.ts Normal file
View file

@ -0,0 +1,3 @@
export function cleanExit() {
process.exit(0);
};

48
src/utils/config.ts Normal file
View file

@ -0,0 +1,48 @@
import { configSchema } from "$/schemas/config";
import type { config } from "$/types/config";
import { isDev } from "$/main";
import toml from "@iarna/toml";
import fs from "fs";
/**
* Attempts to load the config from disk and validate it's structure.
*/
export function loadConfig() {
let file = null;
if (isDev) {
try {
file = fs.readFileSync(`./config.dev.toml`, `utf-8`);
} catch (_) {
console.error(`Couldn't find development config, checking production`);
};
};
if (!file) {
try {
file = fs.readFileSync(`./config.toml`, `utf-8`);
} catch (_) {
console.error(`Couldn't find production config. Fill out the config and run the server again`);
process.exit(1);
};
};
try {
var data = toml.parse(file);
} catch (_) {
console.error(`Invalid TOML file, stopping server`);
process.exit(1);
};
let { error, value } = configSchema.validate(data, { abortEarly: false });
if (error) {
console.error(`Config failed to validate, see below for details:`)
for (const err of error.details) {
console.error(` - ${err.message}`);
};
process.exit(1);
};
return value as config;
};

View file

@ -0,0 +1,25 @@
import { databaseOptions } from "$/types/config";
import fs from "fs";
export class JSONDatabase {
private data = {};
private conf: databaseOptions;
constructor(conf: databaseOptions) {
this.conf = conf;
if (!fs.existsSync(conf.uri)) {
console.error(`Can't find database file, creating default`);
try {
fs.writeFileSync(conf.uri, `{}`);
} catch (_) {
console.log(`Couldn't create database file, ensure the uri is a valid filepath`);
};
};
this.data = JSON.parse(fs.readFileSync(conf.uri, `utf-8`));
};
public shutdown() {
fs.writeFileSync(this.conf.uri, JSON.stringify(this.data));
};
};