diff --git a/server/ai/app.js b/server/ai/app.ts
similarity index 98%
rename from server/ai/app.js
rename to server/ai/app.ts
index 855fd35..81b5bc9 100644
--- a/server/ai/app.js
+++ b/server/ai/app.ts
@@ -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);
};
diff --git a/server/ai/longest-road.js b/server/ai/longest-road.ts
similarity index 85%
rename from server/ai/longest-road.js
rename to server/ai/longest-road.ts
index 317fc8c..434dbbf 100644
--- a/server/ai/longest-road.js
+++ b/server/ai/longest-road.ts
@@ -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;
\ No newline at end of file
+export default calculateRoadLengths;
\ No newline at end of file
diff --git a/server/app.js b/server/app.ts
similarity index 80%
rename from server/app.js
rename to server/app.ts
index 17c61f5..21099f5 100755
--- a/server/app.js
+++ b/server/app.ts
@@ -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 };
diff --git a/server/basepath.js b/server/basepath.ts
similarity index 91%
rename from server/basepath.js
rename to server/basepath.ts
index 207bf08..37f621b 100755
--- a/server/basepath.js
+++ b/server/basepath.ts
@@ -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 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;
diff --git a/server/console-line.js b/server/console-line.js
deleted file mode 100755
index a6f33b9..0000000
--- a/server/console-line.js
+++ /dev/null
@@ -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);
- };
- })();
- });
-}
diff --git a/server/console-line.ts b/server/console-line.ts
new file mode 100755
index 0000000..4edc689
--- /dev/null
+++ b/server/console-line.ts
@@ -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);
+ };
+ })();
+ });
+}
diff --git a/server/db/games.js b/server/db/games.js
deleted file mode 100755
index b487f54..0000000
--- a/server/db/games.js
+++ /dev/null
@@ -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();
diff --git a/server/db/users.js b/server/db/users.js
deleted file mode 100755
index 00e5dae..0000000
--- a/server/db/users.js
+++ /dev/null
@@ -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();
diff --git a/server/lib/mail.js b/server/lib/mail.ts
similarity index 73%
rename from server/lib/mail.js
rename to server/lib/mail.ts
index 3cc10c2..3ab6e8b 100644
--- a/server/lib/mail.js
+++ b/server/lib/mail.ts
@@ -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(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(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(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
}
diff --git a/server/mail.js b/server/mail.ts
similarity index 60%
rename from server/mail.js
rename to server/mail.ts
index 87f741e..28786fc 100755
--- a/server/mail.js
+++ b/server/mail.ts
@@ -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 {
+ let envelope: any = {
subject: subject,
from: 'Ketr.Ketran ',
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
};
diff --git a/server/package.json b/server/package.json
index 09f4585..f234a4a 100644
--- a/server/package.json
+++ b/server/package.json
@@ -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",
diff --git a/server/routes/basepath.js b/server/routes/basepath.ts
similarity index 61%
rename from server/routes/basepath.js
rename to server/routes/basepath.ts
index eba896d..504ec2c 100644
--- a/server/routes/basepath.js
+++ b/server/routes/basepath.ts
@@ -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 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(
/ 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(
/