Merge branch 'develop' of Software_Engineering_I/greenvironment-server into master

pull/5/head
Trivernis 5 years ago committed by Gitea
commit 3ad3f04859

@ -1,35 +1,43 @@
# Configuration of the database connection # Configuration of the database connection
[database] [database]
# A connection uri string to the database # A connection uri string to the database
connectionUri = "sqlite://greenvironment.db" connectionUri = "sqlite://greenvironment.db"
# Configuration for the redis connection # Configuration for the redis connection
[redis] [redis]
# A connection uri string to the redis server # A connection uri string to the redis server
connectionUri = "redis://localhost:6379" connectionUri = "redis://localhost:6379"
# Configuration of the http server # Configuration of the http server
[server] [server]
# The port the server is running on # The port the server is running on
port = 8080 port = 8080
# Allow cross origin requests # Allow cross origin requests
cors = false cors = false
# The timeout for a server response # The timeout for a server response
timeout = 30000 timeout = 30000
# Configuration for the sessions # Configuration for the sessions
[session] [session]
# A secret that is used for the sessions. Must be secure # A secret that is used for the sessions. Must be secure
secret = "REPLACE WITH SAFE RANDOM GENERATED SECRET" secret = "REPLACE WITH SAFE RANDOM GENERATED SECRET"
# The maximum age of a cookie. The age is reset with every request of the client # The maximum age of a cookie. The age is reset with every request of the client
cookieMaxAge = 6048e+5 # 7 days cookieMaxAge = 6048e+5 # 7 days
# Configuration for markdown rendering # Configuration for markdown rendering
[markdown] [markdown]
# The plugins used in the markdown parser # The plugins used in the markdown parser
plugins = ["markdown-it-emoji"] plugins = ["markdown-it-emoji"]
@ -53,6 +61,7 @@ publicPath = "./public"
# Configuration for the api # Configuration for the api
[api] [api]
# if graphiql should be enabled # if graphiql should be enabled
graphiql = true graphiql = true
@ -61,16 +70,22 @@ maxQueryComplexity = 5000
# Configuration for the api rate limit # Configuration for the api rate limit
[api.rateLimit] [api.rateLimit]
# rate limit of /upload # rate limit of /upload
[api.rateLimit.upload] [api.rateLimit.upload]
# The time in milliseconds before the rate limit is reset # The time in milliseconds before the rate limit is reset
expire = 60000 expire = 60000
# The total number of calls allowed before rate limiting # The total number of calls allowed before rate limiting
total = 10 total = 10
# rate limit of /graphql # rate limit of /graphql
[api.rateLimit.graphql] [api.rateLimit.graphql]
# The time in milliseconds before the rate limit is reset # The time in milliseconds before the rate limit is reset
expire = 60000 expire = 60000
# The total number of calls allowed before rate limiting # The total number of calls allowed before rate limiting
total = 30 total = 60

@ -2,8 +2,8 @@ import * as compression from "compression";
import * as config from "config"; import * as config from "config";
import * as cookieParser from "cookie-parser"; import * as cookieParser from "cookie-parser";
import * as cors from "cors"; import * as cors from "cors";
import {Request, Response} from "express";
import * as express from "express"; import * as express from "express";
import {Request, Response} from "express";
import * as graphqlHTTP from "express-graphql"; import * as graphqlHTTP from "express-graphql";
import * as session from "express-session"; import * as session from "express-session";
import sharedsession = require("express-socket.io-session"); import sharedsession = require("express-socket.io-session");
@ -11,12 +11,12 @@ import * as fsx from "fs-extra";
import {buildSchema} from "graphql"; import {buildSchema} from "graphql";
import {importSchema} from "graphql-import"; import {importSchema} from "graphql-import";
import queryComplexity, {directiveEstimator, simpleEstimator} from "graphql-query-complexity"; import queryComplexity, {directiveEstimator, simpleEstimator} from "graphql-query-complexity";
import {IncomingMessage, ServerResponse} from "http";
import * as http from "http"; import * as http from "http";
import {IncomingMessage} from "http";
import * as httpStatus from "http-status"; import * as httpStatus from "http-status";
import * as path from "path"; import * as path from "path";
import {RedisClient} from "redis";
import * as redis from "redis"; import * as redis from "redis";
import {RedisClient} from "redis";
import {Sequelize} from "sequelize-typescript"; import {Sequelize} from "sequelize-typescript";
import * as socketIo from "socket.io"; import * as socketIo from "socket.io";
import * as socketIoRedis from "socket.io-redis"; import * as socketIoRedis from "socket.io-redis";
@ -107,7 +107,7 @@ class App {
store: new SequelizeStore({db: this.sequelize}), 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); this.sequelize.options.logging = (msg) => logger.silly(msg);
logger.info("Setting up socket.io"); logger.info("Setting up socket.io");
try { try {
@ -192,7 +192,7 @@ class App {
}); });
// @ts-ignore // @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")); response.setHeader("X-Max-Query-Complexity", config.get("api.maxQueryComplexity"));
return { return {
// @ts-ignore all // @ts-ignore all
@ -224,7 +224,7 @@ class App {
}); });
// redirect all request to the angular file // redirect all request to the angular file
this.app.use((req: any, res: Response) => { 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")); const angularIndex = path.join(this.publicPath, config.get("frontend.angularIndex"));
if (fsx.existsSync(path.join(angularIndex))) { if (fsx.existsSync(path.join(angularIndex))) {
res.sendFile(angularIndex); res.sendFile(angularIndex);

@ -254,10 +254,12 @@ export function resolver(req: any, res: any): any {
}, },
async deletePost({postId}: { postId: number }) { async deletePost({postId}: { postId: number }) {
if (postId) { if (postId) {
const post = await models.Post.findByPk(postId, {include: [{ const post = await models.Post.findByPk(postId, {
include: [{
as: "rAuthor", as: "rAuthor",
model: models.User, model: models.User,
}]}); }],
});
const isAdmin = (await models.User.findOne({where: {id: req.session.userId}})).isAdmin; const isAdmin = (await models.User.findOne({where: {id: req.session.userId}})).isAdmin;
if (post.rAuthor.id === req.session.userId || isAdmin) { if (post.rAuthor.id === req.session.userId || isAdmin) {
try { try {
@ -497,7 +499,7 @@ export function resolver(req: any, res: any): any {
return models.Activity.findAll(); return models.Activity.findAll();
}, },
async createActivity({name, description, points}: async createActivity({name, description, points}:
{name: string, description: string, points: number}) { { name: string, description: string, points: number }) {
if (req.session.userId) { if (req.session.userId) {
const user = await models.User.findByPk(req.session.userId); const user = await models.User.findByPk(req.session.userId);
if (user.isAdmin) { if (user.isAdmin) {

@ -61,7 +61,7 @@ type Mutation {
"Report the post" "Report the post"
report(postId: ID!): Boolean! report(postId: ID!): Boolean!
"send a request" "send a request"
sendRequest(receiver: ID!, type: RequestType): Request sendRequest(receiver: ID!, type: RequestType): Request
"lets you accept a request for a given request id" "lets you accept a request for a given request id"

@ -2,6 +2,7 @@ process.env.NODE_CONFIG_DIR = __dirname + "/../config";
// tslint:disable:no-console // tslint:disable:no-console
import * as cluster from "cluster"; import * as cluster from "cluster";
import App from "./app"; import App from "./app";
const numCPUs = require("os").cpus().length; const numCPUs = require("os").cpus().length;
if (cluster.isMaster) { if (cluster.isMaster) {

@ -14,8 +14,8 @@ import {NoActionSpecifiedError} from "./errors/NoActionSpecifiedError";
import {UserNotFoundError} from "./errors/UserNotFoundError"; import {UserNotFoundError} from "./errors/UserNotFoundError";
import globals from "./globals"; import globals from "./globals";
import {InternalEvents} from "./InternalEvents"; import {InternalEvents} from "./InternalEvents";
import {Activity} from "./models";
import * as models from "./models"; import * as models from "./models";
import {Activity} from "./models";
// tslint:disable:completed-docs // tslint:disable:completed-docs
@ -160,7 +160,7 @@ namespace dataaccess {
} else { } else {
// more performant way to get the votes with plain sql // more performant way to get the votes with plain sql
return await sequelize.query( return await sequelize.query(
`SELECT * FROM ( `SELECT * FROM (
SELECT *, 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 = 'UPVOTE' AND post_id = posts.id) AS upvotes ,
(SELECT count(*) FROM post_votes WHERE vote_type = 'DOWNVOTE' AND post_id = posts.id) AS downvotes (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 * Deletes a post
* @param postId * @param postId
*/ */
export async function deletePost(postId: number): Promise<boolean|GraphQLError> { export async function deletePost(postId: number): Promise<boolean | GraphQLError> {
try { try {
const post = await models.Post.findByPk(postId, {include: [{model: Activity}, {association: "rAuthor"}]}); const post = await models.Post.findByPk(postId, {include: [{model: Activity}, {association: "rAuthor"}]});
const activity = await post.activity(); const activity = await post.activity();
@ -263,8 +263,10 @@ namespace dataaccess {
export async function createRequest(sender: number, receiver: number, requestType?: RequestType) { export async function createRequest(sender: number, receiver: number, requestType?: RequestType) {
requestType = requestType || RequestType.FRIENDREQUEST; requestType = requestType || RequestType.FRIENDREQUEST;
const requestExists = !!await models.Request.findOne({where: const requestExists = !!await models.Request.findOne({
{senderId: sender, receiverId: receiver, requestType}}); where:
{senderId: sender, receiverId: receiver, requestType},
});
if (!requestExists) { if (!requestExists) {
const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType}); const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType});

@ -4,7 +4,7 @@ import {BaseError} from "./BaseError";
* An error that is thrown when a specified user was not found * An error that is thrown when a specified user was not found
*/ */
export class UserNotFoundError extends BaseError { export class UserNotFoundError extends BaseError {
constructor(username: (string|number)) { constructor(username: (string | number)) {
super(`User ${username} not found!`); super(`User ${username} not found!`);
} }
} }

@ -37,10 +37,10 @@ export class ChatRoom extends Model<ChatRoom> {
/** /**
* Returns the messages that have been sent in the chatroom * Returns the messages that have been sent in the chatroom
*/ */
public async messages({first, offset}: {first: number, offset: number}): Promise<ChatMessage[]> { public async messages({first, offset}: { first: number, offset: number }): Promise<ChatMessage[]> {
const limit = first ?? 10; const limit = first ?? 10;
offset = offset ?? 0; offset = offset ?? 0;
return await this.$get("rMessages", {limit, offset}) as ChatMessage[]; return await this.$get("rMessages", {limit, offset, order: [["id", "DESC"]]}) as ChatMessage[];
} }
/** /**

@ -55,7 +55,7 @@ export class Event extends Model<Event> {
* @param first * @param first
* @param offset * @param offset
*/ */
public async participants({first, offset}: {first: number, offset: number}): Promise<User[]> { public async participants({first, offset}: { first: number, offset: number }): Promise<User[]> {
const limit = first ?? 10; const limit = first ?? 10;
offset = offset ?? 0; offset = offset ?? 0;
return await this.$get("rParticipants", {limit, offset}) as User[]; return await this.$get("rParticipants", {limit, offset}) as User[];
@ -66,7 +66,7 @@ export class Event extends Model<Event> {
* @param userId * @param userId
* @param request * @param request
*/ */
public async joined({userId}: {userId: number}, request: any): Promise<boolean> { public async joined({userId}: { userId: number }, request: any): Promise<boolean> {
userId = userId ?? request.session.userId; userId = userId ?? request.session.userId;
if (userId) { if (userId) {
const participants = await this.$get("rParticipants", {where: {id: userId}}) as User[]; const participants = await this.$get("rParticipants", {where: {id: userId}}) as User[];

@ -127,7 +127,7 @@ export class Group extends Model<Group> {
* @param userId * @param userId
* @param request * @param request
*/ */
public async joined({userId}: {userId: number}, request: any): Promise<boolean> { public async joined({userId}: { userId: number }, request: any): Promise<boolean> {
userId = userId ?? request.session.userId; userId = userId ?? request.session.userId;
if (userId) { if (userId) {
const members = await this.$get("rMembers", {where: {id: userId}}) as User[]; const members = await this.$get("rMembers", {where: {id: userId}}) as User[];

@ -50,7 +50,7 @@ export class Post extends Model<Post> {
*/ */
@BelongsToMany(() => User, () => PostVote) @BelongsToMany(() => User, () => PostVote)
// tslint:disable-next-line:completed-docs // tslint:disable-next-line:completed-docs
public rVotes: Array<User & {PostVote: PostVote}>; public rVotes: Array<User & { PostVote: PostVote }>;
/** /**
* The date the post was created at * The date the post was created at
@ -68,15 +68,15 @@ export class Post extends Model<Post> {
/** /**
* Returns the activity of the post. * Returns the activity of the post.
*/ */
public async activity(): Promise<Activity|undefined> { public async activity(): Promise<Activity | undefined> {
return await this.$get("rActivity") as Activity; return await this.$get("rActivity") as Activity;
} }
/** /**
* Returns the votes on a post * Returns the votes on a post
*/ */
public async votes(): Promise<Array<User & {PostVote: PostVote}>> { public async votes(): Promise<Array<User & { PostVote: PostVote }>> {
return await this.$get("rVotes") as Array<User & {PostVote: PostVote}>; return await this.$get("rVotes") as Array<User & { PostVote: PostVote }>;
} }
/** /**
@ -107,12 +107,12 @@ export class Post extends Model<Post> {
*/ */
public async vote(userId: number, type: VoteType): Promise<VoteType> { public async vote(userId: number, type: VoteType): Promise<VoteType> {
type = type ?? VoteType.UPVOTE; type = type ?? VoteType.UPVOTE;
let votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & {PostVote: PostVote}>; let votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & { PostVote: PostVote }>;
let vote = votes[0] ?? null; let vote = votes[0] ?? null;
let created = false; let created = false;
if (!vote) { if (!vote) {
await this.$add("rVote", userId); await this.$add("rVote", userId);
votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & {PostVote: PostVote}>; votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & { PostVote: PostVote }>;
vote = votes[0] ?? null; vote = votes[0] ?? null;
created = true; created = true;
} }
@ -134,10 +134,10 @@ export class Post extends Model<Post> {
* @param userId * @param userId
* @param request * @param request
*/ */
public async userVote({userId}: {userId: number}, request: any): Promise<VoteType> { public async userVote({userId}: { userId: number }, request: any): Promise<VoteType> {
userId = userId ?? request.session.userId; userId = userId ?? request.session.userId;
if (userId) { if (userId) {
const votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & {PostVote: PostVote}>; const votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & { PostVote: PostVote }>;
return votes[0]?.PostVote?.voteType; return votes[0]?.PostVote?.voteType;
} else { } else {
return undefined; return undefined;
@ -149,9 +149,9 @@ export class Post extends Model<Post> {
* @param userId * @param userId
* @param request * @param request
*/ */
public async deletable({userId}: {userId: number}, request: any): Promise<boolean> { public async deletable({userId}: { userId: number }, request: any): Promise<boolean> {
userId = userId ?? request.session.userId; userId = userId ?? request.session.userId;
const isAuthor = Number(userId) === Number(this.authorId); const isAuthor = Number(userId) === Number(this.authorId);
if (userId && !isAuthor) { if (userId && !isAuthor) {
return (await User.findOne({where: {id: userId}})).isAdmin; return (await User.findOne({where: {id: userId}})).isAdmin;
} }

@ -9,8 +9,8 @@ describe("markdown", () => {
expect(result).to.equal("<strong>Hello</strong>"); expect(result).to.equal("<strong>Hello</strong>");
}); });
it("renders markdown emoji", () => { it("renders markdown emoji", () => {
const result = markdown.renderInline(":smile:"); const result = markdown.renderInline(":smile:");
expect(result).to.equal("😄"); expect(result).to.equal("😄");
}); });
}); });
describe("render", () => { describe("render", () => {
@ -19,8 +19,8 @@ describe("markdown", () => {
expect(result).to.equal("<p>#header</p>\n<pre><code></code></pre>\n"); expect(result).to.equal("<p>#header</p>\n<pre><code></code></pre>\n");
}); });
it("renders markdown emoji", () => { it("renders markdown emoji", () => {
const result = markdown.render(":smile:"); const result = markdown.render(":smile:");
expect(result).to.equal("<p>😄</p>\n"); expect(result).to.equal("<p>😄</p>\n");
}); });
}); });
}); });

Loading…
Cancel
Save