Merge branch 'develop' into julius-dev
commit
8dd1edd817
@ -1,73 +0,0 @@
|
|||||||
import * as crypto from "crypto";
|
|
||||||
import {EventEmitter} from "events";
|
|
||||||
|
|
||||||
export class MemoryCache extends EventEmitter {
|
|
||||||
private cacheItems: any = {};
|
|
||||||
private cacheExpires: any = {};
|
|
||||||
private expireCheck: NodeJS.Timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates interval function.
|
|
||||||
* @param ttl
|
|
||||||
*/
|
|
||||||
constructor(private ttl: number = 500) {
|
|
||||||
super();
|
|
||||||
this.expireCheck = setInterval(() => this.checkExpires(), ttl / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a md5 hash of the given key.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public hashKey(key: string): string {
|
|
||||||
const hash = crypto.createHash("sha1");
|
|
||||||
const data = hash.update(key, "utf8");
|
|
||||||
return data.digest("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an entry.
|
|
||||||
* @param key
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
public set(key: string, value: any) {
|
|
||||||
this.cacheItems[key] = value;
|
|
||||||
this.cacheExpires[key] = Date.now() + this.ttl;
|
|
||||||
this.emit("set", key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the entry stored with the given key.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public get(key: string) {
|
|
||||||
if (this.cacheItems.hasOwnProperty(key)) {
|
|
||||||
this.emit("hit", key, this.cacheItems[key]);
|
|
||||||
return this.cacheItems[key];
|
|
||||||
} else {
|
|
||||||
this.emit("miss", key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a cache item.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public delete(key: string) {
|
|
||||||
this.emit("delete", key);
|
|
||||||
delete this.cacheItems[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks expires and clears items that are over the expire value.
|
|
||||||
*/
|
|
||||||
private checkExpires() {
|
|
||||||
for (const [key, value] of Object.entries(this.cacheExpires)) {
|
|
||||||
if (value < Date.now()) {
|
|
||||||
this.emit("delete", key);
|
|
||||||
delete this.cacheItems[key];
|
|
||||||
delete this.cacheExpires[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<User> {
|
|
||||||
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<Chatroom> {
|
|
||||||
return (await this.message.getChat()).chatroom;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<User[]> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<number> {
|
|
||||||
return PostVotes.count({where: {voteType: dataaccess.VoteType.UPVOTE, post_id: this.id}});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the downvotes of the post
|
|
||||||
*/
|
|
||||||
public async downvotes(): Promise<number> {
|
|
||||||
return PostVotes.count({where: {voteType: dataaccess.VoteType.DOWNVOTE, post_id: this.id}});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the content rendered by markdown-it.
|
|
||||||
*/
|
|
||||||
public async htmlContent(): Promise<string> {
|
|
||||||
return markdown.render(this.content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The autor of the post.
|
|
||||||
*/
|
|
||||||
public async author(): Promise<User> {
|
|
||||||
return new User(await this.post.getUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the post.
|
|
||||||
*/
|
|
||||||
public async delete(): Promise<void> {
|
|
||||||
await this.post.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of vote the user performed on the post.
|
|
||||||
*/
|
|
||||||
public async userVote(userId: number): Promise<dataaccess.VoteType> {
|
|
||||||
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<dataaccess.VoteType> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<number> {
|
|
||||||
return this.user.countPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all friends of the user.
|
|
||||||
*/
|
|
||||||
public async friends(): Promise<wrappers.User[]> {
|
|
||||||
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<wrappers.Post[]> {
|
|
||||||
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<Chatroom[]> {
|
|
||||||
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<number> {
|
|
||||||
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<string> {
|
|
||||||
this.user.email = email;
|
|
||||||
await this.user.save();
|
|
||||||
return this.user.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the handle of the user
|
|
||||||
*/
|
|
||||||
public async setHandle(handle: string): Promise<string> {
|
|
||||||
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<string> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<number> {
|
|
||||||
return this.user.countPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all friends of the user.
|
|
||||||
*/
|
|
||||||
public async friends(): Promise<User[]> {
|
|
||||||
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<wrappers.Post[]> {
|
|
||||||
const postRes = await this.user.getPosts();
|
|
||||||
const posts = [];
|
|
||||||
|
|
||||||
for (const post of postRes) {
|
|
||||||
posts.push(new wrappers.Post(post));
|
|
||||||
}
|
|
||||||
return posts;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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";
|
|
@ -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<User, User>;
|
|
||||||
posts: Association<User, Post>;
|
|
||||||
votes: Association<User, PostVotes>;
|
|
||||||
requests: Association<User, Request>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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<User>;
|
|
||||||
public addFriend!: HasManyAddAssociationMixin<User, number>;
|
|
||||||
public hasFriend!: HasManyHasAssociationMixin<User, number>;
|
|
||||||
public countFriends!: HasManyCountAssociationsMixin;
|
|
||||||
|
|
||||||
public getPosts!: HasManyGetAssociationsMixin<Post>;
|
|
||||||
public addPost!: HasManyAddAssociationMixin<Post, number>;
|
|
||||||
public hasPost!: HasManyHasAssociationMixin<Post, number>;
|
|
||||||
public countPosts!: HasManyCountAssociationsMixin;
|
|
||||||
public createPost!: HasManyCreateAssociationMixin<Post>;
|
|
||||||
|
|
||||||
public getReceivedRequests!: HasManyGetAssociationsMixin<Request>;
|
|
||||||
public addReceivedRequest!: HasManyAddAssociationMixin<Request, number>;
|
|
||||||
public hasReceivedRequest!: HasManyHasAssociationMixin<Request, number>;
|
|
||||||
public countReceivedRequests!: HasManyCountAssociationsMixin;
|
|
||||||
public createReceivedRequest!: HasManyCreateAssociationMixin<Request>;
|
|
||||||
|
|
||||||
|
|
||||||
public getSentRequests!: HasManyGetAssociationsMixin<Request>;
|
|
||||||
public addSentRequest!: HasManyAddAssociationMixin<Request, number>;
|
|
||||||
public hasSentRequest!: HasManyHasAssociationMixin<Request, number>;
|
|
||||||
public countSentRequests!: HasManyCountAssociationsMixin;
|
|
||||||
public createSentRequest!: HasManyCreateAssociationMixin<Request>;
|
|
||||||
|
|
||||||
public getChats!: BelongsToManyGetAssociationsMixin<Chat>;
|
|
||||||
public addChat!: BelongsToManyAddAssociationMixin<Chat, number>;
|
|
||||||
public hasChat!: BelongsToManyHasAssociationMixin<Chat, number>;
|
|
||||||
public countChats!: BelongsToManyCountAssociationsMixin;
|
|
||||||
public createChat!: BelongsToManyCreateAssociationMixin<Chat>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Post, User>,
|
|
||||||
votes: Association<Post, PostVotes>,
|
|
||||||
};
|
|
||||||
|
|
||||||
public id!: number;
|
|
||||||
public content!: string;
|
|
||||||
|
|
||||||
public readonly createdAt!: Date;
|
|
||||||
public readonly updatedAt!: Date;
|
|
||||||
|
|
||||||
public getUser!: BelongsToGetAssociationMixin<User>;
|
|
||||||
|
|
||||||
public getVotes!: HasManyGetAssociationsMixin<PostVotes>;
|
|
||||||
public addVote!: HasManyAddAssociationMixin<PostVotes, number>;
|
|
||||||
public hasVote!: HasManyHasAssociationMixin<PostVotes, number>;
|
|
||||||
public countVotes!: HasManyCountAssociationsMixin;
|
|
||||||
public createVote!: HasManyCreateAssociationMixin<PostVotes>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<User>;
|
|
||||||
public getReceiver!: HasOneGetAssociationMixin<User>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Chat extends Model {
|
|
||||||
public static associations: {
|
|
||||||
members: Association<Chat, User>,
|
|
||||||
messages: Association<Chat, ChatMessage>,
|
|
||||||
};
|
|
||||||
|
|
||||||
public id!: number;
|
|
||||||
|
|
||||||
public readonly createdAt!: Date;
|
|
||||||
public readonly updatedAt!: Date;
|
|
||||||
|
|
||||||
public getMembers!: BelongsToManyGetAssociationsMixin<User>;
|
|
||||||
public addMember!: BelongsToManyAddAssociationMixin<User, number>;
|
|
||||||
public hasMember!: BelongsToManyHasAssociationMixin<User, number>;
|
|
||||||
public countMembers!: BelongsToManyCountAssociationsMixin;
|
|
||||||
|
|
||||||
public getMessages!: HasManyGetAssociationsMixin<ChatMessage>;
|
|
||||||
public addMessage!: HasManyAddAssociationMixin<ChatMessage, number>;
|
|
||||||
public hasMessage!: HasManyHasAssociationMixin<ChatMessage, number>;
|
|
||||||
public countMessages!: HasManyCountAssociationsMixin;
|
|
||||||
public createMessage!: HasManyCreateAssociationMixin<ChatMessage>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<User>;
|
|
||||||
public getChat!: BelongsToGetAssociationMixin<Chat>;
|
|
||||||
|
|
||||||
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"});
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
export {User} from "./User";
|
|
||||||
export {Chatroom} from "./Chatroom";
|
|
||||||
export {Post} from "./Post";
|
|
||||||
export {Profile} from "./Profile";
|
|
||||||
export {ChatMessage} from "./ChatMessage";
|
|
@ -1,8 +1,13 @@
|
|||||||
import {GraphQLError} from "graphql";
|
import {GraphQLError} from "graphql";
|
||||||
|
|
||||||
export class NotLoggedInGqlError extends GraphQLError {
|
export class NotLoggedInGqlError extends GraphQLError {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Not logged in");
|
super("Not logged in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PostNotFoundGqlError extends GraphQLError {
|
||||||
|
constructor(postId: number) {
|
||||||
|
super(`Post '${postId}' not found!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||||
|
import {ChatRoom} from "./ChatRoom";
|
||||||
|
import {User} from "./User";
|
||||||
|
|
||||||
|
@Table({underscored: true})
|
||||||
|
export class ChatMember extends Model<ChatMember> {
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public userId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => ChatRoom)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public chatId: number;
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import * as sqz from "sequelize";
|
||||||
|
import {BelongsTo, Column, CreatedAt, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||||
|
import markdown from "../markdown";
|
||||||
|
import {ChatRoom} from "./ChatRoom";
|
||||||
|
import {User} from "./User";
|
||||||
|
|
||||||
|
@Table({underscored: true})
|
||||||
|
export class ChatMessage extends Model<ChatMessage> {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(512), allowNull: false})
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
@ForeignKey(() => ChatRoom)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public chatId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public authorId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => ChatRoom, "chatId")
|
||||||
|
public rChat: ChatRoom;
|
||||||
|
|
||||||
|
@BelongsTo(() => User, "authorId")
|
||||||
|
public rAuthor: User;
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
public async chat(): Promise<ChatRoom> {
|
||||||
|
return await this.$get("rChat") as ChatRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async author(): Promise<User> {
|
||||||
|
return await this.$get("rAuthor") as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get htmlContent(): string {
|
||||||
|
return markdown.renderInline(this.getDataValue("content"));
|
||||||
|
}
|
||||||
|
}
|
@ -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<ChatRoom> {
|
||||||
|
@BelongsToMany(() => User, () => ChatMember)
|
||||||
|
public rMembers: User[];
|
||||||
|
|
||||||
|
@HasMany(() => ChatMessage, "chatId")
|
||||||
|
public rMessages: ChatMessage[];
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
public readonly createdAt!: Date;
|
||||||
|
|
||||||
|
public async members(): Promise<User[]> {
|
||||||
|
return await this.$get("rMembers") as User[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async messages(): Promise<ChatMessage[]> {
|
||||||
|
return await this.$get("rMessages") as ChatMessage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get namespace(): string {
|
||||||
|
return "/chats/" + this.getDataValue("id");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||||
|
import {User} from "./User";
|
||||||
|
|
||||||
|
@Table({underscored: true})
|
||||||
|
export class Friendship extends Model<Friendship> {
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public userId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public friendId: number;
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import * as sqz from "sequelize";
|
||||||
|
import {BelongsTo, BelongsToMany, Column, CreatedAt, ForeignKey, Model, NotNull, 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<Post> {
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(2048), allowNull: false})
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public authorId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => User, "authorId")
|
||||||
|
public rAuthor: User;
|
||||||
|
|
||||||
|
@BelongsToMany(() => User, () => PostVote)
|
||||||
|
public rVotes: Array<User & {PostVote: PostVote}>;
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
public readonly createdAt!: Date;
|
||||||
|
|
||||||
|
public async author(): Promise<User> {
|
||||||
|
return await this.$get("rAuthor") as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async votes(): Promise<Array<User & {PostVote: PostVote}>> {
|
||||||
|
return await this.$get("rVotes") as Array<User & {PostVote: PostVote}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<VoteType> {
|
||||||
|
type = type || VoteType.UPVOTE;
|
||||||
|
let votes = await this.$get("rVotes", {where: {id: userId}}) as Array<User & {PostVote: PostVote}>;
|
||||||
|
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<User & {PostVote: PostVote}>;
|
||||||
|
vote = votes[0] || null;
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
if (vote) {
|
||||||
|
if (vote.PostVote.voteType === type && !created) {
|
||||||
|
await vote.PostVote.destroy();
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
vote.PostVote.voteType = type;
|
||||||
|
await vote.PostVote.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vote.PostVote.voteType;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import * as sqz from "sequelize";
|
||||||
|
import {Column, ForeignKey, Model, NotNull, 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<PostVote> {
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.ENUM, values: ["UPVOTE", "DOWNVOTE"], defaultValue: "UPVOTE", allowNull: false})
|
||||||
|
public voteType: VoteType;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public userId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => Post)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public postId: number;
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import * as sqz from "sequelize";
|
||||||
|
import {BelongsTo, Column, ForeignKey, Model, NotNull, 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<Request> {
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.ENUM, values: ["FRIENDREQUEST", "GROUPINVITE", "EVENTINVITE"],
|
||||||
|
defaultValue: "FRIENDREQUEST", allowNull: false})
|
||||||
|
public requestType: RequestType;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public senderId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => User, "senderId")
|
||||||
|
public rSender: User;
|
||||||
|
|
||||||
|
@ForeignKey(() => User)
|
||||||
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
|
public receiverId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => User, "receiverId")
|
||||||
|
public rReceiver: User;
|
||||||
|
|
||||||
|
public async receiver(): Promise<User> {
|
||||||
|
return await this.$get("rReceiver") as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sender(): Promise<User> {
|
||||||
|
return await this.$get("rSender") as User;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
import * as sqz from "sequelize";
|
||||||
|
import {
|
||||||
|
BelongsToMany,
|
||||||
|
Column,
|
||||||
|
CreatedAt,
|
||||||
|
HasMany,
|
||||||
|
Model,
|
||||||
|
NotNull,
|
||||||
|
Table,
|
||||||
|
Unique,
|
||||||
|
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<User> {
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(128), allowNull: false})
|
||||||
|
public username: string;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Unique
|
||||||
|
@Column({type: sqz.STRING(128), allowNull: false, unique: true})
|
||||||
|
public handle: string;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(128), allowNull: false, unique: true})
|
||||||
|
public email: string;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(128), allowNull: false})
|
||||||
|
public password: string;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column({defaultValue: 0, allowNull: false})
|
||||||
|
public rankpoints: number;
|
||||||
|
|
||||||
|
@BelongsToMany(() => User, () => Friendship)
|
||||||
|
public friends: User[];
|
||||||
|
|
||||||
|
@BelongsToMany(() => Post, () => PostVote)
|
||||||
|
public votes: Array<Post & {PostVote: PostVote}>;
|
||||||
|
|
||||||
|
@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<ChatRoom[]> {
|
||||||
|
return await this.$get("rChats") as ChatRoom[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sentRequests(): Promise<Request[]> {
|
||||||
|
return await this.$get("rSentRequests") as Request[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async receivedRequests(): Promise<Request[]> {
|
||||||
|
return await this.$get("rReceivedRequests") as Request[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async posts({first, offset}: {first: number, offset: number}): Promise<Post[]> {
|
||||||
|
return await this.$get("rPosts", {limit: first, offset}) as Post[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async numberOfPosts(): Promise<number> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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";
|
@ -1,137 +0,0 @@
|
|||||||
--create functions
|
|
||||||
DO $$BEGIN
|
|
||||||
|
|
||||||
IF NOT EXISTS(SELECT 1 from pg_proc WHERE proname = 'function_exists') THEN
|
|
||||||
CREATE FUNCTION function_exists(text) RETURNS boolean LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN EXISTS(SELECT 1 from pg_proc WHERE proname = $1);
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT function_exists('type_exists') THEN
|
|
||||||
CREATE FUNCTION type_exists(text) RETURNS boolean LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN EXISTS (SELECT 1 FROM pg_type WHERE typname = $1);
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
--create types
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
IF NOT type_exists('votetype') THEN
|
|
||||||
CREATE TYPE votetype AS enum ('DOWNVOTE', 'UPVOTE');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT type_exists('posttype') THEN
|
|
||||||
CREATE TYPE posttype AS enum ('MISC', 'ACTION', 'IMAGE', 'TEXT');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT type_exists('requesttype') THEN
|
|
||||||
CREATE TYPE requesttype AS enum ('FRIENDREQUEST');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
-- create functions relying on types
|
|
||||||
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
IF NOT function_exists('cast_to_votetype') THEN
|
|
||||||
CREATE FUNCTION cast_to_votetype(text) RETURNS votetype LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN CASE WHEN $1::votetype IS NULL THEN 'UPVOTE' ELSE $1::votetype END;
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT function_exists('cast_to_posttype') THEN
|
|
||||||
CREATE FUNCTION cast_to_posttype(text) RETURNS posttype LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN CASE WHEN $1::posttype IS NULL THEN 'MISC' ELSE $1::posttype END;
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
-- create tables
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "user_sessions" (
|
|
||||||
"sid" varchar NOT NULL,
|
|
||||||
"sess" json NOT NULL,
|
|
||||||
"expire" timestamp(6) NOT NULL,
|
|
||||||
PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE
|
|
||||||
) WITH (OIDS=FALSE);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name varchar(128) NOT NULL,
|
|
||||||
handle varchar(128) UNIQUE NOT NULL,
|
|
||||||
password varchar(1024) NOT NULL,
|
|
||||||
email varchar(128) UNIQUE NOT NULL,
|
|
||||||
greenpoints INTEGER DEFAULT 0,
|
|
||||||
joined_at TIMESTAMP DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS posts (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
upvotes INTEGER DEFAULT 0,
|
|
||||||
downvotes INTEGER DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT now(),
|
|
||||||
content text,
|
|
||||||
author SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
type posttype NOT NULL DEFAULT 'MISC'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS votes (
|
|
||||||
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
item_id BIGSERIAL REFERENCES posts (id) ON DELETE CASCADE,
|
|
||||||
vote_type votetype DEFAULT 'DOWNVOTE',
|
|
||||||
PRIMARY KEY (user_id, item_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS events (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
time TIMESTAMP,
|
|
||||||
owner SERIAL REFERENCES users (id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS event_members (
|
|
||||||
event BIGSERIAL REFERENCES events (id),
|
|
||||||
member SERIAL REFERENCES users (id),
|
|
||||||
PRIMARY KEY (event, member)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chats (
|
|
||||||
id BIGSERIAL PRIMARY KEY
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
||||||
chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE,
|
|
||||||
author SERIAL REFERENCES users (id) ON DELETE SET NULL,
|
|
||||||
content VARCHAR(1024) NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT now(),
|
|
||||||
PRIMARY KEY (chat, author, created_at)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chat_members (
|
|
||||||
chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE,
|
|
||||||
member SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
PRIMARY KEY (chat, member)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_friends (
|
|
||||||
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
friend_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
PRIMARY KEY (user_id, friend_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS requests (
|
|
||||||
sender SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
receiver SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
type requesttype DEFAULT 'FRIENDREQUEST',
|
|
||||||
PRIMARY KEY (sender, receiver, type)
|
|
||||||
);
|
|
||||||
|
|
||||||
END $$;
|
|
@ -1,19 +0,0 @@
|
|||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS votes
|
|
||||||
ADD COLUMN IF NOT EXISTS vote_type votetype DEFAULT 'UPVOTE',
|
|
||||||
ALTER COLUMN vote_type TYPE votetype USING cast_to_votetype(vote_type::text),
|
|
||||||
ALTER COLUMN vote_type DROP DEFAULT,
|
|
||||||
ALTER COLUMN vote_type SET DEFAULT 'UPVOTE';
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS posts
|
|
||||||
ALTER COLUMN type TYPE posttype USING cast_to_posttype(type::text),
|
|
||||||
ALTER COLUMN type DROP DEFAULT,
|
|
||||||
ALTER COLUMN type SET DEFAULT 'MISC',
|
|
||||||
DROP COLUMN IF EXISTS upvotes,
|
|
||||||
DROP COLUMN IF EXISTS downvotes;
|
|
||||||
|
|
||||||
ALTER TABLE requests
|
|
||||||
ADD COLUMN IF NOT EXISTS type requesttype DEFAULT 'FRIENDREQUEST';
|
|
||||||
|
|
||||||
END $$;
|
|
Loading…
Reference in New Issue