diff --git a/src/app.ts b/src/app.ts index 98964fe..d0e28dd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -24,12 +24,39 @@ import {UploadRoute} from "./routes/UploadRoute"; const SequelizeStore = require("connect-session-sequelize")(session.Store); const logger = globals.logger; +/** + * The main entry class for each cluster worker + */ class App { + + /** + * The corresponding express application + */ public app: express.Application; + + /** + * An instance of the socket.io server for websockets + */ public io: socketIo.Server; + + /** + * An instance of the http server where the site is served + */ public server: http.Server; + + /** + * The path to the public folder that is served statically + */ public readonly publicPath: string; + + /** + * The id of the worker + */ public readonly id?: number; + + /** + * The instance of sequelize for ORM + */ public readonly sequelize: Sequelize; constructor(id?: number) { diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index 032edb8..34a760c 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -10,9 +10,7 @@ import {InternalEvents} from "../lib/InternalEvents"; import * as models from "../lib/models"; import {is} from "../lib/regex"; -class Resolver { - -} +// tslint:disable:completed-docs /** * Returns the resolvers for the graphql api. @@ -212,7 +210,11 @@ export function resolver(req: any, res: any): any { if (req.session.userId) { const post = await models.Post.findByPk(postId); if (post) { - return await post.vote(req.session.userId, type); + const voteType = await post.vote(req.session.userId, type); + return { + post, + voteType, + }; } else { res.status(status.BAD_REQUEST); return new PostNotFoundGqlError(postId); diff --git a/src/graphql/schema.graphql b/src/graphql/schema.graphql index b4c2162..954ca8b 100644 --- a/src/graphql/schema.graphql +++ b/src/graphql/schema.graphql @@ -17,14 +17,11 @@ type Query { "returns the request object for its id" getRequest(requestId: ID!): Request - "DEPRECATED! Find a user by user name or handle" - findUser(first: Int = 20, offset: Int = 0, name: String, handle: String): [User] - "searches for users, groups, events, posts and returns a search result" search(query: String!, first: Int = 20, offset: Int = 0): SearchResult! "returns the post filtered by the sort type with pagination." - getPosts(first: Int=20, offset: Int=0, sort: SortType = NEW): [Post] + getPosts(first: Int=20, offset: Int=0, sort: SortType = NEW): [Post!]! "returns all activities" getActivities: [Activity] @@ -38,10 +35,10 @@ type Mutation { acceptCookies: Boolean "Login of the user. The passwordHash should be a sha512 hash of the password." - login(email: String!, passwordHash: String!): Profile + login(email: String!, passwordHash: String!): Profile! "Registers the user." - register(username: String, email: String, passwordHash: String): Profile + register(username: String, email: String, passwordHash: String): Profile! "Sets the user settings to the specified settings string. The settings parameter should be a valid yaml." setUserSettings(settings: String!): String! @@ -50,22 +47,22 @@ type Mutation { logout: Boolean "Upvote/downvote a Post" - vote(postId: ID!, type: VoteType!): VoteType + vote(postId: ID!, type: VoteType!): VoteResult "Report the post" - report(postId: ID!): Boolean + report(postId: ID!): Boolean! "send a request" sendRequest(receiver: ID!, type: RequestType): Request "lets you accept a request for a given request id" - acceptRequest(sender: ID!, type: RequestType): Boolean + acceptRequest(sender: ID!, type: RequestType): Boolean! "lets you deny a request for a given request id" - denyRequest(sender: ID!, type: RequestType): Boolean + denyRequest(sender: ID!, type: RequestType): Boolean! "removes a friend" - removeFriend(friendId: ID!): Boolean + removeFriend(friendId: ID!): Boolean! "send a message in a Chatroom" sendMessage(chatId: ID!, content: String!): ChatMessage @@ -95,7 +92,7 @@ type Mutation { removeGroupAdmin(groupId: ID!, userId: ID!): Group "Creates a new event with a epoch due date on a group." - createEvent(name: String, dueDate: String, groupId: ID!): Event + createEvent(name: String, dueDate: String, groupId: ID!): Event! "Joins a event." joinEvent(eventId: ID!): Event @@ -124,25 +121,25 @@ interface UserData { postCount: Int! "returns a given number of posts of a user" - posts(first: Int=10, offset: Int=0): [Post] + posts(first: Int=10, offset: Int=0): [Post!]! "creation date of the user account" joinedAt: String! "all friends of the user" - friends(first: Int=10, offset: Int=0): [User] + friends(first: Int=10, offset: Int=0): [User!]! "The number of friends the user has" friendCount: Int! "The groups the user has joined" - groups(first: Int=10, offset: Int=0): [Group] + groups(first: Int=10, offset: Int=0): [Group!]! "The number of groups the user has joined" groupCount: Int! "The events the user is participating in" - events(first: Int=10, offset: Int=0): [Event] + events(first: Int=10, offset: Int=0): [Event!]! "The number of events the user is participating in" eventCount: Int! @@ -172,7 +169,7 @@ type User implements UserData{ numberOfPosts: Int! "returns a given number of posts of a user" - posts(first: Int=10, offset: Int): [Post] + posts(first: Int=10, offset: Int): [Post!]! "the number of posts the user has created" postCount: Int! @@ -181,7 +178,7 @@ type User implements UserData{ joinedAt: String! "all friends of the user" - friends(first: Int=10, offset: Int=0): [User] + friends(first: Int=10, offset: Int=0): [User!]! "The number of friends the user has" friendCount: Int! @@ -190,13 +187,13 @@ type User implements UserData{ points: Int! "the groups the user has joined" - groups(first: Int=10, offset: Int=0): [Group] + groups(first: Int=10, offset: Int=0): [Group!]! "The numbef of groups the user has joined" groupCount: Int! "The events the user is participating in" - events(first: Int=10, offset: Int=0): [Event] + events(first: Int=10, offset: Int=0): [Event!]! "The number of events the user is participating in" eventCount: Int! @@ -264,7 +261,7 @@ type Profile implements UserData { groupCount: Int! "The events the user is participating in" - events(first: Int=10, offset: Int=0): [Event] + events(first: Int=10, offset: Int=0): [Event!]! "The number of events the user is participating in" eventCount: Int! @@ -310,7 +307,7 @@ type Post { userVote(userId: ID!): VoteType "if the post can be deleted by the specified user" - deletable(userId: ID!): Boolean + deletable(userId: ID!): Boolean! "the activity that belongs to the post" activity: Activity @@ -338,10 +335,10 @@ type ChatRoom { namespace: String "the members of the chatroom" - members(first: Int=10, offset: Int=0): [User!] + members(first: Int=10, offset: Int=0): [User!]! "return a specfic range of messages posted in the chat" - messages(first: Int = 10, offset: Int, containing: String): [ChatMessage]! + messages(first: Int = 10, offset: Int): [ChatMessage!]! "id of the chat" id: ID! @@ -375,22 +372,22 @@ type Group { name: String! "the creator of the group" - creator: User + creator: User! "all admins of the group" - admins(first: Int=10, offset: Int=0): [User]! + admins(first: Int=10, offset: Int=0): [User!]! "the members of the group with pagination" - members(first: Int = 10, offset: Int = 0): [User]! + members(first: Int = 10, offset: Int = 0): [User!]! "the groups chat" - chat: ChatRoom + chat: ChatRoom! "the events of the group" events(first: Int=10, offset: Int=0): [Event!]! "If the user with the specified id has joined the group" - joined(userId: Int!): Boolean + joined(userId: Int!): Boolean! } type Event { @@ -437,6 +434,16 @@ type SearchResult { events: [Event!]! } +"The result of voting on a post" +type VoteResult { + + "The type of vote that was performed" + voteType: VoteType + + "The post the vote was performed on" + post: Post! +} + "An activity that grants points" type Activity { diff --git a/src/index.ts b/src/index.ts index 119f82c..5099ca5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,17 +3,6 @@ import * as cluster from "cluster"; import App from "./app"; const numCPUs = require("os").cpus().length; -interface IResourceUsage { - mem: {rss: number, heapTotal: number, heapUsed: number, external: number}; - cpu: {user: number, system: number}; -} - -interface IClusterData { - reqCount: number; - workerCount: () => number; - workerRes: {[key: string]: IResourceUsage}; -} - if (cluster.isMaster) { console.log(`[CLUSTER-M] Master ${process.pid} is running`); diff --git a/src/lib/Route.ts b/src/lib/Route.ts index b0ad0ef..a3e1a1e 100644 --- a/src/lib/Route.ts +++ b/src/lib/Route.ts @@ -8,12 +8,31 @@ import {Namespace, Server} from "socket.io"; */ abstract class Route { + /** + * The express router belonging to the route + */ public router?: Router; + + /** + * An instance of socket io for the route + */ protected io?: Server; + + /** + * The namespace of the websocket for the route + */ protected ions?: Namespace; + /** + * An asynchronous init function + * @param params + */ public abstract async init(...params: any): Promise; + /** + * An asynchronous destroy function + * @param params + */ public abstract async destroy(...params: any): Promise; } diff --git a/src/lib/dataAccess.ts b/src/lib/dataAccess.ts index 48c0f60..6dcf7e9 100644 --- a/src/lib/dataAccess.ts +++ b/src/lib/dataAccess.ts @@ -17,6 +17,8 @@ import {InternalEvents} from "./InternalEvents"; import {Activity} from "./models"; import * as models from "./models"; +// tslint:disable:completed-docs + /** * Generates a new handle from the username and a base64 string of the current time. * @param username @@ -39,7 +41,8 @@ namespace dataaccess { let sequelize: Sequelize; /** - * Initializes everything that needs to be initialized asynchronous. + * An asynchronous init method for sequelize + * @param seq */ export async function init(seq: Sequelize): Promise { sequelize = seq; diff --git a/src/lib/errors/ActivityNotFoundError.ts b/src/lib/errors/ActivityNotFoundError.ts index 839afa6..fb0f5c9 100644 --- a/src/lib/errors/ActivityNotFoundError.ts +++ b/src/lib/errors/ActivityNotFoundError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when an activity was not found. + */ export class ActivityNotFoundError extends BaseError { constructor(id: number) { super(`The activity with the id ${id} could not be found.`); diff --git a/src/lib/errors/BaseError.ts b/src/lib/errors/BaseError.ts index 310fdd3..b4ad153 100644 --- a/src/lib/errors/BaseError.ts +++ b/src/lib/errors/BaseError.ts @@ -4,6 +4,9 @@ import {GraphQLError} from "graphql"; * Base error class. */ export class BaseError extends Error { + /** + * The graphql error with a frontend error message + */ public readonly graphqlError: GraphQLError; constructor(message?: string, friendlyMessage?: string) { diff --git a/src/lib/errors/ChatNotFoundError.ts b/src/lib/errors/ChatNotFoundError.ts index d38e709..17042dc 100644 --- a/src/lib/errors/ChatNotFoundError.ts +++ b/src/lib/errors/ChatNotFoundError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when the chatroom doesn't exist + */ export class ChatNotFoundError extends BaseError { constructor(chatId: number) { super(`Chat with id ${chatId} not found.`); diff --git a/src/lib/errors/DuplicatedRequestError.ts b/src/lib/errors/DuplicatedRequestError.ts index 404b3c0..20b6ae5 100644 --- a/src/lib/errors/DuplicatedRequestError.ts +++ b/src/lib/errors/DuplicatedRequestError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a request of a specific type already exists + */ export class DuplicatedRequestError extends BaseError { constructor() { super(`Request already exists.`); diff --git a/src/lib/errors/EmailAlreadyRegisteredError.ts b/src/lib/errors/EmailAlreadyRegisteredError.ts index 7551f02..e13d89a 100644 --- a/src/lib/errors/EmailAlreadyRegisteredError.ts +++ b/src/lib/errors/EmailAlreadyRegisteredError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when the provided email for registering is already registered with a user + */ export class EmailAlreadyRegisteredError extends BaseError { constructor(email: string) { super(`A user for '${email}' does already exist.`); diff --git a/src/lib/errors/GroupAlreadyExistsError.ts b/src/lib/errors/GroupAlreadyExistsError.ts index 02dffe2..57a2313 100644 --- a/src/lib/errors/GroupAlreadyExistsError.ts +++ b/src/lib/errors/GroupAlreadyExistsError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a group already exists on creation request + */ export class GroupAlreadyExistsError extends BaseError { constructor(name: string) { super(`A group with the name "${name}" already exists.`); diff --git a/src/lib/errors/GroupNotFoundError.ts b/src/lib/errors/GroupNotFoundError.ts index 5010d4e..f63e119 100644 --- a/src/lib/errors/GroupNotFoundError.ts +++ b/src/lib/errors/GroupNotFoundError.ts @@ -1,8 +1,10 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a group was not found for a specified id + */ export class GroupNotFoundError extends BaseError { constructor(groupId: number) { super(`Group ${groupId} not found!`); } - } diff --git a/src/lib/errors/InvalidLoginError.ts b/src/lib/errors/InvalidLoginError.ts index 54e6227..c6651d0 100644 --- a/src/lib/errors/InvalidLoginError.ts +++ b/src/lib/errors/InvalidLoginError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a user provides invalid login data for a login request + */ export class InvalidLoginError extends BaseError { constructor(email: (string)) { super(`Invalid login data for ${email}.`); diff --git a/src/lib/errors/NoActionSpecifiedError.ts b/src/lib/errors/NoActionSpecifiedError.ts index 3ee3eb4..f18fed4 100644 --- a/src/lib/errors/NoActionSpecifiedError.ts +++ b/src/lib/errors/NoActionSpecifiedError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when no action was specified on a group membership change + */ export class NoActionSpecifiedError extends BaseError { constructor(actions?: any) { if (actions) { diff --git a/src/lib/errors/RequestNotFoundError.ts b/src/lib/errors/RequestNotFoundError.ts index 112dd8f..cd94fc5 100644 --- a/src/lib/errors/RequestNotFoundError.ts +++ b/src/lib/errors/RequestNotFoundError.ts @@ -1,9 +1,11 @@ import dataaccess from "../dataAccess"; import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a request for a sender, receiver and type was not found + */ export class RequestNotFoundError extends BaseError { constructor(sender: number, receiver: number, type: dataaccess.RequestType) { super(`Request with sender '${sender}' and receiver '${receiver}' of type '${type}' not found.`); } - } diff --git a/src/lib/errors/UserNotFoundError.ts b/src/lib/errors/UserNotFoundError.ts index ccddc54..69724c0 100644 --- a/src/lib/errors/UserNotFoundError.ts +++ b/src/lib/errors/UserNotFoundError.ts @@ -1,5 +1,8 @@ import {BaseError} from "./BaseError"; +/** + * An error that is thrown when a specified user was not found + */ export class UserNotFoundError extends BaseError { constructor(username: (string|number)) { super(`User ${username} not found!`); diff --git a/src/lib/errors/graphqlErrors.ts b/src/lib/errors/graphqlErrors.ts index 7098aee..c336ae0 100644 --- a/src/lib/errors/graphqlErrors.ts +++ b/src/lib/errors/graphqlErrors.ts @@ -1,17 +1,26 @@ import {GraphQLError} from "graphql"; +/** + * An error for the frontend that is thrown when the user is not logged in + */ export class NotLoggedInGqlError extends GraphQLError { constructor() { super("Not logged in"); } } +/** + * An error for the frontend that is thrown when a post was not found + */ export class PostNotFoundGqlError extends GraphQLError { constructor(postId: number) { super(`Post '${postId}' not found!`); } } +/** + * An error for the forntend that is thrown when a group was not found + */ export class GroupNotFoundGqlError extends GraphQLError { constructor(groupId: number) { super(`Group '${groupId}' not found!`); diff --git a/src/lib/models/Activity.ts b/src/lib/models/Activity.ts index 8b56ad2..448ffec 100644 --- a/src/lib/models/Activity.ts +++ b/src/lib/models/Activity.ts @@ -1,18 +1,30 @@ import * as sqz from "sequelize"; -import {Column, ForeignKey, Model, NotNull, Table, Unique} from "sequelize-typescript"; +import {Column, Model, NotNull, Table, Unique} from "sequelize-typescript"; +/** + * Represents an environmental friendly activity that provides points to level up + */ @Table({underscored: true}) export class Activity extends Model { + /** + * The name of the Activity + */ @Unique @NotNull @Column({type: sqz.STRING(128), allowNull: false, unique: true}) public name: string; + /** + * The description of the activity to describe what exactly has to be done + */ @NotNull @Column({type: sqz.TEXT, allowNull: false}) public description: string; + /** + * The points one can get by completing the activity + */ @Column public points: number; } diff --git a/src/lib/models/ChatMember.ts b/src/lib/models/ChatMember.ts index 1caf9ec..029749a 100644 --- a/src/lib/models/ChatMember.ts +++ b/src/lib/models/ChatMember.ts @@ -2,13 +2,22 @@ import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {ChatRoom} from "./ChatRoom"; import {User} from "./User"; +/** + * Represents a member of a chat + */ @Table({underscored: true}) export class ChatMember extends Model { + /** + * The id of the user + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public userId: number; + /** + * The id of the chatroom + */ @ForeignKey(() => ChatRoom) @NotNull @Column({allowNull: false}) diff --git a/src/lib/models/ChatMessage.ts b/src/lib/models/ChatMessage.ts index 520b63b..b47217b 100644 --- a/src/lib/models/ChatMessage.ts +++ b/src/lib/models/ChatMessage.ts @@ -4,40 +4,71 @@ import markdown from "../markdown"; import {ChatRoom} from "./ChatRoom"; import {User} from "./User"; +/** + * A single chat message in a chatroom + */ @Table({underscored: true}) export class ChatMessage extends Model { + /** + * The content of a message in markdown utf-8 format + */ @NotNull @Column({type: sqz.STRING(512), allowNull: false}) public content: string; + /** + * The id of the chatroom the message was sent in + */ @ForeignKey(() => ChatRoom) @NotNull @Column({allowNull: false}) public chatId: number; + /** + * The id of the author of the message + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public authorId: number; + /** + * The chatroom the message belongs to + */ @BelongsTo(() => ChatRoom, "chatId") public rChat: ChatRoom; + /** + * The author the message belongs to + */ @BelongsTo(() => User, "authorId") public rAuthor: User; + /** + * The date when the message was created + */ @CreatedAt public createdAt: Date; + /** + * Returns the chatroom of the message + */ public async chat(): Promise { return await this.$get("rChat") as ChatRoom; } + /** + * Returns the author of the message + */ public async author(): Promise { return await this.$get("rAuthor") as User; } + /** + * Returns the rendered html content of the message. + * Rendered by markdown.it + */ public get htmlContent(): string { return markdown.renderInline(this.getDataValue("content")); } diff --git a/src/lib/models/ChatRoom.ts b/src/lib/models/ChatRoom.ts index 3f4fe30..f521387 100644 --- a/src/lib/models/ChatRoom.ts +++ b/src/lib/models/ChatRoom.ts @@ -3,25 +3,49 @@ import {ChatMember} from "./ChatMember"; import {ChatMessage} from "./ChatMessage"; import {User} from "./User"; +/** + * The chatroom model + */ @Table({underscored: true}) export class ChatRoom extends Model { + + /** + * The members of the chatroom + */ @BelongsToMany(() => User, () => ChatMember) public rMembers: User[]; + /** + * The messages in the chatroom + */ @HasMany(() => ChatMessage, "chatId") public rMessages: ChatMessage[]; + /** + * The date the chatroom was created at + */ @CreatedAt public readonly createdAt!: Date; + /** + * Returns the members of the chatroom + */ public async members(): Promise { return await this.$get("rMembers") as User[]; } - public async messages(): Promise { - return await this.$get("rMessages") as ChatMessage[]; + /** + * Returns the messages that have been sent in the chatroom + */ + public async messages({first, offset}: {first: number, offset: number}): Promise { + const limit = first ?? 10; + offset = offset ?? 0; + return await this.$get("rMessages", {limit, offset}) as ChatMessage[]; } + /** + * Returns the namespace of the websocket of the chatroom + */ public get namespace(): string { return "/chats/" + this.getDataValue("id"); } diff --git a/src/lib/models/Event.ts b/src/lib/models/Event.ts index 37858c8..cd98388 100644 --- a/src/lib/models/Event.ts +++ b/src/lib/models/Event.ts @@ -3,24 +3,43 @@ import {EventParticipant} from "./EventParticipant"; import {Group} from "./Group"; import {User} from "./User"; +/** + * Represents an event + */ @Table({underscored: true}) export class Event extends Model { + + /** + * The name of the event + */ @NotNull @Column({allowNull: false}) public name: string; + /** + * The date the event takes place + */ @NotNull @Column({allowNull: false}) public dueDate: Date; + /** + * The group id the event belongs to + */ @NotNull @ForeignKey(() => Group) @Column({allowNull: false}) public groupId: number; + /** + * The group the event belongs to + */ @BelongsTo(() => Group, "groupId") public rGroup: Group; + /** + * The participants in the event + */ @BelongsToMany(() => User, () => EventParticipant) public rParticipants: User[]; diff --git a/src/lib/models/EventParticipant.ts b/src/lib/models/EventParticipant.ts index 8c7daa5..cff5037 100644 --- a/src/lib/models/EventParticipant.ts +++ b/src/lib/models/EventParticipant.ts @@ -1,14 +1,24 @@ -import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; +import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {Event} from "./Event"; import {User} from "./User"; +/** + * A single participant in an event + */ @Table({underscored: true}) export class EventParticipant extends Model { + + /** + * The id of the participating user + */ @NotNull @ForeignKey(() => User) @Column({allowNull: false}) public userId: number; + /** + * The id of the event + */ @NotNull @ForeignKey(() => Event) @Column({allowNull: false}) diff --git a/src/lib/models/Friendship.ts b/src/lib/models/Friendship.ts index b28d4f4..d767748 100644 --- a/src/lib/models/Friendship.ts +++ b/src/lib/models/Friendship.ts @@ -1,15 +1,24 @@ import {Column, ForeignKey, Model, NotNull, PrimaryKey, Table} from "sequelize-typescript"; import {User} from "./User"; +/** + * A friendship between two users + */ @Table({underscored: true}) export class Friendship extends Model { + /** + * The id of the first user + */ @ForeignKey(() => User) @PrimaryKey @NotNull @Column({allowNull: false}) public userId: number; + /** + * The id of the second user + */ @ForeignKey(() => User) @PrimaryKey @NotNull diff --git a/src/lib/models/Group.ts b/src/lib/models/Group.ts index 8b3fd7f..2b6b282 100644 --- a/src/lib/models/Group.ts +++ b/src/lib/models/Group.ts @@ -15,35 +15,63 @@ import {GroupAdmin} from "./GroupAdmin"; import {GroupMember} from "./GroupMember"; import {User} from "./User"; +/** + * A single group with members + */ @Table({underscored: true}) export class Group extends Model { + + /** + * The name of the group + */ @NotNull @Unique @Column({allowNull: false, unique: true}) public name: string; + /** + * The id of the user who created the group + */ @NotNull @ForeignKey(() => User) @Column({allowNull: false}) public creatorId: number; + /** + * The id of the chat that belongs to the group + */ @NotNull @ForeignKey(() => ChatRoom) @Column({allowNull: false}) public chatId: number; + /** + * The creator of the group + */ @BelongsTo(() => User, "creatorId") public rCreator: User; + /** + * The admins of the group + */ @BelongsToMany(() => User, () => GroupAdmin) public rAdmins: User[]; + /** + * The members of the group + */ @BelongsToMany(() => User, () => GroupMember) public rMembers: User[]; + /** + * The chatroom of the group + */ @BelongsTo(() => ChatRoom) public rChat: ChatRoom; + /** + * The events that were created for the group + */ @HasMany(() => Event, "groupId") public rEvents: Event[]; diff --git a/src/lib/models/GroupAdmin.ts b/src/lib/models/GroupAdmin.ts index 9c48167..7d65f7e 100644 --- a/src/lib/models/GroupAdmin.ts +++ b/src/lib/models/GroupAdmin.ts @@ -2,13 +2,23 @@ import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {Group} from "./Group"; import {User} from "./User"; +/** + * A single admin of a group + */ @Table({underscored: true}) export class GroupAdmin extends Model { + + /** + * The id of the user + */ @NotNull @ForeignKey(() => User) @Column({allowNull: false}) public userId: number; + /** + * The id of the group + */ @NotNull @ForeignKey(() => Group) @Column({allowNull: false}) diff --git a/src/lib/models/GroupMember.ts b/src/lib/models/GroupMember.ts index 3334348..4e30b75 100644 --- a/src/lib/models/GroupMember.ts +++ b/src/lib/models/GroupMember.ts @@ -2,13 +2,23 @@ import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {Group} from "./Group"; import {User} from "./User"; +/** + * A single member of a group + */ @Table({underscored: true}) export class GroupMember extends Model { + + /** + * The id of the user + */ @NotNull @ForeignKey(() => User) @Column({allowNull: false}) public userId: number; + /** + * The id of the group + */ @NotNull @ForeignKey(() => Group) @Column({allowNull: false}) diff --git a/src/lib/models/Post.ts b/src/lib/models/Post.ts index 39f2ebc..ed41e56 100644 --- a/src/lib/models/Post.ts +++ b/src/lib/models/Post.ts @@ -5,30 +5,56 @@ import {Activity} from "./Activity"; import {PostVote, VoteType} from "./PostVote"; import {User} from "./User"; +/** + * A single post of a user + */ @Table({underscored: true}) export class Post extends Model { + + /** + * The markdown formatted utf-8 content of the post + */ @NotNull @Column({type: sqz.STRING(2048), allowNull: false}) public content: string; + /** + * The id of the post author + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public authorId: number; + /** + * The id of the activiy of the post if one was provided during creation + */ @ForeignKey(() => Activity) @Column({allowNull: true}) public activityId: number; + /** + * The author of the post + */ @BelongsTo(() => User, "authorId") public rAuthor: User; + /** + * The activiy of the post + */ @BelongsTo(() => Activity, "activityId") public rActivity?: Activity; + /** + * The votes that were performed on the post + */ @BelongsToMany(() => User, () => PostVote) + // tslint:disable-next-line:completed-docs public rVotes: Array; + /** + * The date the post was created at + */ @CreatedAt public readonly createdAt!: Date; diff --git a/src/lib/models/PostVote.ts b/src/lib/models/PostVote.ts index 1b2d019..4c4c789 100644 --- a/src/lib/models/PostVote.ts +++ b/src/lib/models/PostVote.ts @@ -3,22 +3,38 @@ import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {Post} from "./Post"; import {User} from "./User"; +/** + * An enum that represents all possible types of votes + */ export enum VoteType { UPVOTE = "UPVOTE", DOWNVOTE = "DOWNVOTE", } +/** + * A single vote on a post + */ @Table({underscored: true}) export class PostVote extends Model { + + /** + * The type of vote (UPVOTE/DOWNVOTE) + */ @NotNull @Column({type: sqz.ENUM, values: ["UPVOTE", "DOWNVOTE"], defaultValue: "UPVOTE", allowNull: false}) public voteType: VoteType; + /** + * The id of the user that performed the vote + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public userId: number; + /** + * The id of the post the vote was performed on + */ @ForeignKey(() => Post) @NotNull @Column({allowNull: false}) diff --git a/src/lib/models/Request.ts b/src/lib/models/Request.ts index c1faf8d..3e0722d 100644 --- a/src/lib/models/Request.ts +++ b/src/lib/models/Request.ts @@ -2,14 +2,24 @@ import * as sqz from "sequelize"; import {BelongsTo, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {User} from "./User"; +/** + * An enum that represents all possible types of requests + */ export enum RequestType { FRIENDREQUEST = "FRIENDREQUEST", GROUPINVITE = "GROUPINVITE", EVENTINVITE = "EVENTINVITE", } +/** + * A single request for a friendship, group invide, event invite + */ @Table({underscored: true}) export class Request extends Model { + + /** + * The type of the request + */ @NotNull @Column({ allowNull: false, @@ -19,19 +29,31 @@ export class Request extends Model { }) public requestType: RequestType; + /** + * The id of the user who sent the request + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public senderId: number; + /** + * The user who sent the request + */ @BelongsTo(() => User, "senderId") public rSender: User; + /** + * The id of the user who received the request + */ @ForeignKey(() => User) @NotNull @Column({allowNull: false}) public receiverId: number; + /** + * The user who received the request + */ @BelongsTo(() => User, "receiverId") public rReceiver: User; diff --git a/src/lib/models/User.ts b/src/lib/models/User.ts index 3ec3dc1..963fabb 100644 --- a/src/lib/models/User.ts +++ b/src/lib/models/User.ts @@ -26,87 +26,163 @@ import {Post} from "./Post"; import {PostVote} from "./PostVote"; import {Request, RequestType} from "./Request"; +/** + * A single user + */ @Table({underscored: true}) export class User extends Model { + + /** + * The name of the user + */ @NotNull @Column({type: sqz.STRING(128), allowNull: false}) public username: string; + /** + * The handle of the user + */ @NotNull @Unique @Column({type: sqz.STRING(128), allowNull: false, unique: true}) public handle: string; + /** + * The email address of the user + */ @Unique @NotNull @Column({type: sqz.STRING(128), allowNull: false, unique: true}) public email: string; + /** + * The password hash of the user + */ @NotNull @Column({type: sqz.STRING(128), allowNull: false}) public password: string; + /** + * The ranking points of the user + */ @NotNull @Column({defaultValue: 0, allowNull: false}) public rankpoints: number; + /** + * The JSON-Frontend settings of the user to provide a way to store custom settings in the backend + */ @NotNull @Column({defaultValue: {}, allowNull: false, type: sqz.JSON}) public frontendSettings: any; + /** + * The auth token for bearer authentication + */ @Unique @Column({defaultValue: uuidv4, unique: true}) public authToken: string; + /** + * The date and time the auth token expires + */ @Column({defaultValue: () => Date.now() + 7200000}) public authExpire: Date; + /** + * A flag if the user is a site admin + */ @NotNull @Column({defaultValue: false, allowNull: false}) public isAdmin: boolean; + /** + * The url of the users profile picture + */ @Column({type: sqz.STRING(512)}) public profilePicture: string; + /** + * The friends of the user + */ @BelongsToMany(() => User, () => Friendship, "userId") public rFriends: User[]; + /** + * The friends of the user + */ @BelongsToMany(() => User, () => Friendship, "friendId") public rFriendOf: User[]; + /** + * The votes the user performed + */ @BelongsToMany(() => Post, () => PostVote) public votes: Array; + /** + * The chatrooms the user has joined + */ @BelongsToMany(() => ChatRoom, () => ChatMember) public rChats: ChatRoom[]; + /** + * The group the user is an admin in + */ @BelongsToMany(() => Group, () => GroupAdmin) public rAdministratedGroups: Group[]; + /** + * The events the user has joined + */ @BelongsToMany(() => Event, () => EventParticipant) public rEvents: Event[]; + /** + * The groups the user has joined + */ @BelongsToMany(() => Group, () => GroupMember) public rGroups: Group[]; + /** + * The posts the user has created + */ @HasMany(() => Post, "authorId") public rPosts: Post[]; + /** + * The requests the user has sent + */ @HasMany(() => Request, "senderId") public rSentRequests: Request[]; + /** + * The requests the user has received + */ @HasMany(() => Request, "receiverId") public rReceivedRequests: Request[]; + /** + * The messages the user has sent in a chatroom + */ @HasMany(() => ChatMessage, "authorId") public messages: ChatMessage[]; + /** + * The groups the user has created + */ @HasMany(() => Group, "creatorId") public rCreatedGroups: Group[]; + /** + * The date the account was created + */ @CreatedAt public readonly createdAt!: Date; + /** + * The date of the last change to the user + */ @UpdatedAt public readonly updatedAt!: Date; diff --git a/src/routes/UploadRoute.ts b/src/routes/UploadRoute.ts index 1ecb516..6f526b0 100644 --- a/src/routes/UploadRoute.ts +++ b/src/routes/UploadRoute.ts @@ -43,6 +43,9 @@ export class UploadRoute extends Route { return hash.digest("hex"); } + /** + * The directory where the uploaded data will be saved in + */ public readonly dataDir: string; constructor(private publicPath: string) { diff --git a/tslint.json b/tslint.json index 6341bcb..28549b8 100644 --- a/tslint.json +++ b/tslint.json @@ -22,7 +22,14 @@ "no-namespace": false, "no-internal-module": false, "max-classes-per-file": false, - "no-var-requires": false + "no-var-requires": false, + "jsdoc-format": [true, "check-multiline-start"], + "completed-docs": [true, "classes", "enums", "methods", { + "properties": { + "privacies": "all", + "locations": "instance" + } + }] }, "jsRules": { "max-line-length": {