diff --git a/config/default.toml b/config/default.toml index 9c52a26..8b38170 100644 --- a/config/default.toml +++ b/config/default.toml @@ -1,35 +1,43 @@ # Configuration of the database connection [database] + # A connection uri string to the database connectionUri = "sqlite://greenvironment.db" # Configuration for the redis connection [redis] + # A connection uri string to the redis server connectionUri = "redis://localhost:6379" # Configuration of the http server [server] + # The port the server is running on port = 8080 + # Allow cross origin requests cors = false + # The timeout for a server response timeout = 30000 # Configuration for the sessions [session] + # A secret that is used for the sessions. Must be secure secret = "REPLACE WITH SAFE RANDOM GENERATED SECRET" + # The maximum age of a cookie. The age is reset with every request of the client cookieMaxAge = 6048e+5 # 7 days # Configuration for markdown rendering [markdown] + # The plugins used in the markdown parser plugins = ["markdown-it-emoji"] @@ -53,6 +61,7 @@ publicPath = "./public" # Configuration for the api [api] + # if graphiql should be enabled graphiql = true @@ -61,16 +70,22 @@ maxQueryComplexity = 5000 # Configuration for the api rate limit [api.rateLimit] + # rate limit of /upload [api.rateLimit.upload] + # The time in milliseconds before the rate limit is reset expire = 60000 + # The total number of calls allowed before rate limiting total = 10 + # rate limit of /graphql [api.rateLimit.graphql] + # The time in milliseconds before the rate limit is reset expire = 60000 + # The total number of calls allowed before rate limiting - total = 30 + total = 60 diff --git a/src/app.ts b/src/app.ts index 6d9aa2f..391f6d7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,8 +2,8 @@ import * as compression from "compression"; import * as config from "config"; import * as cookieParser from "cookie-parser"; import * as cors from "cors"; -import {Request, Response} from "express"; import * as express from "express"; +import {Request, Response} from "express"; import * as graphqlHTTP from "express-graphql"; import * as session from "express-session"; import sharedsession = require("express-socket.io-session"); @@ -11,12 +11,12 @@ import * as fsx from "fs-extra"; import {buildSchema} from "graphql"; import {importSchema} from "graphql-import"; import queryComplexity, {directiveEstimator, simpleEstimator} from "graphql-query-complexity"; -import {IncomingMessage, ServerResponse} from "http"; import * as http from "http"; +import {IncomingMessage} from "http"; import * as httpStatus from "http-status"; import * as path from "path"; -import {RedisClient} from "redis"; import * as redis from "redis"; +import {RedisClient} from "redis"; import {Sequelize} from "sequelize-typescript"; import * as socketIo from "socket.io"; import * as socketIoRedis from "socket.io-redis"; @@ -107,7 +107,7 @@ class App { store: new SequelizeStore({db: this.sequelize}), }); - await this.sequelize.sync({ logging: (msg) => logger.silly(msg)}); + await this.sequelize.sync({logging: (msg) => logger.silly(msg)}); this.sequelize.options.logging = (msg) => logger.silly(msg); logger.info("Setting up socket.io"); try { @@ -192,7 +192,7 @@ class App { }); // @ts-ignore - this.app.use("/graphql", graphqlHTTP(async (request, response, {variables}) => { + this.app.use("/graphql", graphqlHTTP(async (request, response, {variables}) => { response.setHeader("X-Max-Query-Complexity", config.get("api.maxQueryComplexity")); return { // @ts-ignore all @@ -224,7 +224,7 @@ class App { }); // redirect all request to the angular file this.app.use((req: any, res: Response) => { - if (config.get("frontend.angularIndex")) { + if (config.has("frontend.angularIndex")) { const angularIndex = path.join(this.publicPath, config.get("frontend.angularIndex")); if (fsx.existsSync(path.join(angularIndex))) { res.sendFile(angularIndex); diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index 34a760c..e5ef772 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -254,10 +254,12 @@ export function resolver(req: any, res: any): any { }, async deletePost({postId}: { postId: number }) { if (postId) { - const post = await models.Post.findByPk(postId, {include: [{ + const post = await models.Post.findByPk(postId, { + include: [{ as: "rAuthor", model: models.User, - }]}); + }], + }); const isAdmin = (await models.User.findOne({where: {id: req.session.userId}})).isAdmin; if (post.rAuthor.id === req.session.userId || isAdmin) { try { @@ -497,7 +499,7 @@ export function resolver(req: any, res: any): any { return models.Activity.findAll(); }, async createActivity({name, description, points}: - {name: string, description: string, points: number}) { + { name: string, description: string, points: number }) { if (req.session.userId) { const user = await models.User.findByPk(req.session.userId); if (user.isAdmin) { diff --git a/src/graphql/schema.graphql b/src/graphql/schema.graphql index 48075fa..adec8b8 100644 --- a/src/graphql/schema.graphql +++ b/src/graphql/schema.graphql @@ -61,7 +61,7 @@ type Mutation { "Report the post" report(postId: ID!): Boolean! - "send a request" + "send a request" sendRequest(receiver: ID!, type: RequestType): Request "lets you accept a request for a given request id" diff --git a/src/index.ts b/src/index.ts index 9b9724f..928d26a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ process.env.NODE_CONFIG_DIR = __dirname + "/../config"; // tslint:disable:no-console import * as cluster from "cluster"; import App from "./app"; + const numCPUs = require("os").cpus().length; if (cluster.isMaster) { diff --git a/src/lib/dataAccess.ts b/src/lib/dataAccess.ts index 6dcf7e9..544bf5a 100644 --- a/src/lib/dataAccess.ts +++ b/src/lib/dataAccess.ts @@ -14,8 +14,8 @@ import {NoActionSpecifiedError} from "./errors/NoActionSpecifiedError"; import {UserNotFoundError} from "./errors/UserNotFoundError"; import globals from "./globals"; import {InternalEvents} from "./InternalEvents"; -import {Activity} from "./models"; import * as models from "./models"; +import {Activity} from "./models"; // tslint:disable:completed-docs @@ -160,7 +160,7 @@ namespace dataaccess { } else { // more performant way to get the votes with plain sql return await sequelize.query( - `SELECT * FROM ( + `SELECT * FROM ( SELECT *, (SELECT count(*) FROM post_votes WHERE vote_type = 'UPVOTE' AND post_id = posts.id) AS upvotes , (SELECT count(*) FROM post_votes WHERE vote_type = 'DOWNVOTE' AND post_id = posts.id) AS downvotes @@ -195,7 +195,7 @@ namespace dataaccess { * Deletes a post * @param postId */ - export async function deletePost(postId: number): Promise { + export async function deletePost(postId: number): Promise { try { const post = await models.Post.findByPk(postId, {include: [{model: Activity}, {association: "rAuthor"}]}); const activity = await post.activity(); @@ -263,8 +263,10 @@ namespace dataaccess { export async function createRequest(sender: number, receiver: number, requestType?: RequestType) { requestType = requestType || RequestType.FRIENDREQUEST; - const requestExists = !!await models.Request.findOne({where: - {senderId: sender, receiverId: receiver, requestType}}); + const requestExists = !!await models.Request.findOne({ + where: + {senderId: sender, receiverId: receiver, requestType}, + }); if (!requestExists) { const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType}); diff --git a/src/lib/errors/UserNotFoundError.ts b/src/lib/errors/UserNotFoundError.ts index 69724c0..642dc74 100644 --- a/src/lib/errors/UserNotFoundError.ts +++ b/src/lib/errors/UserNotFoundError.ts @@ -4,7 +4,7 @@ import {BaseError} from "./BaseError"; * An error that is thrown when a specified user was not found */ export class UserNotFoundError extends BaseError { - constructor(username: (string|number)) { + constructor(username: (string | number)) { super(`User ${username} not found!`); } } diff --git a/src/lib/models/ChatRoom.ts b/src/lib/models/ChatRoom.ts index f521387..9d557d1 100644 --- a/src/lib/models/ChatRoom.ts +++ b/src/lib/models/ChatRoom.ts @@ -37,10 +37,10 @@ export class ChatRoom extends Model { /** * Returns the messages that have been sent in the chatroom */ - public async messages({first, offset}: {first: number, offset: number}): Promise { + public async messages({first, offset}: { first: number, offset: number }): Promise { const limit = first ?? 10; offset = offset ?? 0; - return await this.$get("rMessages", {limit, offset}) as ChatMessage[]; + return await this.$get("rMessages", {limit, offset, order: [["id", "DESC"]]}) as ChatMessage[]; } /** diff --git a/src/lib/models/Event.ts b/src/lib/models/Event.ts index d6564d6..6d3fcd9 100644 --- a/src/lib/models/Event.ts +++ b/src/lib/models/Event.ts @@ -55,7 +55,7 @@ export class Event extends Model { * @param first * @param offset */ - public async participants({first, offset}: {first: number, offset: number}): Promise { + public async participants({first, offset}: { first: number, offset: number }): Promise { const limit = first ?? 10; offset = offset ?? 0; return await this.$get("rParticipants", {limit, offset}) as User[]; @@ -66,7 +66,7 @@ export class Event extends Model { * @param userId * @param request */ - public async joined({userId}: {userId: number}, request: any): Promise { + public async joined({userId}: { userId: number }, request: any): Promise { userId = userId ?? request.session.userId; if (userId) { const participants = await this.$get("rParticipants", {where: {id: userId}}) as User[]; diff --git a/src/lib/models/Group.ts b/src/lib/models/Group.ts index f4f83a8..eb445e1 100644 --- a/src/lib/models/Group.ts +++ b/src/lib/models/Group.ts @@ -127,7 +127,7 @@ export class Group extends Model { * @param userId * @param request */ - public async joined({userId}: {userId: number}, request: any): Promise { + public async joined({userId}: { userId: number }, request: any): Promise { userId = userId ?? request.session.userId; if (userId) { const members = await this.$get("rMembers", {where: {id: userId}}) as User[]; diff --git a/src/lib/models/Post.ts b/src/lib/models/Post.ts index 7aac135..fad5013 100644 --- a/src/lib/models/Post.ts +++ b/src/lib/models/Post.ts @@ -50,7 +50,7 @@ export class Post extends Model { */ @BelongsToMany(() => User, () => PostVote) // tslint:disable-next-line:completed-docs - public rVotes: Array; + public rVotes: Array; /** * The date the post was created at @@ -68,15 +68,15 @@ export class Post extends Model { /** * Returns the activity of the post. */ - public async activity(): Promise { + public async activity(): Promise { return await this.$get("rActivity") as Activity; } /** * Returns the votes on a post */ - public async votes(): Promise> { - return await this.$get("rVotes") as Array; + public async votes(): Promise> { + return await this.$get("rVotes") as Array; } /** @@ -107,12 +107,12 @@ export class Post extends Model { */ public async vote(userId: number, type: VoteType): Promise { type = type ?? VoteType.UPVOTE; - let votes = await this.$get("rVotes", {where: {id: userId}}) as Array; + let votes = await this.$get("rVotes", {where: {id: userId}}) as Array; let vote = votes[0] ?? null; let created = false; if (!vote) { await this.$add("rVote", userId); - votes = await this.$get("rVotes", {where: {id: userId}}) as Array; + votes = await this.$get("rVotes", {where: {id: userId}}) as Array; vote = votes[0] ?? null; created = true; } @@ -134,10 +134,10 @@ export class Post extends Model { * @param userId * @param request */ - public async userVote({userId}: {userId: number}, request: any): Promise { + public async userVote({userId}: { userId: number }, request: any): Promise { userId = userId ?? request.session.userId; if (userId) { - const votes = await this.$get("rVotes", {where: {id: userId}}) as Array; + const votes = await this.$get("rVotes", {where: {id: userId}}) as Array; return votes[0]?.PostVote?.voteType; } else { return undefined; @@ -149,9 +149,9 @@ export class Post extends Model { * @param userId * @param request */ - public async deletable({userId}: {userId: number}, request: any): Promise { + public async deletable({userId}: { userId: number }, request: any): Promise { userId = userId ?? request.session.userId; - const isAuthor = Number(userId) === Number(this.authorId); + const isAuthor = Number(userId) === Number(this.authorId); if (userId && !isAuthor) { return (await User.findOne({where: {id: userId}})).isAdmin; } diff --git a/src/tests/lib/markdownTest.ts b/src/tests/lib/markdownTest.ts index f38c14b..75d6a12 100644 --- a/src/tests/lib/markdownTest.ts +++ b/src/tests/lib/markdownTest.ts @@ -9,8 +9,8 @@ describe("markdown", () => { expect(result).to.equal("Hello"); }); it("renders markdown emoji", () => { - const result = markdown.renderInline(":smile:"); - expect(result).to.equal("😄"); + const result = markdown.renderInline(":smile:"); + expect(result).to.equal("😄"); }); }); describe("render", () => { @@ -19,8 +19,8 @@ describe("markdown", () => { expect(result).to.equal("

#header

\n
\n"); }); it("renders markdown emoji", () => { - const result = markdown.render(":smile:"); - expect(result).to.equal("

😄

\n"); + const result = markdown.render(":smile:"); + expect(result).to.equal("

😄

\n"); }); }); });