From 298940cc321a29010c2e8ec152334103cbf70794 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Sun, 13 Oct 2019 17:10:47 +0200 Subject: [PATCH] Switched to typescript-sequelize - delted old models - deleted wrapper models - integrated api functions in sequelize-typescript models --- .gitignore | 1 + CHANGELOG.md | 3 +- package-lock.json | 47 ++-- package.json | 6 +- src/app.ts | 6 +- src/graphql/resolvers.ts | 32 +-- src/lib/dataaccess/ChatMessage.ts | 38 --- src/lib/dataaccess/Chatroom.ts | 38 --- src/lib/dataaccess/Post.ts | 95 -------- src/lib/dataaccess/Profile.ts | 161 ------------- src/lib/dataaccess/User.ts | 53 ----- src/lib/dataaccess/datamodels/index.ts | 12 - src/lib/dataaccess/datamodels/models.ts | 279 ----------------------- src/lib/dataaccess/index.ts | 105 ++++----- src/lib/dataaccess/models/ChatMember.ts | 14 ++ src/lib/dataaccess/models/ChatMessage.ts | 41 ++++ src/lib/dataaccess/models/ChatRoom.ts | 28 +++ src/lib/dataaccess/models/Friendship.ts | 14 ++ src/lib/dataaccess/models/Post.ts | 64 ++++++ src/lib/dataaccess/models/PostVote.ts | 23 ++ src/lib/dataaccess/models/Request.ts | 37 +++ src/lib/dataaccess/models/User.ts | 105 +++++++++ src/lib/dataaccess/models/index.ts | 8 + src/lib/dataaccess/wrappers.ts | 5 - src/routes/home.ts | 21 +- tsconfig.json | 4 +- 26 files changed, 453 insertions(+), 787 deletions(-) delete mode 100644 src/lib/dataaccess/ChatMessage.ts delete mode 100644 src/lib/dataaccess/Chatroom.ts delete mode 100644 src/lib/dataaccess/Post.ts delete mode 100644 src/lib/dataaccess/Profile.ts delete mode 100644 src/lib/dataaccess/User.ts delete mode 100644 src/lib/dataaccess/datamodels/index.ts delete mode 100644 src/lib/dataaccess/datamodels/models.ts create mode 100644 src/lib/dataaccess/models/ChatMember.ts create mode 100644 src/lib/dataaccess/models/ChatMessage.ts create mode 100644 src/lib/dataaccess/models/ChatRoom.ts create mode 100644 src/lib/dataaccess/models/Friendship.ts create mode 100644 src/lib/dataaccess/models/Post.ts create mode 100644 src/lib/dataaccess/models/PostVote.ts create mode 100644 src/lib/dataaccess/models/Request.ts create mode 100644 src/lib/dataaccess/models/User.ts create mode 100644 src/lib/dataaccess/models/index.ts delete mode 100644 src/lib/dataaccess/wrappers.ts diff --git a/.gitignore b/.gitignore index f043384..92b6a08 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ test/*.log dist .idea config.yaml +sqz-force diff --git a/CHANGELOG.md b/CHANGELOG.md index a76df00..3edaa34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,4 +13,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - DTOs - Home Route - session management -- Sequelize modules and integration +- Sequelize models and integration +- Sequelize-typescript integration diff --git a/package-lock.json b/package-lock.json index 2249f82..6ccf67c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -223,9 +223,9 @@ "dev": true }, "@types/node": { - "version": "12.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.8.tgz", - "integrity": "sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A==" + "version": "12.7.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz", + "integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==" }, "@types/pg": { "version": "7.11.0", @@ -1477,14 +1477,6 @@ } } }, - "connect-pg-simple": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-6.0.1.tgz", - "integrity": "sha512-zW5AOtRNOLcXxphSmQ+oYj0snlLs1Je3u5K2NWyF7WhMVoPvnQXraK2wzS8f7qLwhMcmYukah2ymu0Gdxf7Qsg==", - "requires": { - "pg": "^7.4.3" - } - }, "connect-session-sequelize": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-6.0.0.tgz", @@ -3185,11 +3177,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "g": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/g/-/g-2.0.1.tgz", - "integrity": "sha1-C1lj69DKcOO8jGdmk0oCGCHIuFc=" - }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -5830,6 +5817,11 @@ "strip-indent": "^1.0.1" } }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -6242,6 +6234,29 @@ "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-2.3.0.tgz", "integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==" }, + "sequelize-typescript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sequelize-typescript/-/sequelize-typescript-1.0.0.tgz", + "integrity": "sha512-oXyvHRTOyI8sJettpISL5LO30GaMMrLqzxiLCy6MjUmBJdaQDpdjn7ofge4J87MSdw+YPzkjrJLogMc9ONY2Tg==", + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", diff --git a/package.json b/package.json index c47a635..94d36ad 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "author": "SoftEngI", "license": "ISC", "devDependencies": { + "@types/bluebird": "^3.5.27", "@types/compression": "^1.0.1", "@types/connect-pg-simple": "^4.2.0", "@types/cookie-parser": "^1.4.2", @@ -33,10 +34,11 @@ "@types/http-status": "^0.2.30", "@types/js-yaml": "^3.12.1", "@types/markdown-it": "0.0.9", - "@types/node": "^12.7.8", + "@types/node": "^12.7.12", "@types/pg": "^7.11.0", "@types/sequelize": "^4.28.5", "@types/socket.io": "^2.1.2", + "@types/validator": "^10.11.3", "@types/winston": "^2.4.4", "delete": "^1.1.0", "gulp": "^4.0.2", @@ -66,7 +68,9 @@ "markdown-it-emoji": "^1.4.0", "pg": "^7.12.1", "pug": "^2.0.4", + "reflect-metadata": "^0.1.13", "sequelize": "^5.19.6", + "sequelize-typescript": "^1.0.0", "socket.io": "^2.2.0", "sqlite3": "^4.1.0", "winston": "^3.2.1" diff --git a/src/app.ts b/src/app.ts index dabc2ec..0a565a6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,17 +5,17 @@ import * as express from "express"; import * as graphqlHTTP from "express-graphql"; import * as session from "express-session"; import sharedsession = require("express-socket.io-session"); +import * as fsx from "fs-extra"; import {buildSchema} from "graphql"; import {importSchema} from "graphql-import"; import * as http from "http"; import * as path from "path"; -import {Sequelize} from "sequelize"; +import {Sequelize} from "sequelize-typescript"; import * as socketIo from "socket.io"; import {resolver} from "./graphql/resolvers"; import dataaccess from "./lib/dataaccess"; import globals from "./lib/globals"; import routes from "./routes"; -import * as fsx from "fs-extra"; const SequelizeStore = require("connect-session-sequelize")(session.Store); const logger = globals.logger; @@ -30,7 +30,7 @@ class App { this.app = express(); this.server = new http.Server(this.app); this.io = socketIo(this.server); - this.sequelize = new Sequelize(globals.config.database.connectionUri); + this.sequelize = new Sequelize(globals.config.database.connectionUri ); } /** diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index fe4e76a..8867c30 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -1,12 +1,7 @@ import {GraphQLError} from "graphql"; import * as status from "http-status"; -import {Sequelize} from "sequelize"; import dataaccess from "../lib/dataaccess"; -import {Chatroom} from "../lib/dataaccess/Chatroom"; -import * as models from "../lib/dataaccess/datamodels"; -import {Post} from "../lib/dataaccess/Post"; -import {Profile} from "../lib/dataaccess/Profile"; -import {User} from "../lib/dataaccess/User"; +import * as models from "../lib/dataaccess/models"; import {NotLoggedInGqlError} from "../lib/errors/graphqlErrors"; import globals from "../lib/globals"; import {InternalEvents} from "../lib/InternalEvents"; @@ -21,8 +16,7 @@ export function resolver(req: any, res: any): any { return { async getSelf() { if (req.session.userId) { - const user = await models.SqUser.findByPk(req.session.userId); - return user.profile; + return models.User.findByPk(req.session.userId); } else { res.status(status.UNAUTHORIZED); return new NotLoggedInGqlError(); @@ -32,8 +26,7 @@ export function resolver(req: any, res: any): any { if (handle) { return await dataaccess.getUserByHandle(handle); } else if (userId) { - const user = await models.SqUser.findByPk(userId); - return user.user; + return models.User.findByPk(userId); } else { res.status(status.BAD_REQUEST); return new GraphQLError("No userId or handle provided."); @@ -49,8 +42,7 @@ export function resolver(req: any, res: any): any { }, async getChat({chatId}: { chatId: number }) { if (chatId) { - const chat = await models.SqChat.findByPk(chatId); - return new Chatroom(chat); + return models.ChatRoom.findByPk(chatId); } else { res.status(status.BAD_REQUEST); return new GraphQLError("No chatId given."); @@ -110,8 +102,8 @@ export function resolver(req: any, res: any): any { async vote({postId, type}: { postId: number, type: dataaccess.VoteType }) { if (postId && type) { if (req.session.userId) { - const post = await models.SqPost.findByPk(postId); - return await post.post.vote(req.session.userId, type); + const post = await models.Post.findByPk(postId); + return await post.vote(req.session.userId, type); } else { res.status(status.UNAUTHORIZED); return new NotLoggedInGqlError(); @@ -138,8 +130,8 @@ export function resolver(req: any, res: any): any { }, async deletePost({postId}: { postId: number }) { if (postId) { - const post = (await models.SqPost.findByPk(postId)).post; - if ((await post.author()).id === req.session.userId) { + const post = await models.Post.findByPk(postId, {include: [models.User]}); + if (post.rAuthor.id === req.session.userId) { return await dataaccess.deletePost(post.id); } else { res.status(status.FORBIDDEN); @@ -200,8 +192,8 @@ export function resolver(req: any, res: any): any { return new NotLoggedInGqlError(); } if (sender && type) { - const profile = new Profile(req.session.userId); - await profile.denyRequest(sender, type); + const user = await models.User.findByPk(req.session.userId); + await user.denyRequest(sender, type); return true; } else { res.status(status.BAD_REQUEST); @@ -215,8 +207,8 @@ export function resolver(req: any, res: any): any { } if (sender && type) { try { - const profile = new Profile(req.session.userId); - await profile.acceptRequest(sender, type); + const user = await models.User.findByPk(req.session.userId); + await user.acceptRequest(sender, type); return true; } catch (err) { globals.logger.warn(err.message); diff --git a/src/lib/dataaccess/ChatMessage.ts b/src/lib/dataaccess/ChatMessage.ts deleted file mode 100644 index 1e42a05..0000000 --- a/src/lib/dataaccess/ChatMessage.ts +++ /dev/null @@ -1,38 +0,0 @@ -import markdown from "../markdown"; -import {Chatroom} from "./Chatroom"; -import * as models from "./datamodels/models"; -import {User} from "./User"; - -export class ChatMessage { - - public id: number; - public content: string; - public createdAt: Date; - - constructor(private message: models.ChatMessage) { - this.id = message.id; - this.content = message.content; - this.createdAt = message.createdAt; - } - - /** - * returns the author of the chat message. - */ - public async author(): Promise { - return new User(await this.message.getAuthor()); - } - - /** - * Returns the rendered html content of the chat message. - */ - public htmlContent(): string { - return markdown.renderInline(this.content); - } - - /** - * returns the chatroom for the chatmessage. - */ - public async chat(): Promise { - return (await this.message.getChat()).chatroom; - } -} diff --git a/src/lib/dataaccess/Chatroom.ts b/src/lib/dataaccess/Chatroom.ts deleted file mode 100644 index 3e08389..0000000 --- a/src/lib/dataaccess/Chatroom.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {SqChat} from "./datamodels"; -import {User} from "./User"; - -export class Chatroom { - - public readonly id: number; - public namespace: string; - - constructor(private chat: SqChat) { - this.id = chat.id; - this.namespace = `/chat/${chat.id}`; - } - - /** - * Returns all members of a chatroom. - */ - public async members(): Promise { - const members = await this.chat.getMembers(); - return members.map((m) => new User(m)); - } - - /** - * Returns messages of the chat - * @param limit - the limit of messages to return - * @param offset - the offset of messages to return - * @param containing - filter by containing - */ - public async messages({first, offset, containing}: { first?: number, offset?: number, containing?: string }) { - const lim = first || 16; - const offs = offset || 0; - const messages = await this.chat.getMessages({limit: lim, offset: offs}); - if (containing) { - return messages.filter((x) => x.content.includes(containing)).map((m) => m.message); - } else { - return messages.map((m) => m.message); - } - } -} diff --git a/src/lib/dataaccess/Post.ts b/src/lib/dataaccess/Post.ts deleted file mode 100644 index db6690c..0000000 --- a/src/lib/dataaccess/Post.ts +++ /dev/null @@ -1,95 +0,0 @@ -import markdown from "../markdown"; -import {SqPost, SqPostVotes} from "./datamodels"; -import {PostVotes} from "./datamodels/models"; -import dataaccess from "./index"; -import {User} from "./User"; - -export class Post { - public readonly id: number; - public createdAt: Date; - public content: string; - public type: string; - - private post: SqPost; - - constructor(post: SqPost) { - this.id = post.id; - this.createdAt = post.createdAt; - this.post = post; - this.type = ""; - this.content = post.content; - } - - /** - * Returns the upvotes of a post. - */ - public async upvotes(): Promise { - return PostVotes.count({where: {voteType: dataaccess.VoteType.UPVOTE, post_id: this.id}}); - } - - /** - * Returns the downvotes of the post - */ - public async downvotes(): Promise { - return PostVotes.count({where: {voteType: dataaccess.VoteType.DOWNVOTE, post_id: this.id}}); - } - - /** - * the content rendered by markdown-it. - */ - public async htmlContent(): Promise { - return markdown.render(this.content); - } - - /** - * The autor of the post. - */ - public async author(): Promise { - return new User(await this.post.getUser()); - } - - /** - * Deletes the post. - */ - public async delete(): Promise { - await this.post.destroy(); - } - - /** - * The type of vote the user performed on the post. - */ - public async userVote(userId: number): Promise { - const votes = await this.post.getVotes({where: {userId}}); - - if (votes.length >= 1) { - return votes[0].voteType; - } else { - return null; - } - } - - /** - * Performs a vote on a post. - * @param userId - * @param type - */ - public async vote(userId: number, type: dataaccess.VoteType): Promise { - type = type || dataaccess.VoteType.UPVOTE; - let vote = await SqPostVotes.findOne({where: {user_id: userId, post_id: this.id}}); - if (!vote) { - await this.post.addVote(userId); - vote = await SqPostVotes.findOne({where: {user_id: userId, post_id: this.id}}); - } - if (vote) { - if (vote.voteType === type) { - await vote.destroy(); - return null; - } else { - vote.voteType = type; - await vote.save(); - } - } - - return vote.voteType; - } -} diff --git a/src/lib/dataaccess/Profile.ts b/src/lib/dataaccess/Profile.ts deleted file mode 100644 index a9eaba2..0000000 --- a/src/lib/dataaccess/Profile.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {RequestNotFoundError} from "../errors/RequestNotFoundError"; -import {Chatroom} from "./Chatroom"; -import {SqUser} from "./datamodels"; -import dataaccess from "./index"; -import * as wrappers from "./wrappers"; - -export class Profile { - - public id: number; - public name: string; - public handle: string; - public email: string; - public greenpoints: number; - public joinedAt: Date; - - protected user: SqUser; - - constructor(user: SqUser) { - this.name = user.username; - this.handle = user.handle; - this.email = user.email; - this.greenpoints = user.rankpoints; - this.joinedAt = user.joinedAt; - this.id = user.id; - this.user = user; - } - - /** - * Returns the number of posts the user created - */ - public async numberOfPosts(): Promise { - return this.user.countPosts(); - } - - /** - * Returns all friends of the user. - */ - public async friends(): Promise { - const result = await this.user.getFriends(); - const userFriends = []; - for (const friend of result) { - userFriends.push(new wrappers.User(friend)); - } - return userFriends; - } - - /** - * Returns all posts for a user. - */ - public async posts({first, offset}: { first: number, offset: number }): Promise { - const postRes = await this.user.getPosts(); - const posts = []; - - for (const post of postRes) { - posts.push(new wrappers.Post(post)); - } - return posts; - } - - /** - * Returns all chatrooms (with pagination). - * Skips the query if the user doesn't exist. - * @param first - * @param offset - */ - public async chats({first, offset}: { first: number, offset?: number }): Promise { - first = first || 10; - offset = offset || 0; - - const result = await this.user.getChats(); - - if (result) { - return result.map((chat) => new Chatroom(chat)); - } else { - return []; - } - } - - /** - * Returns all open requests the user has send. - */ - public async sentRequests() { - return this.user.getSentRequests(); - } - - /** - * Returns all received requests of the user. - */ - public async receivedRequests() { - return this.user.getReceivedRequests(); - } - - /** - * Sets the greenpoints of a user. - * @param points - */ - public async setGreenpoints(points: number): Promise { - this.user.rankpoints = points; - await this.user.save(); - return this.user.rankpoints; - } - - /** - * Sets the email of the user - * @param email - */ - public async setEmail(email: string): Promise { - this.user.email = email; - await this.user.save(); - return this.user.email; - } - - /** - * Updates the handle of the user - */ - public async setHandle(handle: string): Promise { - this.user.handle = handle; - await this.user.save(); - return this.user.handle; - } - - /** - * Sets the username of the user - * @param name - */ - public async setName(name: string): Promise { - this.user.username = name; - await this.user.save(); - return this.user.username; - } - - /** - * Denys a request. - * @param sender - * @param type - */ - public async denyRequest(sender: number, type: dataaccess.RequestType) { - const request = await this.user.getReceivedRequests({where: {senderId: sender, requestType: type}}); - if (request[0]) { - await request[0].destroy(); - } - } - - /** - * Accepts a request. - * @param sender - * @param type - */ - public async acceptRequest(sender: number, type: dataaccess.RequestType) { - const requests = await this.user.getReceivedRequests({where: {senderId: sender, requestType: type}}); - if (requests.length > 0) { - const request = requests[0]; - if (request.requestType === dataaccess.RequestType.FRIENDREQUEST) { - await this.user.addFriend(sender); - await request.destroy(); - } - } else { - throw new RequestNotFoundError(sender, this.id, type); - } - } -} diff --git a/src/lib/dataaccess/User.ts b/src/lib/dataaccess/User.ts deleted file mode 100644 index 8d0b19e..0000000 --- a/src/lib/dataaccess/User.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {SqUser} from "./datamodels"; -import * as wrappers from "./wrappers"; - -export class User { - public id: number; - public name: string; - public handle: string; - public greenpoints: number; - public joinedAt: Date; - - protected user: SqUser; - - constructor(user: SqUser) { - this.id = user.id; - this.name = user.username; - this.handle = user.handle; - this.greenpoints = user.rankpoints; - this.joinedAt = user.joinedAt; - this.user = user; - } - - /** - * Returns the number of posts the user created - */ - public async numberOfPosts(): Promise { - return this.user.countPosts(); - } - - /** - * Returns all friends of the user. - */ - public async friends(): Promise { - const result = await this.user.getFriends(); - const userFriends = []; - for (const friend of result) { - userFriends.push(new User(friend)); - } - return userFriends; - } - - /** - * Returns all posts for a user. - */ - public async posts({first, offset}: { first: number, offset: number }): Promise { - const postRes = await this.user.getPosts(); - const posts = []; - - for (const post of postRes) { - posts.push(new wrappers.Post(post)); - } - return posts; - } -} diff --git a/src/lib/dataaccess/datamodels/index.ts b/src/lib/dataaccess/datamodels/index.ts deleted file mode 100644 index 9d57bf6..0000000 --- a/src/lib/dataaccess/datamodels/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export { - init as datainit, - User as SqUser, - Post as SqPost, - Chat as SqChat, - Request as SqRequest, - PostVotes as SqPostVotes, - ChatMessage as SqChatMessage, - ChatMembers as SqChatMembers, - RequestType as SqRequestType, - UserFriends as SqUserFriends, -} from "./models"; diff --git a/src/lib/dataaccess/datamodels/models.ts b/src/lib/dataaccess/datamodels/models.ts deleted file mode 100644 index 1780124..0000000 --- a/src/lib/dataaccess/datamodels/models.ts +++ /dev/null @@ -1,279 +0,0 @@ -// tslint:disable:object-literal-sort-keys - -import * as sqz from "sequelize"; -import { - Association, - BelongsToGetAssociationMixin, - BelongsToManyAddAssociationMixin, - BelongsToManyCountAssociationsMixin, - BelongsToManyCreateAssociationMixin, - BelongsToManyGetAssociationsMixin, - BelongsToManyHasAssociationMixin, - DataTypes, - HasManyAddAssociationMixin, - HasManyCountAssociationsMixin, - HasManyCreateAssociationMixin, - HasManyGetAssociationsMixin, - HasManyHasAssociationMixin, - HasOneGetAssociationMixin, - Model, - Sequelize, -} from "sequelize"; -import * as wrappers from "../wrappers"; - -const underscored = true; - -enum VoteType { - UPVOTE = "UPVOTE", - DOWNVOTE = "DOWNVOTE", -} - -export enum RequestType { - FRIENDREQUEST = "FRIENDREQUEST", - GROUPINVITE = "GROUPINVITE", - EVENTINVITE = "EVENTINVITE", -} - -export class User extends Model { - - public static associations: { - friends: Association; - posts: Association; - votes: Association; - requests: Association; - }; - - public id!: number; - public username!: string; - public handle!: string; - public email!: string; - public password!: string; - public rankpoints!: number; - - public readonly createdAt!: Date; - public readonly updatedAt!: Date; - - public getFriends!: HasManyGetAssociationsMixin; - public addFriend!: HasManyAddAssociationMixin; - public hasFriend!: HasManyHasAssociationMixin; - public countFriends!: HasManyCountAssociationsMixin; - - public getPosts!: HasManyGetAssociationsMixin; - public addPost!: HasManyAddAssociationMixin; - public hasPost!: HasManyHasAssociationMixin; - public countPosts!: HasManyCountAssociationsMixin; - public createPost!: HasManyCreateAssociationMixin; - - public getReceivedRequests!: HasManyGetAssociationsMixin; - public addReceivedRequest!: HasManyAddAssociationMixin; - public hasReceivedRequest!: HasManyHasAssociationMixin; - public countReceivedRequests!: HasManyCountAssociationsMixin; - public createReceivedRequest!: HasManyCreateAssociationMixin; - - - public getSentRequests!: HasManyGetAssociationsMixin; - public addSentRequest!: HasManyAddAssociationMixin; - public hasSentRequest!: HasManyHasAssociationMixin; - public countSentRequests!: HasManyCountAssociationsMixin; - public createSentRequest!: HasManyCreateAssociationMixin; - - public getChats!: BelongsToManyGetAssociationsMixin; - public addChat!: BelongsToManyAddAssociationMixin; - public hasChat!: BelongsToManyHasAssociationMixin; - public countChats!: BelongsToManyCountAssociationsMixin; - public createChat!: BelongsToManyCreateAssociationMixin; - - /** - * Getter for joined at as the date the entry was created. - */ - public get joinedAt(): Date { - // @ts-ignore - return this.getDataValue("createdAt"); - } - - /** - * Wraps itself into a user - */ - public get user(): wrappers.User { - return new wrappers.User(this); - } - - /** - * returns the username. - */ - public get name(): string { - return this.getDataValue("username"); - } - - /** - * Wraps itself into a profile. - */ - public get profile(): wrappers.Profile { - return new wrappers.Profile(this); - } -} - -export class UserFriends extends Model { -} - -export class Post extends Model { - - public static associations: { - author: Association, - votes: Association, - }; - - public id!: number; - public content!: string; - - public readonly createdAt!: Date; - public readonly updatedAt!: Date; - - public getUser!: BelongsToGetAssociationMixin; - - public getVotes!: HasManyGetAssociationsMixin; - public addVote!: HasManyAddAssociationMixin; - public hasVote!: HasManyHasAssociationMixin; - public countVotes!: HasManyCountAssociationsMixin; - public createVote!: HasManyCreateAssociationMixin; - - /** - * Wraps itself into a Post instance. - */ - public get post(): wrappers.Post { - return new wrappers.Post(this); - } -} - -export class PostVotes extends Model { - public voteType: VoteType; -} - -export class Request extends Model { - public id!: number; - public requestType!: RequestType; - - public getSender!: HasOneGetAssociationMixin; - public getReceiver!: HasOneGetAssociationMixin; -} - -export class Chat extends Model { - public static associations: { - members: Association, - messages: Association, - }; - - public id!: number; - - public readonly createdAt!: Date; - public readonly updatedAt!: Date; - - public getMembers!: BelongsToManyGetAssociationsMixin; - public addMember!: BelongsToManyAddAssociationMixin; - public hasMember!: BelongsToManyHasAssociationMixin; - public countMembers!: BelongsToManyCountAssociationsMixin; - - public getMessages!: HasManyGetAssociationsMixin; - public addMessage!: HasManyAddAssociationMixin; - public hasMessage!: HasManyHasAssociationMixin; - public countMessages!: HasManyCountAssociationsMixin; - public createMessage!: HasManyCreateAssociationMixin; - - /** - * wraps itself into a chatroom. - */ - public get chatroom(): wrappers.Chatroom { - return new wrappers.Chatroom(this); - } -} - -export class ChatMembers extends Model { -} - -export class ChatMessage extends Model { - public id: number; - public content!: string; - public readonly createdAt!: Date; - public readonly updatedAt!: Date; - - public getAuthor!: BelongsToGetAssociationMixin; - public getChat!: BelongsToGetAssociationMixin; - - public get message(): wrappers.ChatMessage { - return new wrappers.ChatMessage(this); - } -} - -export function init(sequelize: Sequelize) { - User.init({ - username: { - allowNull: false, - type: sqz.STRING(128), - }, - handle: { - allowNull: false, - type: sqz.STRING(128), - unique: true, - }, - email: { - allowNull: false, - type: sqz.STRING(128), - unique: true, - }, - password: { - allowNull: false, - type: sqz.STRING(128), - }, - rankpoints: { - allowNull: false, - type: DataTypes.INTEGER, - defaultValue: 0, - }, - }, {sequelize, underscored}); - - UserFriends.init({}, {sequelize, underscored}); - - Post.init({ - content: DataTypes.TEXT, - }, {sequelize, underscored}); - - PostVotes.init({ - voteType: { - type: DataTypes.ENUM, - values: ["UPVOTE", "DOWNVOTE"], - }, - }, {sequelize, underscored}); - - Request.init({ - requestType: { - type: DataTypes.ENUM, - values: ["FRIENDREQUEST", "GROUPINVITE", "EVENTINVITE"], - }, - }, {sequelize, underscored}); - - Chat.init({}, {sequelize, underscored}); - - ChatMembers.init({}, {sequelize, underscored}); - - ChatMessage.init({ - content: { - type: DataTypes.TEXT, - allowNull: false, - }, - }, {sequelize, underscored}); - - User.belongsToMany(User, {through: UserFriends, as: "friends"}); - Post.belongsTo(User, {foreignKey: "userId"}); - User.hasMany(Post, {as: "posts", foreignKey: "userId"}); - Post.belongsToMany(User, {through: PostVotes, as: "votes"}); - User.belongsToMany(Post, {through: PostVotes, as: "votes"}); - User.hasMany(Request, {as: "sentRequests"}); - User.hasMany(Request, {as: "receivedRequests"}); - User.belongsToMany(Chat, {through: ChatMembers}); - Chat.belongsToMany(User, {through: ChatMembers, as: "members"}); - Chat.hasMany(ChatMessage, {as: "messages"}); - ChatMessage.belongsTo(Chat); - ChatMessage.belongsTo(User, {as: "author", foreignKey: "userId"}); - User.hasMany(ChatMessage, {foreignKey: "userId"}); -} - diff --git a/src/lib/dataaccess/index.ts b/src/lib/dataaccess/index.ts index 9fc6359..e5f04cc 100644 --- a/src/lib/dataaccess/index.ts +++ b/src/lib/dataaccess/index.ts @@ -1,18 +1,10 @@ -import {Sequelize} from "sequelize"; +import {Sequelize} from "sequelize-typescript"; import {ChatNotFoundError} from "../errors/ChatNotFoundError"; import {EmailAlreadyRegisteredError} from "../errors/EmailAlreadyRegisteredError"; import {UserNotFoundError} from "../errors/UserNotFoundError"; import globals from "../globals"; import {InternalEvents} from "../InternalEvents"; -import {Chatroom} from "./Chatroom"; -import * as models from "./datamodels"; -import {Post} from "./Post"; -import {Profile} from "./Profile"; -import {User} from "./User"; - -const config = globals.config; -const tableCreationFile = __dirname + "/../../sql/create-tables.sql"; -const tableUpdateFile = __dirname + "/../../sql/update-tables.sql"; +import * as models from "./models"; /** * Generates a new handle from the username and a base64 string of the current time. @@ -35,7 +27,16 @@ namespace dataaccess { export async function init(seq: Sequelize) { sequelize = seq; try { - await models.datainit(sequelize); + await sequelize.addModels([ + models.ChatMember, + models.ChatMessage, + models.ChatRoom, + models.Friendship, + models.Post, + models.PostVote, + models.Request, + models.User, + ]); } catch (err) { globals.logger.error(err.message); globals.logger.debug(err.stack); @@ -46,10 +47,10 @@ namespace dataaccess { * Returns the user by handle. * @param userHandle */ - export async function getUserByHandle(userHandle: string): Promise { - const user = await models.SqUser.findOne({where: {handle: userHandle}}); + export async function getUserByHandle(userHandle: string): Promise { + const user = await models.User.findOne({where: {handle: userHandle}}); if (user) { - return new User(user); + return user; } else { throw new UserNotFoundError(userHandle); } @@ -60,10 +61,10 @@ namespace dataaccess { * @param email * @param password */ - export async function getUserByLogin(email: string, password: string): Promise { - const user = await models.SqUser.findOne({where: {email, password}}); + export async function getUserByLogin(email: string, password: string): Promise { + const user = await models.User.findOne({where: {email, password}}); if (user) { - return new Profile(user); + return user; } else { throw new UserNotFoundError(email); } @@ -75,12 +76,11 @@ namespace dataaccess { * @param email * @param password */ - export async function registerUser(username: string, email: string, password: string) { - const existResult = !!(await models.SqUser.findOne({where: {username, email, password}})); + export async function registerUser(username: string, email: string, password: string): Promise { + const existResult = !!(await models.User.findOne({where: {username, email, password}})); const handle = generateHandle(username); if (!existResult) { - const user = await models.SqUser.create({username, email, password, handle}); - return new Profile(user); + return models.User.create({username, email, password, handle}); } else { throw new EmailAlreadyRegisteredError(email); } @@ -90,10 +90,10 @@ namespace dataaccess { * Returns a post for a given postId.s * @param postId */ - export async function getPost(postId: number): Promise { - const post = await models.SqPost.findByPk(postId); + export async function getPost(postId: number): Promise { + const post = await models.Post.findByPk(postId); if (post) { - return new Post(post); + return post; } else { return null; } @@ -107,18 +107,20 @@ namespace dataaccess { */ export async function getPosts(first: number, offset: number, sort: SortType) { if (sort === SortType.NEW) { - const posts = await models.SqPost.findAll({order: [["createdAt", "DESC"]], limit: first, offset}); - return posts.map((p) => new Post(p)); + return models.Post.findAll({ + include: [{association: "rVotes"}], + limit: first, + offset, + order: [["createdAt", "DESC"]], + }); } else { - const results: models.SqPost[] = await sequelize.query( - `SELECT id FROM ( + return await sequelize.query( + `SELECT * FROM ( SELECT *, - (SELECT count(*) FROM votes WHERE vote_type = 'UPVOTE' AND item_id = posts.id) AS upvotes , - (SELECT count(*) FROM votes WHERE vote_type = 'DOWNVOTE' AND item_id = posts.id) AS downvotes + (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 FROM posts) AS a ORDER BY (a.upvotes - a.downvotes) DESC LIMIT ? OFFSET ?`, - {replacements: [first, offset], mapToModel: true, model: models.SqPost}); - - return results.map((p) => new Post(p)); + {replacements: [first, offset], mapToModel: true, model: models.Post}) as models.Post[]; } } @@ -128,10 +130,9 @@ namespace dataaccess { * @param authorId * @param type */ - export async function createPost(content: string, authorId: number, type?: string): Promise { + export async function createPost(content: string, authorId: number, type?: string): Promise { type = type || "MISC"; - const sqPost = await models.SqPost.create({content, userId: authorId}); - const post = new Post(sqPost); + const post = await models.Post.create({content, authorId}); globals.internalEmitter.emit(InternalEvents.POSTCREATE, post); return post; } @@ -141,7 +142,7 @@ namespace dataaccess { * @param postId */ export async function deletePost(postId: number): Promise { - await (await models.SqPost.findByPk(postId)).destroy(); + await (await models.Post.findByPk(postId)).destroy(); return true; } @@ -149,15 +150,16 @@ namespace dataaccess { * Creates a chatroom containing two users * @param members */ - export async function createChat(...members: number[]): Promise { + export async function createChat(...members: number[]): Promise { return sequelize.transaction(async (t) => { - const chat = await models.SqChat.create({}, {transaction: t}); + const chat = await models.ChatRoom.create({}, {transaction: t, include: [models.User]}); for (const member of members) { - await chat.addMember(Number(member), {transaction: t}); + const user = await models.User.findByPk(member); + await chat.$add("rMember", user, {transaction: t}); } - const chatroom = new Chatroom(chat); - globals.internalEmitter.emit(InternalEvents.CHATCREATE, chatroom); - return chatroom; + await chat.save({transaction: t}); + globals.internalEmitter.emit(InternalEvents.CHATCREATE, chat); + return chat; }); } @@ -168,22 +170,21 @@ namespace dataaccess { * @param content */ export async function sendChatMessage(authorId: number, chatId: number, content: string) { - const chat = await models.SqChat.findByPk(chatId); + const chat = await models.ChatRoom.findByPk(chatId); if (chat) { - const message = await chat.createMessage({content, userId: authorId}); - globals.internalEmitter.emit(InternalEvents.CHATMESSAGE, message.message); - return message.message; + const message = await chat.$create("rMessage", {content, authorId}) as models.ChatMessage; + globals.internalEmitter.emit(InternalEvents.CHATMESSAGE, message); + return message; } else { throw new ChatNotFoundError(chatId); } } /** - * Returns all chats. + * Returns all rChats. */ - export async function getAllChats(): Promise { - const chats = await models.SqChat.findAll(); - return chats.map((c) => new Chatroom(c)); + export async function getAllChats(): Promise { + return models.ChatRoom.findAll(); } /** @@ -195,7 +196,7 @@ namespace dataaccess { export async function createRequest(sender: number, receiver: number, requestType?: RequestType) { requestType = requestType || RequestType.FRIENDREQUEST; - const request = await models.SqRequest.create({senderId: sender, receiverId: receiver, requestType}); + const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType}); globals.internalEmitter.emit(InternalEvents.REQUESTCREATE, Request); return request; } diff --git a/src/lib/dataaccess/models/ChatMember.ts b/src/lib/dataaccess/models/ChatMember.ts new file mode 100644 index 0000000..dacd80a --- /dev/null +++ b/src/lib/dataaccess/models/ChatMember.ts @@ -0,0 +1,14 @@ +import {Column, ForeignKey, Model, Table,} from "sequelize-typescript"; +import {ChatRoom} from "./ChatRoom"; +import {User} from "./User"; + +@Table({underscored: true}) +export class ChatMember extends Model { + @ForeignKey(() => User) + @Column + public userId: number; + + @ForeignKey(() => ChatRoom) + @Column + public chatId: number; +} diff --git a/src/lib/dataaccess/models/ChatMessage.ts b/src/lib/dataaccess/models/ChatMessage.ts new file mode 100644 index 0000000..9811579 --- /dev/null +++ b/src/lib/dataaccess/models/ChatMessage.ts @@ -0,0 +1,41 @@ +import * as sqz from "sequelize"; +import {BelongsTo, Column, CreatedAt, ForeignKey, Model, Table,} from "sequelize-typescript"; +import markdown from "../../markdown"; +import {ChatRoom} from "./ChatRoom"; +import {User} from "./User"; + +@Table({underscored: true}) +export class ChatMessage extends Model { + + @Column(sqz.STRING(512)) + public content: string; + + @ForeignKey(() => ChatRoom) + @Column + public chatId: number; + + @ForeignKey(() => User) + @Column + public authorId: number; + + @BelongsTo(() => ChatRoom, "chatId") + public rChat: ChatRoom; + + @BelongsTo(() => User, "authorId") + public rAuthor: User; + + @CreatedAt + public createdAt: Date; + + public async chat(): Promise { + return await this.$get("rChat") as ChatRoom; + } + + public async author(): Promise { + return await this.$get("rAuthor") as User; + } + + public get htmlContent(): string { + return markdown.renderInline(this.getDataValue("content")); + } +} diff --git a/src/lib/dataaccess/models/ChatRoom.ts b/src/lib/dataaccess/models/ChatRoom.ts new file mode 100644 index 0000000..386da0f --- /dev/null +++ b/src/lib/dataaccess/models/ChatRoom.ts @@ -0,0 +1,28 @@ +import {BelongsToMany, CreatedAt, HasMany, Model, Table,} from "sequelize-typescript"; +import {ChatMember} from "./ChatMember"; +import {ChatMessage} from "./ChatMessage"; +import {User} from "./User"; + +@Table({underscored: true}) +export class ChatRoom extends Model { + @BelongsToMany(() => User, () => ChatMember) + public rMembers: User[]; + + @HasMany(() => ChatMessage, "chatId") + public rMessages: ChatMessage[]; + + @CreatedAt + public readonly createdAt!: Date; + + public async members(): Promise { + return await this.$get("rMembers") as User[]; + } + + public async messages(): Promise { + return await this.$get("rMessages") as ChatMessage[]; + } + + public get namespace(): string { + return "/chats/" + this.getDataValue("id"); + } +} diff --git a/src/lib/dataaccess/models/Friendship.ts b/src/lib/dataaccess/models/Friendship.ts new file mode 100644 index 0000000..d383e7f --- /dev/null +++ b/src/lib/dataaccess/models/Friendship.ts @@ -0,0 +1,14 @@ +import {Column, ForeignKey, Model, Table} from "sequelize-typescript"; +import {User} from "./User"; + +@Table({underscored: true}) +export class Friendship extends Model { + + @ForeignKey(() => User) + @Column + public userId: number; + + @ForeignKey(() => User) + @Column + public friendId: number; +} diff --git a/src/lib/dataaccess/models/Post.ts b/src/lib/dataaccess/models/Post.ts new file mode 100644 index 0000000..b027822 --- /dev/null +++ b/src/lib/dataaccess/models/Post.ts @@ -0,0 +1,64 @@ +import * as sqz from "sequelize"; +import {BelongsTo, BelongsToMany, Column, CreatedAt, ForeignKey, Model, Table,} from "sequelize-typescript"; +import markdown from "../../markdown"; +import {PostVote, VoteType} from "./PostVote"; +import {User} from "./User"; + +@Table({underscored: true}) +export class Post extends Model { + @Column(sqz.STRING(2048)) + public content: string; + + @ForeignKey(() => User) + @Column + public authorId: number; + + @BelongsTo(() => User, "authorId") + public rAuthor: User; + + @BelongsToMany(() => User, () => PostVote) + public rVotes: Array; + + @CreatedAt + public readonly createdAt!: Date; + + public async author(): Promise { + return await this.$get("rAuthor") as User; + } + + public async votes(): Promise> { + return await this.$get("rVotes") as Array; + } + + public get htmlContent() { + return markdown.render(this.getDataValue("content")); + } + + public async upvotes() { + return (await this.votes()).filter((v) => v.PostVote.voteType === VoteType.UPVOTE).length; + } + + public async downvotes() { + return (await this.votes()).filter((v) => v.PostVote.voteType === VoteType.DOWNVOTE).length; + } + + public async vote(userId: number, type: VoteType): Promise { + type = type || VoteType.UPVOTE; + let vote = await PostVote.findOne({where: {user_id: userId, post_id: this.id}}); + if (!vote) { + await this.$add("rVotes", userId); + vote = await PostVote.findOne({where: {user_id: userId, post_id: this.id}}); + } + if (vote) { + if (vote.voteType === type) { + await vote.destroy(); + return null; + } else { + vote.voteType = type; + await vote.save(); + } + } + + return vote.voteType; + } +} diff --git a/src/lib/dataaccess/models/PostVote.ts b/src/lib/dataaccess/models/PostVote.ts new file mode 100644 index 0000000..058176d --- /dev/null +++ b/src/lib/dataaccess/models/PostVote.ts @@ -0,0 +1,23 @@ +import * as sqz from "sequelize"; +import {Column, ForeignKey, Model, Table,} from "sequelize-typescript"; +import {Post} from "./Post"; +import {User} from "./User"; + +export enum VoteType { + UPVOTE = "UPVOTE", + DOWNVOTE = "DOWNVOTE", +} + +@Table({underscored: true}) +export class PostVote extends Model { + @Column({type: sqz.ENUM, values: ["UPVOTE", "DOWNVOTE"]}) + public voteType: VoteType; + + @ForeignKey(() => User) + @Column + public userId: number; + + @ForeignKey(() => Post) + @Column + public postId: number; +} diff --git a/src/lib/dataaccess/models/Request.ts b/src/lib/dataaccess/models/Request.ts new file mode 100644 index 0000000..fffa09f --- /dev/null +++ b/src/lib/dataaccess/models/Request.ts @@ -0,0 +1,37 @@ +import * as sqz from "sequelize"; +import {BelongsTo, Column, ForeignKey, Model, Table,} from "sequelize-typescript"; +import {User} from "./User"; + +export enum RequestType { + FRIENDREQUEST = "FRIENDREQUEST", + GROUPINVITE = "GROUPINVITE", + EVENTINVITE = "EVENTINVITE", +} + +@Table({underscored: true}) +export class Request extends Model { + @Column({type: sqz.ENUM, values: ["FRIENDREQUEST", "GROUPINVITE", "EVENTINVITE"]}) + public requestType: RequestType; + + @ForeignKey(() => User) + @Column + public senderId: number; + + @BelongsTo(() => User, "senderId") + public rSender: User; + + @ForeignKey(() => User) + @Column + public receiverId: number; + + @BelongsTo(() => User, "receiverId") + public rReceiver: User; + + public async receiver(): Promise { + return await this.$get("rReceiver") as User; + } + + public async sender(): Promise { + return await this.$get("rSender") as User; + } +} diff --git a/src/lib/dataaccess/models/User.ts b/src/lib/dataaccess/models/User.ts new file mode 100644 index 0000000..1219173 --- /dev/null +++ b/src/lib/dataaccess/models/User.ts @@ -0,0 +1,105 @@ +import * as sqz from "sequelize"; +import {BelongsToMany, Column, CreatedAt, HasMany, Model, Table, UpdatedAt,} from "sequelize-typescript"; +import {RequestNotFoundError} from "../../errors/RequestNotFoundError"; +import {ChatMember} from "./ChatMember"; +import {ChatMessage} from "./ChatMessage"; +import {ChatRoom} from "./ChatRoom"; +import {Friendship} from "./Friendship"; +import {Post} from "./Post"; +import {PostVote} from "./PostVote"; +import {Request, RequestType} from "./Request"; + +@Table({underscored: true}) +export class User extends Model { + @Column(sqz.STRING(128)) + public username: string; + + @Column(sqz.STRING(128)) + public handle: string; + + @Column(sqz.STRING(128)) + public email: string; + + @Column(sqz.STRING(128)) + public password: string; + + @Column({defaultValue: 0}) + public rankpoints: number; + + @BelongsToMany(() => User, () => Friendship) + public friends: User[]; + + @BelongsToMany(() => Post, () => PostVote) + public votes: Array; + + @BelongsToMany(() => ChatRoom, () => ChatMember) + public rChats: ChatRoom[]; + + @HasMany(() => Post, "authorId") + public rPosts: Post[]; + + @HasMany(() => Request, "receiverId") + public rSentRequests: Request[]; + + @HasMany(() => Request, "receiverId") + public rReceivedRequests: Request[]; + + @HasMany(() => ChatMessage, "authorId") + public messages: ChatMessage[]; + + @CreatedAt + public readonly createdAt!: Date; + + @UpdatedAt + public readonly updatedAt!: Date; + + public get name(): string { + return this.getDataValue("username"); + } + + public get joinedAt(): Date { + return this.getDataValue("createdAt"); + } + + public async chats(): Promise { + return await this.$get("rChats") as ChatRoom[]; + } + + public async sentRequests(): Promise { + return await this.$get("rSentRequests") as Request[]; + } + + public async receivedRequests(): Promise { + return await this.$get("rReceivedRequests") as Request[]; + } + + public async posts({first, offset}: {first: number, offset: number}): Promise { + return await this.$get("rPosts", {limit: first, offset}) as Post[]; + } + + public async numberOfPosts(): Promise { + return this.$count("rPosts"); + } + + public async denyRequest(sender: number, type: RequestType) { + const request = await this.$get("rReceivedRequests", + {where: {senderId: sender, requestType: type}}) as Request[]; + if (request[0]) { + await request[0].destroy(); + } + } + + public async acceptRequest(sender: number, type: RequestType) { + const requests = await this.$get("rReceivedRequests", + {where: {senderId: sender, requestType: type}}) as Request[]; + if (requests.length > 0) { + const request = requests[0]; + if (request.requestType === RequestType.FRIENDREQUEST) { + await this.$add("friends", sender); + await request.destroy(); + } + } else { + throw new RequestNotFoundError(sender, this.id, type); + } + } +} diff --git a/src/lib/dataaccess/models/index.ts b/src/lib/dataaccess/models/index.ts new file mode 100644 index 0000000..9e47059 --- /dev/null +++ b/src/lib/dataaccess/models/index.ts @@ -0,0 +1,8 @@ +export {ChatMember} from "./ChatMember"; +export {ChatMessage} from "./ChatMessage"; +export {ChatRoom} from "./ChatRoom"; +export {Friendship} from "./Friendship"; +export {Post} from "./Post"; +export {PostVote} from "./PostVote"; +export {Request} from "./Request"; +export {User} from "./User"; diff --git a/src/lib/dataaccess/wrappers.ts b/src/lib/dataaccess/wrappers.ts deleted file mode 100644 index 052fb95..0000000 --- a/src/lib/dataaccess/wrappers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export {User} from "./User"; -export {Chatroom} from "./Chatroom"; -export {Post} from "./Post"; -export {Profile} from "./Profile"; -export {ChatMessage} from "./ChatMessage"; diff --git a/src/routes/home.ts b/src/routes/home.ts index 4fb7f79..128088c 100644 --- a/src/routes/home.ts +++ b/src/routes/home.ts @@ -1,10 +1,7 @@ import {Router} from "express"; import {Namespace, Server} from "socket.io"; import dataaccess from "../lib/dataaccess"; -import {ChatMessage} from "../lib/dataaccess/ChatMessage"; -import {Chatroom} from "../lib/dataaccess/Chatroom"; -import {Request} from "../lib/dataaccess/datamodels/models"; -import {Post} from "../lib/dataaccess/Post"; +import {ChatMessage, ChatRoom, Post, Request, User} from "../lib/dataaccess/models"; import globals from "../lib/globals"; import {InternalEvents} from "../lib/InternalEvents"; import Route from "../lib/Route"; @@ -37,18 +34,18 @@ class HomeRoute extends Route { socket.on("postCreate", async (content) => { if (socket.handshake.session.userId) { const post = await dataaccess.createPost(content, socket.handshake.session.userId); - io.emit("post", Object.assign(post, {htmlContent: post.htmlContent()})); + io.emit("post", Object.assign(post, {htmlContent: post.htmlContent})); } else { socket.emit("error", "Not logged in!"); } }); globals.internalEmitter.on(InternalEvents.REQUESTCREATE, async (request: Request) => { - if ((await request.getSender()).id === socket.handshake.session.userId) { + if ((await request.$get("sender") as User).id === socket.handshake.session.userId) { socket.emit("request", request); } }); globals.internalEmitter.on(InternalEvents.GQLPOSTCREATE, async (post: Post) => { - socket.emit("post", Object.assign(post, {htmlContent: post.htmlContent()})); + socket.emit("post", Object.assign(post, {htmlContent: post.htmlContent})); }); }); @@ -56,7 +53,7 @@ class HomeRoute extends Route { for (const chat of chats) { chatRooms[chat.id] = this.getChatSocketNamespace(chat.id); } - globals.internalEmitter.on(InternalEvents.CHATCREATE, (chat: Chatroom) => { + globals.internalEmitter.on(InternalEvents.CHATCREATE, (chat: ChatRoom) => { chatRooms[chat.id] = this.getChatSocketNamespace(chat.id); }); } @@ -82,15 +79,15 @@ class HomeRoute extends Route { if (socket.handshake.session.userId) { const userId = socket.handshake.session.userId; const message = await dataaccess.sendChatMessage(userId, chatId, content); - socket.broadcast.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent()})); - socket.emit("chatMessageSent", Object.assign(message, {htmlContent: message.htmlContent()})); + socket.broadcast.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent})); + socket.emit("chatMessageSent", Object.assign(message, {htmlContent: message.htmlContent})); } else { socket.emit("error", "Not logged in!"); } }); globals.internalEmitter.on(InternalEvents.GQLCHATMESSAGE, async (message: ChatMessage) => { - if ((await message.chat()).id === chatId) { - socket.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent()})); + if ((await message.$get("chat") as ChatRoom).id === chatId) { + socket.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent})); } }); }); diff --git a/tsconfig.json b/tsconfig.json index 6fedd9d..f8deaa4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,9 @@ "target": "es2018", "allowJs": true, "moduleResolution": "node", - "module": "commonjs" + "module": "commonjs", + "experimentalDecorators": true, + "emitDecoratorMetadata": true }, "include": [ "src/**/*"