Initial commit
This commit is contained in:
commit
56f2195962
14 changed files with 1121 additions and 0 deletions
10
src/endpoints/ping.ts
Normal file
10
src/endpoints/ping.ts
Normal 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
60
src/main.ts
Normal 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
30
src/schemas/config.ts
Normal 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
3
src/utils/cleanExit.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export function cleanExit() {
|
||||
process.exit(0);
|
||||
};
|
||||
48
src/utils/config.ts
Normal file
48
src/utils/config.ts
Normal 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;
|
||||
};
|
||||
25
src/utils/database/json.ts
Normal file
25
src/utils/database/json.ts
Normal 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));
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue