Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .env.ci.example

This file was deleted.

9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ DB_PASS=root
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=secret

MAIL_FROM=no-reply@yourdomain.tld
MAIL_SUPPORT=support@yourdomain.tld

SMTP_HOST=smtp.yourdomain.tld
SMTP_PORT=465
SMTP_USERNAME=yourUsername
SMTP_PASSWORD=yourPassword

4 changes: 3 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const JSON_FILES = ["src/*.json", "src/**/*.json"];
const TEST_FILES = ["src/**/*.spec.ts", "src/**/*.test.ts"];
const CONFIG_FILES = ["src/config/**/*"];
const SCHEMA_FILES = ["src/schemas/**/*"];
const TEMPLATE_FILES = ["src/templates/**/*"];
const SOURCE_FILES = ["src/**/*.ts", "!" + TEST_FILES];

// pull in the project TypeScript config
Expand Down Expand Up @@ -55,8 +56,9 @@ gulp.task("doc", function() {
gulp.task("copy:config", () => gulp.src(CONFIG_FILES).pipe(gulp.dest("dist/config")));
gulp.task("copy:schema", () => gulp.src(SCHEMA_FILES).pipe(gulp.dest("dist/schemas")));
gulp.task("copy:json", () => gulp.src(JSON_FILES).pipe(gulp.dest("dist")));
gulp.task("copy:templates", () => gulp.src(TEMPLATE_FILES).pipe(gulp.dest("dist/templates")));

gulp.task("assets", gulp.series(["copy:config", "copy:schema", "copy:json"]));
gulp.task("assets", gulp.series(["copy:config", "copy:schema", "copy:json", "copy:templates"]));

gulp.task("build", gulp.series("assets", "compile"));
gulp.task("default", gulp.series("build", "watch"));
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"morgan": "^1.9.1",
"mysql2": "^1.6.5",
"node-pre-gyp": "^0.14.0",
"nodemailer": "^6.4.4",
"redis": "^2.8.0",
"safe-squel": "^5.12.4",
"ts-graylog": "^1.0.2",
Expand Down
12 changes: 12 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,18 @@ CREATE TABLE `techs` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `resetTokens`
--

CREATE TABLE `resetTokens` (
`email` varchar(64) NOT NULL,
`ipRequested` varchar(45) NOT NULL,
`resetToken` varchar(64) NOT NULL,
`requestedAt` int(10) NOT NULL,
`usedAt` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- -----------------------------------------------------
-- procedure getFreePosition
-- -----------------------------------------------------
Expand Down
5 changes: 4 additions & 1 deletion src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,13 @@ export default class App {
);

// if the user tries to authenticate, we don't have a token yet
// TODO: Replace with whitelist of urls, which don't need a token
if (
!request.originalUrl.toString().includes("/auth/") &&
!request.originalUrl.toString().includes("/users/create/") &&
!request.originalUrl.toString().includes("/config/")
!request.originalUrl.toString().includes("/config/") &&
!request.originalUrl.toString().includes("/user/forgot") &&
!request.originalUrl.toString().includes("/user/resetPassword")
) {
const authString = request.header("authorization");

Expand Down
18 changes: 18 additions & 0 deletions src/common/Encryption.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const SALT_WORK_FACTOR = 10;
const crypto = require("crypto");

let bcrypt;
try {
bcrypt = require("bcrypt");
Expand Down Expand Up @@ -26,4 +28,20 @@ export default class Encryption {
public static async compare(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}

/**
* Generates a random token
* @param byteLength the length of the generated token
*/
public static async generateToken(byteLength = 128): Promise<string> {
return new Promise((resolve, reject) => {
crypto.randomBytes(byteLength, (err, buffer) => {
if (err) {
reject(err);
} else {
resolve(buffer.toString("base64"));
}
});
});
}
}
38 changes: 38 additions & 0 deletions src/common/MailSender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import dotenv = require("dotenv");

const nodemailer = require("nodemailer");

dotenv.config();

/**
* This is a wrapper-class for node-mailer
*/
export default class MailSender {
/**
* Sends a mail to the given address
* @param recipient
* @param subjectText
* @param body
*/
public static async sendMail(recipient: string, subjectText: string, body: string): Promise<void> {
const mailOptions = {
from: process.env.MAIL_FROM,
to: recipient,
subject: subjectText,
replyTo: process.env.SUPPORT_MAIL,
html: body,
};

const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true, // use SSL
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
});

await transporter.sendMail(mailOptions);
}
}
8 changes: 8 additions & 0 deletions src/interfaces/IResetTokenService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import ResetToken from "../units/ResetToken";

export default interface IResetTokenService {
checkIfResetAlreadyRequested(email: string, lastValidTimestamp: number): Promise<boolean>;
getTokenFromMail(email: string): Promise<ResetToken>;
setTokenUsed(token: string, usedAt: number): Promise<void>;
storeResetToken(data: ResetToken): Promise<void>;
}
1 change: 1 addition & 0 deletions src/interfaces/IUserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default interface IUserService {
getUserById(userID: number): Promise<User>;
getUserForAuthentication(email: string): Promise<User>;
checkIfNameOrMailIsTaken(username: string, email: string);
getUserByMail(email: string): Promise<User>;
getNewId(): Promise<number>;
createNewUser(user: User, connection?);
updateUserData(user: User, connection?);
Expand Down
2 changes: 2 additions & 0 deletions src/ioc/createContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import PlanetService from "../services/PlanetService";
import ShipService from "../services/ShipService";
import TechService from "../services/TechService";
import UserService from "../services/UserService";
import ResetTokenService from "../services/ResetTokenService";

module.exports = function() {
const container = new Container();
Expand All @@ -21,6 +22,7 @@ module.exports = function() {
container.service("shipService", () => new ShipService());
container.service("techService", () => new TechService());
container.service("userService", () => new UserService());
container.service("resetTokenService", () => new ResetTokenService());

return container;
};
104 changes: 104 additions & 0 deletions src/loggers/GraylogLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import ILogger from "../interfaces/ILogger";
import { Graylog } from "ts-graylog/dist";
import { GraylogMessage } from "ts-graylog/dist/models/message.model";
import { GraylogLevelEnum } from "ts-graylog/dist/models/enums.model";

/**
* This class represents a simple logger which logs
* straight to the this.graylog.
*/
export default class GraylogLogger implements ILogger {
private serverUrl: string;
private port: number;
private graylog: Graylog = new Graylog({
servers: [{ host: "127.0.0.1", port: 12201 }],
hostname: "server.name",
bufferSize: 1350,
});

/**
* Creates a new GralogLogger instance
*/
public constructor() {
this.serverUrl = "0.0.0.0";
this.port = 12201;
}

/**
* Log a message of severity 'error'
* @param messageText the message
* @param stackTrace the stacktrace
*/
public error(messageText: string, stackTrace: string) {
const message = new GraylogMessage({
version: "1.0",
short_message: messageText,
timestamp: Date.now() / 1000,
level: GraylogLevelEnum.ERROR,
facility: "api",
stack_trace: stackTrace,
});

this.graylog.error(message, null);
}

/**
* Log a message of severity 'warn'
* @param messageText the message
*/
public warn(messageText: string) {
const message = new GraylogMessage({
version: "1.0",
short_message: messageText,
timestamp: Date.now() / 1000,
level: GraylogLevelEnum.WARNING,
});

this.graylog.warning(message, null);
}

/**
* Log a message of severity 'info'
* @param messageText the message
*/
public info(messageText: string) {
const message = new GraylogMessage({
version: "1.0",
short_message: messageText,
timestamp: Date.now() / 1000,
level: GraylogLevelEnum.INFO,
});

this.graylog.info(message);
}

/**
* Log a message of severity 'log'
* @param messageText the message
*/
public log(messageText: string) {
const message = new GraylogMessage({
version: "1.0",
short_message: messageText,
timestamp: Date.now() / 1000,
level: GraylogLevelEnum.NOTICE,
});

this.graylog.log(message);
}

/**
* Log a message of severity 'debug'
* @param messageText
*/
public debug(messageText: string) {
const message = new GraylogMessage({
version: "1.0",
short_message: messageText,
timestamp: Date.now() / 1000,
level: GraylogLevelEnum.DEBUG,
});

this.graylog.log(message);
}
}
Loading