Restructuring as TypeScript
This commit is contained in:
parent
888688a019
commit
a2cb68b421
@ -1,10 +1,11 @@
|
||||
const fetch = require('node-fetch');
|
||||
const WebSocket = require('ws');
|
||||
const fs = require('fs').promises;
|
||||
const calculateLongestRoad = require('./longest-road.js');
|
||||
// @ts-nocheck
|
||||
import fetch from 'node-fetch';
|
||||
import WebSocket from 'ws';
|
||||
import fs from 'fs';
|
||||
import calculateLongestRoad from './longest-road.js';
|
||||
|
||||
const { getValidRoads, getValidCorners } = require('../util/validLocations.js');
|
||||
const { layout, staticData } = require('../util/layout.js');
|
||||
import { getValidRoads, getValidCorners } from '../util/validLocations.js';
|
||||
import { layout, staticData } from '../util/layout.js';
|
||||
|
||||
const version = '0.0.1';
|
||||
|
||||
@ -24,14 +25,14 @@ const server = process.argv[2];
|
||||
const gameId = process.argv[3];
|
||||
const name = process.argv[4];
|
||||
|
||||
const game = {};
|
||||
const game: any = {};
|
||||
const anyValue = undefined;
|
||||
|
||||
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
|
||||
|
||||
/* Do not use arrow function as this is rebound to have
|
||||
* this as the WebSocket */
|
||||
let send = function (data) {
|
||||
let send = function (this: WebSocket, data: any) {
|
||||
if (data.type === 'get') {
|
||||
console.log(`ws - send: get`, data.fields);
|
||||
} else {
|
||||
@ -40,7 +41,7 @@ let send = function (data) {
|
||||
this.send(JSON.stringify(data));
|
||||
};
|
||||
|
||||
const error = (e) => {
|
||||
const error = (e: any) => {
|
||||
console.log(`ws - error`, e);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
const { layout } = require('../util/layout.js');
|
||||
// @ts-nocheck
|
||||
import { layout } from '../util/layout.js';
|
||||
|
||||
const processCorner = (game, color, cornerIndex, placedCorner) => {
|
||||
const processCorner = (game: any, color: string, cornerIndex: number, placedCorner: any): number => {
|
||||
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
||||
if (placedCorner.color && placedCorner.color !== color) {
|
||||
return 0;
|
||||
@ -13,7 +14,7 @@ const processCorner = (game, color, cornerIndex, placedCorner) => {
|
||||
placedCorner.walking = true;
|
||||
/* Calculate the longest road branching from both corners */
|
||||
let longest = 0;
|
||||
layout.corners[cornerIndex].roads.forEach(roadIndex => {
|
||||
layout.corners[cornerIndex].roads.forEach((roadIndex: number) => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
if (placedRoad.walking) {
|
||||
return;
|
||||
@ -32,7 +33,7 @@ const processCorner = (game, color, cornerIndex, placedCorner) => {
|
||||
return longest;
|
||||
};
|
||||
|
||||
const buildCornerGraph = (game, color, cornerIndex, placedCorner, set) => {
|
||||
const buildCornerGraph = (game: any, color: string, cornerIndex: number, placedCorner: any, set: any) => {
|
||||
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
||||
if (placedCorner.color && placedCorner.color !== color) {
|
||||
return;
|
||||
@ -44,13 +45,13 @@ const buildCornerGraph = (game, color, cornerIndex, placedCorner, set) => {
|
||||
|
||||
placedCorner.walking = true;
|
||||
/* Calculate the longest road branching from both corners */
|
||||
layout.corners[cornerIndex].roads.forEach(roadIndex => {
|
||||
layout.corners[cornerIndex].roads.forEach((roadIndex: number) => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
buildRoadGraph(game, color, roadIndex, placedRoad, set);
|
||||
});
|
||||
};
|
||||
|
||||
const processRoad = (game, color, roadIndex, placedRoad) => {
|
||||
const processRoad = (game: any, color: string, roadIndex: number, placedRoad: any): number => {
|
||||
/* If this road isn't assigned to the walking color, skip it */
|
||||
if (placedRoad.color !== color) {
|
||||
return 0;
|
||||
@ -75,7 +76,7 @@ const processRoad = (game, color, roadIndex, placedRoad) => {
|
||||
return roadLength;
|
||||
};
|
||||
|
||||
const buildRoadGraph = (game, color, roadIndex, placedRoad, set) => {
|
||||
const buildRoadGraph = (game: any, color: string, roadIndex: number, placedRoad: any, set: any) => {
|
||||
/* If this road isn't assigned to the walking color, skip it */
|
||||
if (placedRoad.color !== color) {
|
||||
return;
|
||||
@ -94,7 +95,7 @@ const buildRoadGraph = (game, color, roadIndex, placedRoad, set) => {
|
||||
});
|
||||
};
|
||||
|
||||
const clearRoadWalking = (game) => {
|
||||
const clearRoadWalking = (game: any) => {
|
||||
/* Clear out walk markers on roads */
|
||||
layout.roads.forEach((item, itemIndex) => {
|
||||
delete game.placements.roads[itemIndex].walking;
|
||||
@ -106,7 +107,7 @@ const clearRoadWalking = (game) => {
|
||||
});
|
||||
}
|
||||
|
||||
const calculateRoadLengths = (game) => {
|
||||
const calculateRoadLengths = (game: any) => {
|
||||
const color = game.color;
|
||||
clearRoadWalking(game);
|
||||
|
||||
@ -158,4 +159,4 @@ const calculateRoadLengths = (game) => {
|
||||
return final;
|
||||
};
|
||||
|
||||
module.exports = calculateRoadLengths;
|
||||
export default calculateRoadLengths;
|
@ -1,19 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
// @ts-nocheck
|
||||
process.env.TZ = "Etc/GMT";
|
||||
|
||||
console.log("Loading ketr.ketran");
|
||||
|
||||
const express = require("express"),
|
||||
bodyParser = require("body-parser"),
|
||||
config = require("config"),
|
||||
session = require('express-session'),
|
||||
basePath = require("./basepath"),
|
||||
cookieParser = require("cookie-parser"),
|
||||
app = express(),
|
||||
fs = require('fs');
|
||||
import express from "express";
|
||||
import bodyParser from "body-parser";
|
||||
import config from "config";
|
||||
import session from 'express-session';
|
||||
import basePath from "./basepath";
|
||||
import cookieParser from "cookie-parser";
|
||||
import fs from 'fs';
|
||||
import http from "http";
|
||||
|
||||
const server = require("http").createServer(app);
|
||||
const app = express();
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
@ -21,7 +22,7 @@ app.use(cookieParser());
|
||||
// and URL for requests under the configured basePath so we can trace which
|
||||
// service (server or dev proxy) is handling requests and their returned
|
||||
// status during debugging. Keep this lightweight.
|
||||
app.use((req, res, next) => {
|
||||
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
try {
|
||||
const bp = app.get("basePath") || '/';
|
||||
if (req.url && req.url.indexOf(bp) === 0) {
|
||||
@ -37,16 +38,17 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
const ws = require('express-ws')(app, server);
|
||||
import expressWs from 'express-ws';
|
||||
expressWs(app, server);
|
||||
|
||||
require("./console-line.js"); /* Monkey-patch console.log with line numbers */
|
||||
|
||||
const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/",
|
||||
serverConfig = config.get("server");
|
||||
const frontendPath = (config.get("frontendPath") as string).replace(/\/$/, "") + "/",
|
||||
serverConfig = config.get("server") as any;
|
||||
|
||||
console.log("Hosting server from: " + basePath);
|
||||
|
||||
let userDB, gameDB;
|
||||
let userDB: any, gameDB: any;
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
@ -86,7 +88,7 @@ const users = require("./routes/users");
|
||||
app.use(basePath + "api/v1/users", users.router);
|
||||
*/
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
app.use(function(err: any, req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
console.error(err.message);
|
||||
res.status(err.status || 500).json({
|
||||
message: err.message,
|
||||
@ -126,7 +128,7 @@ require("./db/games").then(function(db) {
|
||||
process.exit(-1);
|
||||
});
|
||||
|
||||
server.on("error", function(error) {
|
||||
server.on("error", function(error: any) {
|
||||
if (error.syscall !== "listen") {
|
||||
throw error;
|
||||
}
|
||||
@ -145,3 +147,5 @@ server.on("error", function(error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
export { app, server };
|
@ -1,5 +1,6 @@
|
||||
const fs = require('fs');
|
||||
let basePathRaw = process.env.VITE_BASEPATH || '';
|
||||
import fs from 'fs';
|
||||
|
||||
let basePathRaw = process.env['VITE_BASEPATH'] || '';
|
||||
|
||||
// If env not provided, try to detect a <base href="..."> in the
|
||||
// built client's index.html (if present). This helps when the
|
||||
@ -29,4 +30,4 @@ if (basePath === '//') basePath = '/';
|
||||
|
||||
console.log(`Using basepath ${basePath}`);
|
||||
|
||||
module.exports = basePath;
|
||||
export default basePath;
|
@ -1,30 +0,0 @@
|
||||
/* monkey-patch console.log to prefix with file/line-number */
|
||||
if (process.env.LOG_LINE) {
|
||||
let cwd = process.cwd(),
|
||||
cwdRe = new RegExp("^[^/]*" + cwd.replace("/", "\\/") + "\/([^:]*:[0-9]*).*$");
|
||||
[ "log", "warn", "error" ].forEach(function(method) {
|
||||
console[method] = (function () {
|
||||
let orig = console[method];
|
||||
return function () {
|
||||
function getErrorObject() {
|
||||
try {
|
||||
throw Error('');
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
let err = getErrorObject(),
|
||||
caller_line = err.stack.split("\n")[3],
|
||||
args = [caller_line.replace(cwdRe, "$1 -")];
|
||||
|
||||
/* arguments.unshift() doesn't exist... */
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
|
||||
orig.apply(this, args);
|
||||
};
|
||||
})();
|
||||
});
|
||||
}
|
28
server/console-line.ts
Executable file
28
server/console-line.ts
Executable file
@ -0,0 +1,28 @@
|
||||
/* monkey-patch console.log to prefix with file/line-number */
|
||||
if (process.env['LOG_LINE']) {
|
||||
let cwd = process.cwd(),
|
||||
cwdRe = new RegExp("^[^/]*" + cwd.replace("/", "\\/") + "\/([^:]*:[0-9]*).*$");
|
||||
[ "log", "warn", "error" ].forEach(function(method: string) {
|
||||
(console as any)[method] = (function () {
|
||||
let orig = (console as any)[method];
|
||||
return function (this: any, ...args: any[]) {
|
||||
function getErrorObject(): Error {
|
||||
try {
|
||||
throw Error('');
|
||||
} catch (err) {
|
||||
return err as Error;
|
||||
}
|
||||
}
|
||||
|
||||
let err = getErrorObject(),
|
||||
caller_line = err.stack?.split("\n")[3] || '',
|
||||
prefixedArgs = [caller_line.replace(cwdRe, "$1 -")];
|
||||
|
||||
/* arguments.unshift() doesn't exist... */
|
||||
prefixedArgs.push(...args);
|
||||
|
||||
orig.apply(this, prefixedArgs);
|
||||
};
|
||||
})();
|
||||
});
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs'),
|
||||
path = require('path'),
|
||||
Sequelize = require('sequelize'),
|
||||
config = require('config');
|
||||
|
||||
function init() {
|
||||
const db = {
|
||||
sequelize: new Sequelize(config.get("db.games")),
|
||||
Sequelize: Sequelize
|
||||
};
|
||||
|
||||
return db.sequelize.authenticate().then(function () {
|
||||
const Game = db.sequelize.define('game', {
|
||||
id: {
|
||||
type: Sequelize.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
path: Sequelize.STRING,
|
||||
name: Sequelize.STRING,
|
||||
}, {
|
||||
timestamps: false,
|
||||
classMethods: {
|
||||
associate: function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return db.sequelize.sync({
|
||||
force: false
|
||||
}).then(function () {
|
||||
return db;
|
||||
});
|
||||
}).catch(function (error) {
|
||||
console.log("ERROR: Failed to authenticate with GAMES DB");
|
||||
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
|
||||
console.log(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = init();
|
@ -1,70 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const Sequelize = require('sequelize'),
|
||||
config = require('config');
|
||||
|
||||
function init() {
|
||||
const db = {
|
||||
sequelize: new Sequelize(config.get("db.users")),
|
||||
Sequelize: Sequelize
|
||||
};
|
||||
|
||||
return db.sequelize.authenticate().then(function () {
|
||||
const User = db.sequelize.define('users', {
|
||||
id: {
|
||||
type: Sequelize.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
displayName: Sequelize.STRING,
|
||||
notes: Sequelize.STRING,
|
||||
uid: Sequelize.STRING,
|
||||
authToken: Sequelize.STRING,
|
||||
authDate: Sequelize.DATE,
|
||||
authenticated: Sequelize.BOOLEAN,
|
||||
mailVerified: Sequelize.BOOLEAN,
|
||||
mail: Sequelize.STRING,
|
||||
memberSince: Sequelize.DATE,
|
||||
password: Sequelize.STRING, /* SHA hash of user supplied password */
|
||||
passwordExpires: Sequelize.DATE
|
||||
}, {
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
const Authentication = db.sequelize.define('authentication', {
|
||||
key: {
|
||||
type: Sequelize.STRING,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
issued: Sequelize.DATE,
|
||||
type: {
|
||||
type: Sequelize.ENUM,
|
||||
values: [ 'account-setup', 'password-reset' ]
|
||||
},
|
||||
userId: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: User,
|
||||
key: 'id',
|
||||
}
|
||||
}
|
||||
}, {
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
return db.sequelize.sync({
|
||||
force: false
|
||||
}).then(function () {
|
||||
return db;
|
||||
});
|
||||
}).catch(function (error) {
|
||||
console.log("ERROR: Failed to authenticate with USER DB");
|
||||
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
|
||||
console.log(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = init();
|
@ -1,8 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
const config = require("config"),
|
||||
crypto = require("crypto"),
|
||||
hb = require("handlebars");
|
||||
import config from "config";
|
||||
import crypto from "crypto";
|
||||
import hb from "handlebars";
|
||||
|
||||
const templates = {
|
||||
"verify": {
|
||||
@ -52,13 +52,13 @@ const templates = {
|
||||
}
|
||||
};
|
||||
|
||||
const sendVerifyMail = function(userDB, req, user) {
|
||||
const sendVerifyMail = function(userDB: any, req: any, user: any): any {
|
||||
return userDB.sequelize.query("DELETE FROM authentications WHERE userId=:id AND type='account-setup'", {
|
||||
replacements: {
|
||||
id: user.id
|
||||
}
|
||||
}).then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
return new Promise<string>(function(resolve, reject) {
|
||||
crypto.randomBytes(16, function(error, buffer) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
@ -66,7 +66,7 @@ const sendVerifyMail = function(userDB, req, user) {
|
||||
return resolve(buffer.toString('hex'));
|
||||
});
|
||||
});
|
||||
}).then(function(secret) {
|
||||
}).then(function(secret: string) {
|
||||
return userDB.sequelize.query(
|
||||
"INSERT INTO authentications " +
|
||||
"(userId,issued,key,type) " +
|
||||
@ -77,11 +77,11 @@ const sendVerifyMail = function(userDB, req, user) {
|
||||
}
|
||||
}).then(function() {
|
||||
return secret;
|
||||
}).catch(function(error) {
|
||||
}).catch(function(error: any) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
});
|
||||
}).then(function(secret) {
|
||||
}).then(function(secret: string) {
|
||||
const transporter = req.app.get("transporter");
|
||||
if (!transporter) {
|
||||
console.log("Not sending VERIFY email; SMTP not configured.");
|
||||
@ -102,36 +102,37 @@ const sendVerifyMail = function(userDB, req, user) {
|
||||
text: hb.compile(templates.verify.text)(data),
|
||||
html: hb.compile(templates.verify.html)(data)
|
||||
};
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise<void>(function (resolve, reject) {
|
||||
let attempts = 10;
|
||||
|
||||
function send(envelope) {
|
||||
/* Rate limit to ten per second */
|
||||
transporter.sendMail(envelope, function (error, info) {
|
||||
if (!error) {
|
||||
console.log('Message sent: ' + info.response);
|
||||
return resolve();
|
||||
}
|
||||
function send(envelope: any) {
|
||||
/* Rate limit to ten per second */
|
||||
transporter.sendMail(envelope, function (error: any, info: any) {
|
||||
if (!error) {
|
||||
console.log('Message sent: ' + (info && info.response));
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attempts == 0) {
|
||||
console.log("Error sending email: ", error);
|
||||
return reject(error);
|
||||
}
|
||||
if (attempts == 0) {
|
||||
console.log("Error sending email: ", error);
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
attempts--;
|
||||
console.log("Unable to send mail. Trying again in 100ms (" + attempts + " attempts remain): ", error);
|
||||
setTimeout(send.bind(undefined, envelope), 100);
|
||||
});
|
||||
}
|
||||
attempts--;
|
||||
console.log("Unable to send mail. Trying again in 100ms (" + attempts + " attempts remain): ", error);
|
||||
setTimeout(send.bind(undefined, envelope), 100);
|
||||
});
|
||||
}
|
||||
|
||||
send(envelope);
|
||||
});
|
||||
}).catch(function(error) {
|
||||
}).catch(function(error: any) {
|
||||
console.log("Error creating account: ", error);
|
||||
});
|
||||
};
|
||||
|
||||
const sendPasswordChangedMail = function(userDB, req, user) {
|
||||
const sendPasswordChangedMail = function(_userDB: any, req: any, user: any): any {
|
||||
const transporter = req.app.get("transporter");
|
||||
if (!transporter) {
|
||||
console.log("Not sending VERIFY email; SMTP not configured.");
|
||||
@ -151,14 +152,14 @@ const sendPasswordChangedMail = function(userDB, req, user) {
|
||||
text: hb.compile(templates.password.text)(data),
|
||||
html: hb.compile(templates.password.html)(data)
|
||||
};
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise<void>(function (resolve, reject) {
|
||||
let attempts = 10;
|
||||
|
||||
function send(envelope) {
|
||||
function send(envelope: any) {
|
||||
/* Rate limit to ten per second */
|
||||
transporter.sendMail(envelope, function (error, info) {
|
||||
transporter.sendMail(envelope, function (error: any, info: any) {
|
||||
if (!error) {
|
||||
console.log('Message sent: ' + info.response);
|
||||
console.log('Message sent: ' + (info && info.response));
|
||||
return resolve();
|
||||
}
|
||||
|
||||
@ -177,7 +178,7 @@ const sendPasswordChangedMail = function(userDB, req, user) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
sendVerifyMail,
|
||||
sendPasswordChangedMail
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const createTransport = require('nodemailer').createTransport,
|
||||
{ timestamp } = require("./timestamp");
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { timestamp } from "./timestamp";
|
||||
|
||||
const transporter = createTransport({
|
||||
host: 'email.ketrenos.com',
|
||||
@ -9,8 +9,8 @@ const transporter = createTransport({
|
||||
port: 25
|
||||
});
|
||||
|
||||
function sendMail(to, subject, message, cc) {
|
||||
let envelope = {
|
||||
function sendMail(to: string, subject: string, message: string, cc?: string): Promise<boolean> {
|
||||
let envelope: any = {
|
||||
subject: subject,
|
||||
from: 'Ketr.Ketran <james_ketran@ketrenos.com>',
|
||||
to: to || '',
|
||||
@ -29,33 +29,29 @@ function sendMail(to, subject, message, cc) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let attempts = 10;
|
||||
|
||||
function attemptSend(envelope) {
|
||||
function attemptSend(envelope: any) {
|
||||
/* Rate limit to ten per second */
|
||||
transporter.sendMail(envelope, function (error, info) {
|
||||
transporter.sendMail(envelope, function (error, _info) {
|
||||
if (error) {
|
||||
if (attempts) {
|
||||
attempts--;
|
||||
console.warn(timestamp() + " Unable to send mail. Trying again in 100ms (" + attempts + " attempts remain): ", error);
|
||||
setTimeout(send.bind(undefined, envelope), 100);
|
||||
setTimeout(() => attemptSend(envelope), 100);
|
||||
} else {
|
||||
console.error(timestamp() + " Error sending email: ", error)
|
||||
return reject(error);
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
console.log(timestamp() + " Mail sent to: " + envelope.to);
|
||||
resolve(true);
|
||||
}
|
||||
|
||||
console.log(timestamp() + " Mail sent to: " + envelope.to);
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
attemptSend(envelope);
|
||||
}).then(function(success) {
|
||||
if (!success) {
|
||||
console.error(timestamp() + " Mail not sent to: " + envelope.to);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendMail: sendMail
|
||||
export {
|
||||
sendMail
|
||||
};
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "peddlers-of-ketran-server",
|
||||
"version": "1.0.0",
|
||||
"main": "app.js",
|
||||
"main": "dist/src/app.js",
|
||||
"scripts": {
|
||||
"start": "export $(cat ../.env | xargs) && node dist/app.js",
|
||||
"start": "export $(cat ../.env | xargs) && node dist/src/app.js",
|
||||
"start:legacy": "export $(cat ../.env | xargs) && node app.js",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"start:dev": "ts-node-dev --respawn --transpile-only src/app.ts",
|
||||
@ -36,9 +36,25 @@
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bluebird": "^3.5.38",
|
||||
"@types/config": "^3.3.1",
|
||||
"@types/connect-sqlite3": "^0.9.3",
|
||||
"@types/cookie-parser": "^1.4.4",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express-session": "^1.17.7",
|
||||
"@types/express-ws": "^3.0.1",
|
||||
"@types/handlebars": "^4.1.0",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/morgan": "^1.9.5",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"@types/node-gzip": "^1.1.0",
|
||||
"@types/nodemailer": "^6.4.8",
|
||||
"@types/random-words": "^1.1.0",
|
||||
"@types/sequelize": "^4.28.15",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@types/ws": "^8.5.5",
|
||||
"jest": "^29.7.0",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-jest": "^29.1.0",
|
||||
|
@ -1,8 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const express = require("express"),
|
||||
fs = require("fs"),
|
||||
url = require("url");
|
||||
import express from 'express';
|
||||
import fs from 'fs';
|
||||
import url from 'url';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -10,9 +8,9 @@ const router = express.Router();
|
||||
* to replace BASEPATH */
|
||||
router.get("/*", (req, res, next) => {
|
||||
const parts = url.parse(req.url),
|
||||
basePath = req.app.get("basePath");
|
||||
basePath = req.app.get("basePath") as string;
|
||||
|
||||
if (!/^\/[^/]+\.html$/.exec(parts.pathname)) {
|
||||
if (!/^\/[^/]+\.html$/.exec(parts.pathname || '')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -20,14 +18,13 @@ router.get("/*", (req, res, next) => {
|
||||
|
||||
/* Replace <script>'<base href="/BASEPATH/">';</script> in index.html with
|
||||
* the basePath */
|
||||
fs.readFile("frontend" + parts.pathname, "utf8", function(error, content) {
|
||||
fs.readFile("frontend" + (parts.pathname || ''), "utf8", function(error, content) {
|
||||
if (error) {
|
||||
return next();
|
||||
}
|
||||
res.send(content.replace(
|
||||
res.send((content as string).replace(
|
||||
/<script>'<base href="BASEPATH">';<\/script>/,
|
||||
"<base href='" + basePath + "'>"));
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
|
||||
/*
|
||||
@ -9,7 +9,7 @@ const router = express.Router();
|
||||
* by the server. It is mounted under the application's basePath so you can
|
||||
* hit: /<basePath>/__debug/request
|
||||
*/
|
||||
router.get('/__debug/request', (req, res) => {
|
||||
router.get('/__debug/request', (req: express.Request, res: express.Response) => {
|
||||
try {
|
||||
console.log('[debug] __debug/request hit:', req.method, req.originalUrl);
|
||||
// Echo back a compact JSON summary so curl or browsers can inspect it.
|
||||
@ -21,10 +21,10 @@ router.get('/__debug/request', (req, res) => {
|
||||
hostname: req.hostname,
|
||||
basePath: req.app && req.app.get && req.app.get('basePath')
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
console.error('[debug] error in __debug/request', e && e.stack || e);
|
||||
res.status(500).json({ error: 'debug endpoint error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
@ -1,24 +1,31 @@
|
||||
"use strict";
|
||||
// @ts-nocheck
|
||||
import express from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { readFile, writeFile, mkdir } from 'fs/promises';
|
||||
import fs from 'fs';
|
||||
import randomWords from 'random-words';
|
||||
import equal from 'fast-deep-equal';
|
||||
import { layout, staticData } from '../util/layout.js';
|
||||
import basePath from '../basepath';
|
||||
|
||||
const express = require("express"),
|
||||
router = express.Router(),
|
||||
crypto = require("crypto")
|
||||
const { readFile, writeFile, mkdir } = require("fs").promises,
|
||||
fs = require("fs"),
|
||||
accessSync = fs.accessSync,
|
||||
randomWords = require("random-words"),
|
||||
equal = require("fast-deep-equal");
|
||||
const { layout, staticData } = require('../util/layout.js');
|
||||
const basePath = require('../basepath');
|
||||
import { getValidRoads, getValidCorners, isRuleEnabled } from '../util/validLocations.js';
|
||||
|
||||
const { getValidRoads, getValidCorners, isRuleEnabled } = require('../util/validLocations.js');
|
||||
interface Player {
|
||||
order: number;
|
||||
orderRoll?: number;
|
||||
position?: string;
|
||||
orderStatus?: string;
|
||||
tied?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const MAX_SETTLEMENTS = 5;
|
||||
const MAX_CITIES = 4;
|
||||
const MAX_ROADS = 15;
|
||||
|
||||
const types = [ 'wheat', 'stone', 'sheep', 'wood', 'brick' ];
|
||||
const types: string[] = [ 'wheat', 'stone', 'sheep', 'wood', 'brick' ];
|
||||
|
||||
const debug = {
|
||||
audio: false,
|
||||
@ -32,9 +39,9 @@ const debug = {
|
||||
// others used a flatter shape. This helper accepts either a string or an
|
||||
// already-parsed object and returns a stable object so handlers don't need
|
||||
// to defensively check multiple nested locations.
|
||||
function normalizeIncoming(msg) {
|
||||
function normalizeIncoming(msg: unknown): { type: string | null, data: unknown } {
|
||||
if (!msg) return { type: null, data: null };
|
||||
let parsed = null;
|
||||
let parsed: unknown = null;
|
||||
try {
|
||||
if (typeof msg === 'string') {
|
||||
parsed = JSON.parse(msg);
|
||||
@ -46,7 +53,7 @@ function normalizeIncoming(msg) {
|
||||
return { type: null, data: null };
|
||||
}
|
||||
if (!parsed) return { type: null, data: null };
|
||||
const type = parsed.type || parsed.action || null;
|
||||
const type = (parsed as any).type || (parsed as any).action || null;
|
||||
// Prefer parsed.data when present, but allow flattened payloads where
|
||||
// properties like `name` live at the root.
|
||||
const data = parsed.data || (Object.keys(parsed).length ? Object.assign({}, parsed) : null);
|
||||
@ -82,14 +89,14 @@ function shuffleArray(array) {
|
||||
const games = {};
|
||||
const audio = {};
|
||||
|
||||
const processTies = (players) => {
|
||||
const processTies = (players: Player[]) => {
|
||||
|
||||
/* Sort the players into buckets based on their
|
||||
* order, and their current roll. If a resulting
|
||||
* roll array has more than one element, then there
|
||||
* is a tie that must be resolved */
|
||||
let slots = [];
|
||||
players.forEach(player => {
|
||||
let slots: Player[][] = [];
|
||||
players.forEach((player: Player) => {
|
||||
if (!slots[player.order]) {
|
||||
slots[player.order] = [];
|
||||
}
|
||||
@ -98,13 +105,13 @@ const processTies = (players) => {
|
||||
|
||||
let ties = false, position = 1;
|
||||
|
||||
const irstify = (position) => {
|
||||
const irstify = (position: number): string => {
|
||||
switch (position) {
|
||||
case 1: return `1st`;
|
||||
case 2: return `2nd`;
|
||||
case 3: return `3rd`;
|
||||
case 4: return `4th`;
|
||||
default: return position;
|
||||
default: return position.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +119,7 @@ const processTies = (players) => {
|
||||
slots.reverse().forEach((slot) => {
|
||||
if (slot.length !== 1) {
|
||||
ties = true;
|
||||
slot.forEach(player => {
|
||||
slot.forEach((player: Player) => {
|
||||
player.orderRoll = 0; /* Ties have to be re-rolled */
|
||||
player.position = irstify(position);
|
||||
player.orderStatus = `Tied for ${irstify(position)}`;
|
||||
@ -5216,4 +5223,4 @@ router.post("/:id?", async (req, res/*, next*/) => {
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
@ -1,10 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const express = require("express"),
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
config = require("config"),
|
||||
basePath = require("../basepath");
|
||||
import express from "express";
|
||||
import fs from "fs";
|
||||
import url from "url";
|
||||
import config from "config";
|
||||
import basePath from "../basepath";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -31,24 +31,24 @@ const extensionMatch = new RegExp("^.*?(" + extensions.join("|") + ")$", "i");
|
||||
* If so, 404 because the asset isn't there. otherwise assume it is a
|
||||
* dynamic client side route and *then* return index.html.
|
||||
*/
|
||||
router.get("/*", function(req, res, next) {
|
||||
router.get("/*", function(req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const parts = url.parse(req.url);
|
||||
|
||||
/* If req.user isn't set yet (authentication hasn't happened) then
|
||||
* only allow / to be loaded--everything else chains to the next
|
||||
* handler */
|
||||
if (!req.user &&
|
||||
if (!(req as any).user &&
|
||||
req.url != "/" &&
|
||||
req.url.indexOf("/games") != 0) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.url == "/" || req.url.indexOf("/games") == 0 || !extensionMatch.exec(parts.pathname)) {
|
||||
if (req.url == "/" || req.url.indexOf("/games") == 0 || !extensionMatch.exec(parts.pathname || '')) {
|
||||
console.log("Returning index for " + req.url);
|
||||
|
||||
/* Replace <script>'<base href="BASEPATH">';</script> in index.html with
|
||||
* the basePath */
|
||||
const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/",
|
||||
const frontendPath = (config.get("frontendPath") as string).replace(/\/$/, "") + "/",
|
||||
index = fs.readFileSync(frontendPath + "index.html", "utf8");
|
||||
res.send(index.replace(
|
||||
/<script>'<base href="BASEPATH">';<\/script>/,
|
||||
@ -63,4 +63,4 @@ router.get("/*", function(req, res, next) {
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
@ -1,19 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
const express = require("express"),
|
||||
config = require("config"),
|
||||
{ sendVerifyMail, sendPasswordChangedMail } = require("../lib/mail"),
|
||||
crypto = require("crypto");
|
||||
import express from "express";
|
||||
import config from "config";
|
||||
import { sendVerifyMail, sendPasswordChangedMail } from "../lib/mail";
|
||||
import crypto from "crypto";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
let userDB;
|
||||
let userDB: any;
|
||||
|
||||
require("../db/users.js").then(function(db) {
|
||||
import("../db/users.js").then(function(db: any) {
|
||||
userDB = db;
|
||||
});
|
||||
|
||||
router.get("/", function(req, res/*, next*/) {
|
||||
router.get("/", function(req: express.Request, res: express.Response/*, next*/) {
|
||||
console.log("/users/");
|
||||
return getSessionUser(req).then((user) => {
|
||||
return res.status(200).send(user);
|
||||
@ -23,12 +23,12 @@ router.get("/", function(req, res/*, next*/) {
|
||||
});
|
||||
});
|
||||
|
||||
router.put("/password", function(req, res) {
|
||||
router.put("/password", function(req: express.Request, res: express.Response) {
|
||||
console.log("/users/password");
|
||||
|
||||
const q = req.query as any;
|
||||
const changes = {
|
||||
currentPassword: req.query.c || req.body.c,
|
||||
newPassword: req.query.n || req.body.n
|
||||
currentPassword: q.c || req.body.c,
|
||||
newPassword: q.n || req.body.n
|
||||
};
|
||||
|
||||
if (!changes.currentPassword || !changes.newPassword) {
|
||||
@ -39,7 +39,7 @@ router.put("/password", function(req, res) {
|
||||
return res.status(400).send("Attempt to set new password to current password.");
|
||||
}
|
||||
|
||||
return getSessionUser(req).then(function(user) {
|
||||
return getSessionUser(req).then(function(user: any) {
|
||||
return userDB.sequelize.query("SELECT id FROM users " +
|
||||
"WHERE uid=:username AND password=:password", {
|
||||
replacements: {
|
||||
@ -48,13 +48,13 @@ router.put("/password", function(req, res) {
|
||||
},
|
||||
type: userDB.Sequelize.QueryTypes.SELECT,
|
||||
raw: true
|
||||
}).then(function(users) {
|
||||
}).then(function(users: any) {
|
||||
if (users.length != 1) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}).then(function(user) {
|
||||
}).then(function(user: any) {
|
||||
if (!user) {
|
||||
console.log("Invalid password");
|
||||
/* Invalid password */
|
||||
@ -77,15 +77,15 @@ router.put("/password", function(req, res) {
|
||||
});
|
||||
});
|
||||
|
||||
router.post("/create", function(req, res) {
|
||||
router.post("/create", function(req: express.Request, res: express.Response) {
|
||||
console.log("/users/create");
|
||||
|
||||
const q = req.query as any;
|
||||
const user = {
|
||||
uid: req.query.m || req.body.m,
|
||||
displayName: req.query.n || req.body.n || "",
|
||||
password: req.query.p || req.body.p || "",
|
||||
mail: req.query.m || req.body.m,
|
||||
notes: req.query.w || req.body.w || ""
|
||||
uid: q.m || req.body.m,
|
||||
displayName: q.n || req.body.n || "",
|
||||
password: q.p || req.body.p || "",
|
||||
mail: q.m || req.body.m,
|
||||
notes: q.w || req.body.w || ""
|
||||
};
|
||||
|
||||
if (!user.uid || !user.password || !user.displayName || !user.notes) {
|
||||
@ -98,37 +98,38 @@ router.post("/create", function(req, res) {
|
||||
replacements: user,
|
||||
type: userDB.Sequelize.QueryTypes.SELECT,
|
||||
raw: true
|
||||
}).then(function(results) {
|
||||
}).then(function(results: any) {
|
||||
if (results.length != 0) {
|
||||
return res.status(400).send("Email address already used.");
|
||||
}
|
||||
|
||||
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
let re = /^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if (!re.exec(user.mail)) {
|
||||
console.log("Invalid email address: " + user.mail);
|
||||
throw "Invalid email address.";
|
||||
}
|
||||
return;
|
||||
}).then(function() {
|
||||
return userDB.sequelize.query("INSERT INTO users " +
|
||||
"(uid,displayName,password,mail,memberSince,authenticated,notes) " +
|
||||
"VALUES(:uid,:displayName,:password,:mail,CURRENT_TIMESTAMP,0,:notes)", {
|
||||
replacements: user
|
||||
}).spread(function(results, metadata) {
|
||||
}).spread(function(_results: any, metadata: any) {
|
||||
req.session.userId = metadata.lastID;
|
||||
}).then(function() {
|
||||
return getSessionUser(req).then(function(user) {
|
||||
return getSessionUser(req).then(function(user: any) {
|
||||
res.status(200).send(user);
|
||||
user.id = req.session.userId;
|
||||
return sendVerifyMail(userDB, req, user);
|
||||
});
|
||||
}).catch(function(error) {
|
||||
}).catch(function(error: any) {
|
||||
console.log("Error creating account: ", error);
|
||||
return res.status(401).send(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const getSessionUser = function(req) {
|
||||
const getSessionUser = function(req: express.Request): Promise<any> {
|
||||
return Promise.resolve().then(function() {
|
||||
if (!req.session || !req.session.userId) {
|
||||
throw "Unauthorized. You must be logged in.";
|
||||
@ -143,7 +144,7 @@ const getSessionUser = function(req) {
|
||||
},
|
||||
type: userDB.Sequelize.QueryTypes.SELECT,
|
||||
raw: true
|
||||
}).then(function(results) {
|
||||
}).then(function(results: any) {
|
||||
if (results.length != 1) {
|
||||
throw "Invalid account.";
|
||||
}
|
||||
@ -160,9 +161,9 @@ const getSessionUser = function(req) {
|
||||
return user;
|
||||
}
|
||||
|
||||
return user;
|
||||
return user;
|
||||
});
|
||||
}).then(function(user) {
|
||||
}).then(function(user: any) {
|
||||
req.user = user;
|
||||
|
||||
/* If the user already has a restriction, or there are no album user restrictions,
|
||||
@ -171,7 +172,7 @@ const getSessionUser = function(req) {
|
||||
return user;
|
||||
}
|
||||
|
||||
let allowed = config.get("restrictions");
|
||||
let allowed: any = config.get("restrictions");
|
||||
if (!Array.isArray(allowed)) {
|
||||
allowed = [ allowed ];
|
||||
}
|
||||
@ -187,8 +188,8 @@ const getSessionUser = function(req) {
|
||||
}).then(function(user) {
|
||||
/* If there are maintainers on this album, check if this user is a maintainer */
|
||||
if (config.has("maintainers")) {
|
||||
let maintainers = config.get("maintainers");
|
||||
if (maintainers.indexOf(user.username) != -1) {
|
||||
let maintainers: any = config.get("maintainers");
|
||||
if (Array.isArray(maintainers) && maintainers.indexOf(user.username) != -1) {
|
||||
user.maintainer = true;
|
||||
if (user.restriction) {
|
||||
console.warn("User " + user.username + " is a maintainer AND has a restriction which will be ignored: " + user.restriction);
|
||||
@ -212,11 +213,12 @@ const getSessionUser = function(req) {
|
||||
});
|
||||
}
|
||||
|
||||
router.post("/login", function(req, res) {
|
||||
router.post("/login", function(req: express.Request, res: express.Response) {
|
||||
console.log("/users/login");
|
||||
const q = req.query as any;
|
||||
|
||||
let username = req.query.u || req.body.u || "",
|
||||
password = req.query.p || req.body.p || "";
|
||||
let username = q.u || req.body.u || "",
|
||||
password = q.p || req.body.p || "";
|
||||
|
||||
console.log("Login attempt");
|
||||
|
||||
@ -224,7 +226,7 @@ router.post("/login", function(req, res) {
|
||||
return res.status(400).send("Missing username and/or password");
|
||||
}
|
||||
|
||||
return new Promise((reject, resolve) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
console.log("Looking up user in DB.");
|
||||
let query = "SELECT " +
|
||||
"id,mailVerified,authenticated,uid AS username,displayName AS name,mail " +
|
||||
@ -235,7 +237,7 @@ router.post("/login", function(req, res) {
|
||||
password: crypto.createHash('sha256').update(password).digest('base64')
|
||||
},
|
||||
type: userDB.Sequelize.QueryTypes.SELECT
|
||||
}).then(function(users) {
|
||||
}).then(function(users: any) {
|
||||
if (users.length != 1) {
|
||||
return resolve(null);
|
||||
}
|
||||
@ -243,7 +245,7 @@ router.post("/login", function(req, res) {
|
||||
req.session.userId = user.id;
|
||||
return resolve(user);
|
||||
});
|
||||
}).then(function(user) {
|
||||
}).then(function(user: any) {
|
||||
if (!user) {
|
||||
console.log(username + " not found (or invalid password.)");
|
||||
req.session.userId = null;
|
||||
@ -259,7 +261,7 @@ router.post("/login", function(req, res) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
return getSessionUser(req).then(function(user) {
|
||||
return getSessionUser(req).then(function(user: any) {
|
||||
return res.status(200).send(user);
|
||||
});
|
||||
}).catch(function(error) {
|
||||
@ -277,7 +279,7 @@ router.get("/logout", function(req, res) {
|
||||
res.status(200).send({});
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
router,
|
||||
getSessionUser
|
||||
};
|
@ -1,42 +1,45 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import type { Request, Response, NextFunction } from 'express';
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import config from 'config';
|
||||
import basePath from '../basepath';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import http from 'http';
|
||||
import expressWs from 'express-ws';
|
||||
|
||||
process.env.TZ = "Etc/GMT";
|
||||
|
||||
console.log("Loading ketr.ketran");
|
||||
|
||||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
const config = require("config");
|
||||
const session = require('express-session');
|
||||
const basePath = require("../basepath");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const fs = require('fs');
|
||||
|
||||
const app = express();
|
||||
const server = require("http").createServer(app);
|
||||
const server = http.createServer(app);
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
const ws = require('express-ws')(app, server);
|
||||
expressWs(app, server);
|
||||
|
||||
require("../console-line.js"); /* Monkey-patch console.log with line numbers */
|
||||
|
||||
// Temporary debug routes (dev-only). Mount before static so we can
|
||||
// inspect what the server receives for base-prefixed requests.
|
||||
try {
|
||||
app.use(basePath, require("../routes/debug.js"));
|
||||
} catch (e) {
|
||||
// import the debug router using ESM style; fallback to require at runtime if needed
|
||||
// (some dev environments may still emit JS commonjs files)
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const debugRouter = require("../routes/debug.js").default || require("../routes/debug.js");
|
||||
app.use(basePath, debugRouter);
|
||||
} catch (e: any) {
|
||||
console.error('Failed to mount debug routes (src):', e && e.stack || e);
|
||||
}
|
||||
|
||||
const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/",
|
||||
serverConfig = config.get("server");
|
||||
const frontendPath = (config.get("frontendPath") as string).replace(/\/$/, "") + "/",
|
||||
serverConfig = config.get("server") as { port: number };
|
||||
|
||||
console.log("Hosting server from: " + basePath);
|
||||
|
||||
let userDB: any, gameDB: any;
|
||||
// DB handles are initialized by the modules below; we don't need file-scoped vars here.
|
||||
|
||||
app.use((req, res, next) => {
|
||||
app.use((req: Request, _res: Response, next: NextFunction) => {
|
||||
console.log(`${req.method} ${req.url}`);
|
||||
next();
|
||||
});
|
||||
@ -48,19 +51,27 @@ app.use(bodyParser.json());
|
||||
app.set("trust proxy", true);
|
||||
|
||||
app.set("basePath", basePath);
|
||||
app.use(basePath, require("../routes/basepath.js"));
|
||||
// basepath is a simple exported string
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const basepathRouter = require("../routes/basepath.js").default || require("../routes/basepath.js");
|
||||
app.use(basePath, basepathRouter);
|
||||
|
||||
/* Handle static files first so excessive logging doesn't occur */
|
||||
app.use(basePath, express.static(frontendPath, { index: false }));
|
||||
|
||||
const index = require("../routes/index");
|
||||
// index route (may be ESM default export)
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const index = require("../routes/index.js").default || require("../routes/index.js");
|
||||
|
||||
if (config.has("admin")) {
|
||||
const admin = config.get("admin");
|
||||
app.set("admin", admin);
|
||||
}
|
||||
|
||||
app.use(`${basePath}api/v1/games`, require("../routes/games"));
|
||||
// games router
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const gamesRouter = require("../routes/games.js").default || require("../routes/games.js");
|
||||
app.use(`${basePath}api/v1/games`, gamesRouter);
|
||||
|
||||
/* Allow loading of the app w/out being logged in */
|
||||
app.use(basePath, index);
|
||||
@ -82,11 +93,14 @@ process.on('SIGINT', () => {
|
||||
server.close(() => process.exit(1));
|
||||
});
|
||||
|
||||
require("../db/games").then(function(db: any) {
|
||||
gameDB = db;
|
||||
// database initializers
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("../db/games.js").then(function(_db: any) {
|
||||
// games DB initialized
|
||||
}).then(function() {
|
||||
return require("../db/users").then(function(db: any) {
|
||||
userDB = db;
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
return require("../db/users.js").then(function(_db: any) {
|
||||
// users DB initialized
|
||||
});
|
||||
}).then(function() {
|
||||
console.log("DB connected. Opening server.");
|
||||
@ -118,4 +132,4 @@ server.on("error", function(error: any) {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = { app, server };
|
||||
export { app, server };
|
||||
|
@ -1,16 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
function twoDigit(number) {
|
||||
function twoDigit(number: number): string {
|
||||
return ("0" + number).slice(-2);
|
||||
}
|
||||
|
||||
function timestamp(date) {
|
||||
function timestamp(date?: Date): string {
|
||||
date = date || new Date();
|
||||
return [ date.getFullYear(), twoDigit(date.getMonth() + 1), twoDigit(date.getDate()) ].join("-") +
|
||||
return [ date.getFullYear(), twoDigit(date.getMonth() + 1), twoDigit(date.getDate()) ].join("-") +
|
||||
" " +
|
||||
[ twoDigit(date.getHours()), twoDigit(date.getMinutes()), twoDigit(date.getSeconds()) ].join(":");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
timestamp
|
||||
};
|
@ -1,19 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"target": "ES2022",
|
||||
"module": "CommonJS",
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"rootDir": ".",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
"include": ["**/*.ts", "**/*.js"],
|
||||
"exclude": ["node_modules", "dist", "test-output"]
|
||||
}
|
||||
|
19
server/types/express-session.d.ts
vendored
Normal file
19
server/types/express-session.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
import 'express';
|
||||
import 'express-session';
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
// populated by getSessionUser
|
||||
user?: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'express-session' {
|
||||
interface SessionData {
|
||||
userId?: number | null;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
@ -62,7 +62,7 @@
|
||||
* |
|
||||
* 12 | 11 10
|
||||
*/
|
||||
const Tile = (corners, roads) => {
|
||||
const Tile = (corners: number[], roads: number[]) => {
|
||||
return {
|
||||
corners: corners, /* 6 */
|
||||
pip: -1,
|
||||
@ -74,7 +74,7 @@ const Tile = (corners, roads) => {
|
||||
/* Borders have three sections each, so they are numbered
|
||||
* 0-17 clockwise. Some corners share two borders. */
|
||||
|
||||
const Corner = (roads, banks) => {
|
||||
const Corner = (roads: number[], banks: number[]) => {
|
||||
return {
|
||||
roads: roads, /* max of 3 */
|
||||
banks: banks, /* max of 2 */
|
||||
@ -82,7 +82,7 @@ const Corner = (roads, banks) => {
|
||||
};
|
||||
};
|
||||
|
||||
const Road = (corners) => {
|
||||
const Road = (corners: number[]) => {
|
||||
return {
|
||||
corners: corners, /* 2 */
|
||||
data: undefined
|
||||
@ -314,7 +314,7 @@ const staticData = {
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
layout,
|
||||
staticData
|
||||
};
|
@ -1,34 +1,41 @@
|
||||
const { layout } = require('./layout.js');
|
||||
import { layout } from './layout.js';
|
||||
|
||||
const isRuleEnabled = (game, rule) => {
|
||||
const isRuleEnabled = (game: any, rule: string): boolean => {
|
||||
return rule in game.rules && game.rules[rule].enabled;
|
||||
};
|
||||
|
||||
const getValidRoads = (game, color) => {
|
||||
const limits = [];
|
||||
const getValidRoads = (game: any, color: string): number[] => {
|
||||
const limits: number[] = [];
|
||||
|
||||
/* For each road, if the road is set, skip it.
|
||||
* If no color is set, check the two corners. If the corner
|
||||
* has a matching color, add this to the set. Otherwise skip.
|
||||
*/
|
||||
layout.roads.forEach((road, roadIndex) => {
|
||||
if (game.placements.roads[roadIndex].color) {
|
||||
if (!game.placements || !game.placements.roads || game.placements.roads[roadIndex]?.color) {
|
||||
return;
|
||||
}
|
||||
let valid = false;
|
||||
for (let c = 0; !valid && c < road.corners.length; c++) {
|
||||
const corner = layout.corners[road.corners[c]],
|
||||
cornerColor = game.placements.corners[road.corners[c]].color;
|
||||
for (let c = 0; !valid && c < road.corners.length; c++) {
|
||||
const cornerIndex = road.corners[c] as number;
|
||||
if (cornerIndex == null || (layout as any).corners[cornerIndex] == null) {
|
||||
continue;
|
||||
}
|
||||
const corner = (layout as any).corners[cornerIndex];
|
||||
const cornerColor = (game as any).placements && (game as any).placements.corners && (game as any).placements.corners[cornerIndex] && (game as any).placements.corners[cornerIndex].color;
|
||||
/* Roads do not pass through other player's settlements */
|
||||
if (cornerColor && cornerColor !== color) {
|
||||
continue;
|
||||
}
|
||||
for (let r = 0; !valid && r < corner.roads.length; r++) {
|
||||
for (let r = 0; !valid && r < (corner.roads || []).length; r++) {
|
||||
/* This side of the corner is pointing to the road being validated. Skip it. */
|
||||
if (corner.roads[r] === roadIndex) {
|
||||
if (!corner.roads || corner.roads[r] === roadIndex) {
|
||||
continue;
|
||||
}
|
||||
if (game.placements.roads[corner.roads[r]].color === color) {
|
||||
const rr = corner.roads[r];
|
||||
if (rr == null) { continue; }
|
||||
const placementsRoads = (game as any).placements && (game as any).placements.roads;
|
||||
if (placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
@ -41,8 +48,8 @@ const getValidRoads = (game, color) => {
|
||||
return limits;
|
||||
}
|
||||
|
||||
const getValidCorners = (game, color, type) => {
|
||||
const limits = [];
|
||||
const getValidCorners = (game: any, color: string, type?: string): number[] => {
|
||||
const limits: number[] = [];
|
||||
|
||||
/* For each corner, if the corner already has a color set, skip it if type
|
||||
* isn't set. If type is set, if it is a match, and the color is a match,
|
||||
@ -77,14 +84,20 @@ const getValidCorners = (game, color, type) => {
|
||||
valid = true; /* Not filtering based on current player */
|
||||
} else {
|
||||
valid = false;
|
||||
for (let r = 0; !valid && r < corner.roads.length; r++) {
|
||||
valid = game.placements.roads[corner.roads[r]].color === color;
|
||||
for (let r = 0; !valid && r < (corner.roads || []).length; r++) {
|
||||
const rr = corner.roads[r];
|
||||
if (rr == null) { continue; }
|
||||
const placementsRoads = (game as any).placements && (game as any).placements.roads;
|
||||
valid = !!(placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color);
|
||||
}
|
||||
}
|
||||
|
||||
for (let r = 0; valid && r < corner.roads.length; r++) {
|
||||
const road = layout.roads[corner.roads[r]];
|
||||
for (let c = 0; valid && c < road.corners.length; c++) {
|
||||
for (let r = 0; valid && r < (corner.roads || []).length; r++) {
|
||||
if (!corner.roads) { break; }
|
||||
const ridx = corner.roads[r] as number;
|
||||
if (ridx == null || (layout as any).roads[ridx] == null) { continue; }
|
||||
const road = (layout as any).roads[ridx];
|
||||
for (let c = 0; valid && c < (road.corners || []).length; c++) {
|
||||
/* This side of the road is pointing to the corner being validated.
|
||||
* Skip it. */
|
||||
if (road.corners[c] === cornerIndex) {
|
||||
@ -92,7 +105,8 @@ const getValidCorners = (game, color, type) => {
|
||||
}
|
||||
/* There is a settlement within one segment from this
|
||||
* corner, so it is invalid for settlement placement */
|
||||
if (game.placements.corners[road.corners[c]].color) {
|
||||
const cc = road.corners[c] as number;
|
||||
if ((game as any).placements && (game as any).placements.corners && (game as any).placements.corners[cc] && (game as any).placements.corners[cc].color) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
@ -103,7 +117,7 @@ const getValidCorners = (game, color, type) => {
|
||||
* on the volcano) */
|
||||
if (!(game.state === 'initial-placement'
|
||||
&& isRuleEnabled(game, 'volcano')
|
||||
&& layout.tiles[game.robber].corners.indexOf(cornerIndex) !== -1
|
||||
&& (layout as any).tiles && (layout as any).tiles[(game as any).robber] && Array.isArray((layout as any).tiles[(game as any).robber].corners) && (layout as any).tiles[(game as any).robber].corners.indexOf(cornerIndex as number) !== -1
|
||||
)) {
|
||||
limits.push(cornerIndex);
|
||||
}
|
||||
@ -113,7 +127,7 @@ const getValidCorners = (game, color, type) => {
|
||||
return limits;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
getValidCorners,
|
||||
getValidRoads,
|
||||
isRuleEnabled
|
Loading…
x
Reference in New Issue
Block a user