Added angular config option
- (converted to unix line endings) - added angular index.html config to run angular appspull/2/head
parent
b17fca2590
commit
d305261a0a
@ -1,11 +1,11 @@
|
||||
.nyc_output/
|
||||
coverage/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
test/*.log
|
||||
dist
|
||||
.idea
|
||||
config.yaml
|
||||
sqz-force
|
||||
greenvironment.db
|
||||
logs
|
||||
.nyc_output/
|
||||
coverage/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
test/*.log
|
||||
dist
|
||||
.idea
|
||||
config.yaml
|
||||
sqz-force
|
||||
greenvironment.db
|
||||
logs
|
||||
|
@ -1,19 +1,19 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Graphql Schema
|
||||
- default-config file and generation of config file on startup
|
||||
- DTOs
|
||||
- Home Route
|
||||
- session management
|
||||
- Sequelize models and integration
|
||||
- Sequelize-typescript integration
|
||||
- error pages
|
||||
- pagination for most list types
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Graphql Schema
|
||||
- default-config file and generation of config file on startup
|
||||
- DTOs
|
||||
- Home Route
|
||||
- session management
|
||||
- Sequelize models and integration
|
||||
- Sequelize-typescript integration
|
||||
- error pages
|
||||
- pagination for most list types
|
||||
|
@ -1,11 +1,11 @@
|
||||
FROM node:current-alpine
|
||||
|
||||
COPY . /home/node/green
|
||||
WORKDIR /home/node/green
|
||||
RUN npm install -g gulp
|
||||
RUN npm install --save-dev
|
||||
RUN npm rebuild node-sass
|
||||
RUN gulp
|
||||
COPY . .
|
||||
EXPOSE 8080
|
||||
CMD ["npm" , "run"]
|
||||
FROM node:current-alpine
|
||||
|
||||
COPY . /home/node/green
|
||||
WORKDIR /home/node/green
|
||||
RUN npm install -g gulp
|
||||
RUN npm install --save-dev
|
||||
RUN npm rebuild node-sass
|
||||
RUN gulp
|
||||
COPY . .
|
||||
EXPOSE 8080
|
||||
CMD ["npm" , "run"]
|
||||
|
@ -1,11 +1,11 @@
|
||||
# greenvironment-server
|
||||
|
||||
Server of the greenvironment social network.
|
||||
|
||||
## Install
|
||||
|
||||
You need to install a nodejs runtime to run the greenvironment server.
|
||||
Then you need to install all requirements. To do so, open a terminal in the
|
||||
greenvironment project folder and execute "npm i". You can build the project by
|
||||
executing "gulp" in the terminal. To run the server you need
|
||||
to execute "node ./dist".
|
||||
# greenvironment-server
|
||||
|
||||
Server of the greenvironment social network.
|
||||
|
||||
## Install
|
||||
|
||||
You need to install a nodejs runtime to run the greenvironment server.
|
||||
Then you need to install all requirements. To do so, open a terminal in the
|
||||
greenvironment project folder and execute "npm i". You can build the project by
|
||||
executing "gulp" in the terminal. To run the server you need
|
||||
to execute "node ./dist".
|
||||
|
@ -1,11 +1,11 @@
|
||||
version: "3"
|
||||
services:
|
||||
greenvironment:
|
||||
build: .
|
||||
user: "root"
|
||||
working_dir: /home/node/green
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
ports:
|
||||
- "8080:8080"
|
||||
command: "npm start"
|
||||
version: "3"
|
||||
services:
|
||||
greenvironment:
|
||||
build: .
|
||||
user: "root"
|
||||
working_dir: /home/node/green
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
ports:
|
||||
- "8080:8080"
|
||||
command: "npm start"
|
||||
|
@ -1,47 +1,47 @@
|
||||
const {src, dest, watch, series, task} = require('gulp');
|
||||
const sass = require('gulp-sass');
|
||||
const ts = require('gulp-typescript');
|
||||
const minify = require('gulp-minify');
|
||||
const del = require('delete');
|
||||
const gulp = require('gulp');
|
||||
|
||||
function clearDist(cb) {
|
||||
del('dist/*', cb);
|
||||
}
|
||||
|
||||
function compileTypescript() {
|
||||
let tsProject = ts.createProject('tsconfig.json');
|
||||
let tsResult = tsProject.src().pipe(tsProject());
|
||||
return tsResult
|
||||
//.pipe(minify())
|
||||
.pipe(dest('dist'));
|
||||
}
|
||||
|
||||
function minifyJs() {
|
||||
return src('src/public/javascripts/**/*.js')
|
||||
.pipe(minify({
|
||||
ext: {
|
||||
src: '-debug.js',
|
||||
min: '.js'
|
||||
}
|
||||
}))
|
||||
.pipe(dest('dist/public/javascripts'));
|
||||
}
|
||||
|
||||
function compileSass() {
|
||||
return src('src/public/stylesheets/sass/**/style.sass')
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(dest('dist/public/stylesheets'));
|
||||
}
|
||||
|
||||
function moveRemaining() {
|
||||
return src(['src/**/*', '!src/**/*.ts', '!src/**/*.sass', '!src/**/*.js'])
|
||||
.pipe(dest('dist'));
|
||||
}
|
||||
|
||||
task('default', series(clearDist, compileTypescript, minifyJs, compileSass, moveRemaining));
|
||||
task('watch', () => {
|
||||
watch('src/public/stylesheets/sass/**/*.sass', compileSass);
|
||||
watch('**/*.js', minifyJs);
|
||||
watch('**/*.ts', compileTypescript);
|
||||
});
|
||||
const {src, dest, watch, series, task} = require('gulp');
|
||||
const sass = require('gulp-sass');
|
||||
const ts = require('gulp-typescript');
|
||||
const minify = require('gulp-minify');
|
||||
const del = require('delete');
|
||||
const gulp = require('gulp');
|
||||
|
||||
function clearDist(cb) {
|
||||
del('dist/*', cb);
|
||||
}
|
||||
|
||||
function compileTypescript() {
|
||||
let tsProject = ts.createProject('tsconfig.json');
|
||||
let tsResult = tsProject.src().pipe(tsProject());
|
||||
return tsResult
|
||||
//.pipe(minify())
|
||||
.pipe(dest('dist'));
|
||||
}
|
||||
|
||||
function minifyJs() {
|
||||
return src('src/public/javascripts/**/*.js')
|
||||
.pipe(minify({
|
||||
ext: {
|
||||
src: '-debug.js',
|
||||
min: '.js'
|
||||
}
|
||||
}))
|
||||
.pipe(dest('dist/public/javascripts'));
|
||||
}
|
||||
|
||||
function compileSass() {
|
||||
return src('src/public/stylesheets/sass/**/style.sass')
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(dest('dist/public/stylesheets'));
|
||||
}
|
||||
|
||||
function moveRemaining() {
|
||||
return src(['src/**/*', '!src/**/*.ts', '!src/**/*.sass', '!src/**/*.js'])
|
||||
.pipe(dest('dist'));
|
||||
}
|
||||
|
||||
task('default', series(clearDist, compileTypescript, minifyJs, compileSass, moveRemaining));
|
||||
task('watch', () => {
|
||||
watch('src/public/stylesheets/sass/**/*.sass', compileSass);
|
||||
watch('**/*.js', minifyJs);
|
||||
watch('**/*.ts', compileTypescript);
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,79 +1,79 @@
|
||||
{
|
||||
"name": "greenvironment-server",
|
||||
"version": "0.1.0",
|
||||
"description": "Server for greenvironment network",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "gulp",
|
||||
"start": "node ./dist/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.trivernis.net/Software_Engineering_I/greenvironment-server.git"
|
||||
},
|
||||
"keywords": [
|
||||
"server",
|
||||
"nodejs",
|
||||
"express"
|
||||
],
|
||||
"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",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.1",
|
||||
"@types/express-graphql": "^0.8.0",
|
||||
"@types/express-session": "^1.15.14",
|
||||
"@types/express-socket.io-session": "^1.3.2",
|
||||
"@types/fs-extra": "^8.0.0",
|
||||
"@types/graphql": "^14.2.3",
|
||||
"@types/http-status": "^0.2.30",
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
"@types/markdown-it": "0.0.9",
|
||||
"@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",
|
||||
"gulp-minify": "^3.1.0",
|
||||
"gulp-sass": "^4.0.2",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"ts-lint": "^4.5.1",
|
||||
"tsc": "^1.20150623.0",
|
||||
"tslint": "^5.19.0",
|
||||
"typescript": "^3.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"compression": "^1.7.4",
|
||||
"connect-session-sequelize": "^6.0.0",
|
||||
"cookie-parser": "^1.4.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-graphql": "^0.9.0",
|
||||
"express-session": "^1.16.2",
|
||||
"express-socket.io-session": "^1.3.5",
|
||||
"fs-extra": "^8.1.0",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql-import": "^0.7.1",
|
||||
"http-status": "^1.3.2",
|
||||
"js-yaml": "^3.13.1",
|
||||
"markdown-it": "^10.0.0",
|
||||
"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",
|
||||
"winston-daily-rotate-file": "^4.2.1"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "greenvironment-server",
|
||||
"version": "0.1.0",
|
||||
"description": "Server for greenvironment network",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "gulp",
|
||||
"start": "node ./dist/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.trivernis.net/Software_Engineering_I/greenvironment-server.git"
|
||||
},
|
||||
"keywords": [
|
||||
"server",
|
||||
"nodejs",
|
||||
"express"
|
||||
],
|
||||
"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",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.1",
|
||||
"@types/express-graphql": "^0.8.0",
|
||||
"@types/express-session": "^1.15.14",
|
||||
"@types/express-socket.io-session": "^1.3.2",
|
||||
"@types/fs-extra": "^8.0.0",
|
||||
"@types/graphql": "^14.2.3",
|
||||
"@types/http-status": "^0.2.30",
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
"@types/markdown-it": "0.0.9",
|
||||
"@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",
|
||||
"gulp-minify": "^3.1.0",
|
||||
"gulp-sass": "^4.0.2",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"ts-lint": "^4.5.1",
|
||||
"tsc": "^1.20150623.0",
|
||||
"tslint": "^5.19.0",
|
||||
"typescript": "^3.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"compression": "^1.7.4",
|
||||
"connect-session-sequelize": "^6.0.0",
|
||||
"cookie-parser": "^1.4.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-graphql": "^0.9.0",
|
||||
"express-session": "^1.16.2",
|
||||
"express-socket.io-session": "^1.3.5",
|
||||
"fs-extra": "^8.1.0",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql-import": "^0.7.1",
|
||||
"http-status": "^1.3.2",
|
||||
"js-yaml": "^3.13.1",
|
||||
"markdown-it": "^10.0.0",
|
||||
"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",
|
||||
"winston-daily-rotate-file": "^4.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,114 +1,118 @@
|
||||
import * as compression from "compression";
|
||||
import * as cookieParser from "cookie-parser";
|
||||
import * as cors from "cors";
|
||||
import {Request, Response} from "express";
|
||||
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 httpStatus from "http-status";
|
||||
import * as path from "path";
|
||||
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";
|
||||
|
||||
const SequelizeStore = require("connect-session-sequelize")(session.Store);
|
||||
const logger = globals.logger;
|
||||
|
||||
class App {
|
||||
public app: express.Application;
|
||||
public io: socketIo.Server;
|
||||
public server: http.Server;
|
||||
public readonly sequelize: Sequelize;
|
||||
|
||||
constructor() {
|
||||
this.app = express();
|
||||
this.server = new http.Server(this.app);
|
||||
this.io = socketIo(this.server);
|
||||
this.sequelize = new Sequelize(globals.config.database.connectionUri );
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes everything that needs to be initialized asynchronous.
|
||||
*/
|
||||
public async init() {
|
||||
await dataaccess.init(this.sequelize);
|
||||
|
||||
const appSession = session({
|
||||
cookie: {
|
||||
maxAge: Number(globals.config.session.cookieMaxAge) || 604800000,
|
||||
secure: "auto",
|
||||
},
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
secret: globals.config.session.secret,
|
||||
store: new SequelizeStore({db: this.sequelize}),
|
||||
});
|
||||
|
||||
const force = fsx.existsSync("sqz-force");
|
||||
logger.info(`Sequelize Table force: ${force}`);
|
||||
await this.sequelize.sync({force, logging: (msg) => logger.silly(msg)});
|
||||
await routes.ioListeners(this.io);
|
||||
|
||||
this.io.use(sharedsession(appSession, {autoSave: true}));
|
||||
|
||||
this.app.set("views", path.join(__dirname, "views"));
|
||||
this.app.set("view engine", "pug");
|
||||
this.app.set("trust proxy", 1);
|
||||
|
||||
this.app.use(compression());
|
||||
this.app.use(express.json());
|
||||
this.app.use(express.urlencoded({extended: false}));
|
||||
this.app.use(express.static(path.join(__dirname, "public")));
|
||||
this.app.use(cookieParser());
|
||||
this.app.use(appSession);
|
||||
if (globals.config.server.cors) {
|
||||
this.app.use(cors());
|
||||
}
|
||||
this.app.use((req, res, next) => {
|
||||
logger.verbose(`${req.method} ${req.url}`);
|
||||
next();
|
||||
});
|
||||
this.app.use(routes.router);
|
||||
this.app.use("/graphql", graphqlHTTP((request, response) => {
|
||||
return {
|
||||
// @ts-ignore all
|
||||
context: {session: request.session},
|
||||
graphiql: true,
|
||||
rootValue: resolver(request, response),
|
||||
schema: buildSchema(importSchema(path.join(__dirname, "./graphql/schema.graphql"))),
|
||||
};
|
||||
}));
|
||||
this.app.use((req: any, res: Response) => {
|
||||
res.status(httpStatus.NOT_FOUND);
|
||||
res.render("errors/404.pug", {url: req.url});
|
||||
});
|
||||
this.app.use((err, req: Request, res: Response) => {
|
||||
res.status(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
res.render("errors/500.pug");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the web server.
|
||||
*/
|
||||
public start() {
|
||||
if (globals.config.server.port) {
|
||||
logger.info(`Starting server...`);
|
||||
this.app.listen(globals.config.server.port);
|
||||
logger.info(`Server running on port ${globals.config.server.port}`);
|
||||
} else {
|
||||
logger.error("No port specified in the config." +
|
||||
"Please configure a port in the config.yaml.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
import * as compression from "compression";
|
||||
import * as cookieParser from "cookie-parser";
|
||||
import * as cors from "cors";
|
||||
import {Request, Response} from "express";
|
||||
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 httpStatus from "http-status";
|
||||
import * as path from "path";
|
||||
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";
|
||||
|
||||
const SequelizeStore = require("connect-session-sequelize")(session.Store);
|
||||
const logger = globals.logger;
|
||||
|
||||
class App {
|
||||
public app: express.Application;
|
||||
public io: socketIo.Server;
|
||||
public server: http.Server;
|
||||
public readonly sequelize: Sequelize;
|
||||
|
||||
constructor() {
|
||||
this.app = express();
|
||||
this.server = new http.Server(this.app);
|
||||
this.io = socketIo(this.server);
|
||||
this.sequelize = new Sequelize(globals.config.database.connectionUri );
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes everything that needs to be initialized asynchronous.
|
||||
*/
|
||||
public async init() {
|
||||
await dataaccess.init(this.sequelize);
|
||||
|
||||
const appSession = session({
|
||||
cookie: {
|
||||
maxAge: Number(globals.config.session.cookieMaxAge) || 604800000,
|
||||
secure: "auto",
|
||||
},
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
secret: globals.config.session.secret,
|
||||
store: new SequelizeStore({db: this.sequelize}),
|
||||
});
|
||||
|
||||
const force = fsx.existsSync("sqz-force");
|
||||
logger.info(`Sequelize Table force: ${force}`);
|
||||
await this.sequelize.sync({force, logging: (msg) => logger.silly(msg)});
|
||||
await routes.ioListeners(this.io);
|
||||
|
||||
this.io.use(sharedsession(appSession, {autoSave: true}));
|
||||
|
||||
this.app.set("views", path.join(__dirname, "views"));
|
||||
this.app.set("view engine", "pug");
|
||||
this.app.set("trust proxy", 1);
|
||||
|
||||
this.app.use(compression());
|
||||
this.app.use(express.json());
|
||||
this.app.use(express.urlencoded({extended: false}));
|
||||
this.app.use(express.static(path.join(__dirname, "public")));
|
||||
this.app.use(cookieParser());
|
||||
this.app.use(appSession);
|
||||
if (globals.config.server.cors) {
|
||||
this.app.use(cors());
|
||||
}
|
||||
this.app.use((req, res, next) => {
|
||||
logger.verbose(`${req.method} ${req.url}`);
|
||||
next();
|
||||
});
|
||||
this.app.use(routes.router);
|
||||
this.app.use("/graphql", graphqlHTTP((request, response) => {
|
||||
return {
|
||||
// @ts-ignore all
|
||||
context: {session: request.session},
|
||||
graphiql: true,
|
||||
rootValue: resolver(request, response),
|
||||
schema: buildSchema(importSchema(path.join(__dirname, "./graphql/schema.graphql"))),
|
||||
};
|
||||
}));
|
||||
this.app.use((req: any, res: Response) => {
|
||||
if (globals.config.frontend.angularIndex) {
|
||||
res.sendFile(path.join(__dirname, globals.config.frontend.angularIndex));
|
||||
} else {
|
||||
res.status(httpStatus.NOT_FOUND);
|
||||
res.render("errors/404.pug", {url: req.url});
|
||||
}
|
||||
});
|
||||
this.app.use((err, req: Request, res: Response) => {
|
||||
res.status(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
res.render("errors/500.pug");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the web server.
|
||||
*/
|
||||
public start() {
|
||||
if (globals.config.server.port) {
|
||||
logger.info(`Starting server...`);
|
||||
this.app.listen(globals.config.server.port);
|
||||
logger.info(`Server running on port ${globals.config.server.port}`);
|
||||
} else {
|
||||
logger.error("No port specified in the config." +
|
||||
"Please configure a port in the config.yaml.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
@ -1,371 +1,371 @@
|
||||
import {AggregateError} from "bluebird";
|
||||
import {GraphQLError} from "graphql";
|
||||
import * as status from "http-status";
|
||||
import dataaccess from "../lib/dataaccess";
|
||||
import {NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors";
|
||||
import globals from "../lib/globals";
|
||||
import {InternalEvents} from "../lib/InternalEvents";
|
||||
import * as models from "../lib/models";
|
||||
import {is} from "../lib/regex";
|
||||
|
||||
/**
|
||||
* Returns the resolvers for the graphql api.
|
||||
* @param req - the request object
|
||||
* @param res - the response object
|
||||
*/
|
||||
export function resolver(req: any, res: any): any {
|
||||
return {
|
||||
async getSelf() {
|
||||
if (req.session.userId) {
|
||||
return models.User.findByPk(req.session.userId);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async getUser({userId, handle}: { userId: number, handle: string }) {
|
||||
if (handle) {
|
||||
return await dataaccess.getUserByHandle(handle);
|
||||
} else if (userId) {
|
||||
return models.User.findByPk(userId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No userId or handle provided.");
|
||||
}
|
||||
},
|
||||
async getPost({postId}: { postId: number }) {
|
||||
if (postId) {
|
||||
return await dataaccess.getPost(postId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No postId given.");
|
||||
}
|
||||
},
|
||||
async getChat({chatId}: { chatId: number }) {
|
||||
if (chatId) {
|
||||
return models.ChatRoom.findByPk(chatId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No chatId given.");
|
||||
}
|
||||
},
|
||||
async getGroup({groupId}: {groupId: number}) {
|
||||
if (groupId) {
|
||||
return models.Group.findByPk(groupId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No group id given.");
|
||||
}
|
||||
},
|
||||
async getRequest({requestId}: {requestId: number}) {
|
||||
if (requestId) {
|
||||
return models.Request.findByPk(requestId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No requestId given.");
|
||||
}
|
||||
},
|
||||
acceptCookies() {
|
||||
req.session.cookiesAccepted = true;
|
||||
return true;
|
||||
},
|
||||
async login({email, passwordHash}: { email: string, passwordHash: string }) {
|
||||
if (email && passwordHash) {
|
||||
try {
|
||||
const user = await dataaccess.getUserByLogin(email, passwordHash);
|
||||
req.session.userId = user.id;
|
||||
return user;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No email or password given.");
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
if (req.session.user) {
|
||||
delete req.session.user;
|
||||
return true;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async register({username, email, passwordHash}: { username: string, email: string, passwordHash: string }) {
|
||||
if (username && email && passwordHash) {
|
||||
if (!is.email(email)) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError(`'${email}' is not a valid email address!`);
|
||||
}
|
||||
try {
|
||||
const user = await dataaccess.registerUser(username, email, passwordHash);
|
||||
req.session.userId = user.id;
|
||||
return user;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No username, email or password given.");
|
||||
}
|
||||
},
|
||||
async vote({postId, type}: { postId: number, type: dataaccess.VoteType }) {
|
||||
if (postId && type) {
|
||||
if (req.session.userId) {
|
||||
const post = await models.Post.findByPk(postId);
|
||||
if (post) {
|
||||
return await post.vote(req.session.userId, type);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new PostNotFoundGqlError(postId);
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No postId or type given.");
|
||||
}
|
||||
},
|
||||
async createPost({content}: { content: string }) {
|
||||
if (content) {
|
||||
if (req.session.userId) {
|
||||
const post = await dataaccess.createPost(content, req.session.userId);
|
||||
globals.internalEmitter.emit(InternalEvents.GQLPOSTCREATE, post);
|
||||
return post;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("Can't create empty post.");
|
||||
}
|
||||
},
|
||||
async deletePost({postId}: { postId: number }) {
|
||||
if (postId) {
|
||||
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);
|
||||
return new GraphQLError("User is not author of the post.");
|
||||
}
|
||||
} else {
|
||||
return new GraphQLError("No postId given.");
|
||||
}
|
||||
},
|
||||
async createChat({members}: { members: number[] }) {
|
||||
if (req.session.userId) {
|
||||
const chatMembers = [req.session.userId];
|
||||
if (members) {
|
||||
chatMembers.push(...members);
|
||||
}
|
||||
return await dataaccess.createChat(...chatMembers);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async sendMessage({chatId, content}: { chatId: number, content: string }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (chatId && content) {
|
||||
try {
|
||||
const message = await dataaccess.sendChatMessage(req.session.userId, chatId, content);
|
||||
globals.internalEmitter.emit(InternalEvents.GQLCHATMESSAGE, message);
|
||||
return message;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No chatId or content given.");
|
||||
}
|
||||
},
|
||||
async sendRequest({receiver, type}: { receiver: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (receiver && type) {
|
||||
return await dataaccess.createRequest(req.session.userId, receiver, type);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No receiver or type given.");
|
||||
}
|
||||
},
|
||||
async denyRequest({sender, type}: { sender: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (sender && type) {
|
||||
const user = await models.User.findByPk(req.session.userId);
|
||||
await user.denyRequest(sender, type);
|
||||
return true;
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No sender or type given.");
|
||||
}
|
||||
},
|
||||
async acceptRequest({sender, type}: { sender: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (sender && type) {
|
||||
try {
|
||||
const user = await models.User.findByPk(req.session.userId);
|
||||
await user.acceptRequest(sender, type);
|
||||
return true;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No sender or type given.");
|
||||
}
|
||||
},
|
||||
async removeFriend({friendId}: {friendId: number}) {
|
||||
if (req.session.userId) {
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
return await self.removeFriend(friendId);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async getPosts({first, offset, sort}: {first: number, offset: number, sort: dataaccess.SortType}) {
|
||||
return await dataaccess.getPosts(first, offset, sort);
|
||||
},
|
||||
async createGroup({name, members}: {name: string, members: number[]}) {
|
||||
if (req.session.userId) {
|
||||
return await dataaccess.createGroup(name, req.session.userId, members);
|
||||
} else {
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async joinGroup({id}: {id: number}) {
|
||||
if (req.session.userId) {
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.ADD);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async leaveGroup({id}: {id: number}) {
|
||||
if (req.session.userId) {
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.REMOVE);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async addGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
|
||||
if (req.session.userId) {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
if (group && !(await group.$has("rAdmins", self)) && (await group.creator()) !== self.id) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not a group admin!");
|
||||
}
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.OP);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async removeGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
|
||||
if (req.session.userId) {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
const isCreator = Number(group.creatorId) === Number(req.session.userId);
|
||||
const userIsCreator = Number(group.creatorId) === Number(userId) ;
|
||||
if (group && !isCreator && Number(userId) !== Number(req.session.userId)) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not the group creator!");
|
||||
} else if (userIsCreator) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not allowed to remove a creator as an admin.");
|
||||
}
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.DEOP);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async createEvent({name, dueDate, groupId}: {name: string, dueDate: string, groupId: number}) {
|
||||
if (req.session.userId) {
|
||||
const date = new Date(dueDate);
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
return group.$create<models.Event>("rEvent", {name, dueDate: date});
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async joinEvent({eventId}: {eventId: number}) {
|
||||
if (req.session.userId) {
|
||||
const event = await models.Event.findByPk(eventId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
await event.$add("rParticipants", self);
|
||||
return event;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async leaveEvent({eventId}: {eventId: number}) {
|
||||
if (req.session.userId) {
|
||||
const event = await models.Event.findByPk(eventId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
await event.$remove("rParticipants", self);
|
||||
return event;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
import {AggregateError} from "bluebird";
|
||||
import {GraphQLError} from "graphql";
|
||||
import * as status from "http-status";
|
||||
import dataaccess from "../lib/dataaccess";
|
||||
import {NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors";
|
||||
import globals from "../lib/globals";
|
||||
import {InternalEvents} from "../lib/InternalEvents";
|
||||
import * as models from "../lib/models";
|
||||
import {is} from "../lib/regex";
|
||||
|
||||
/**
|
||||
* Returns the resolvers for the graphql api.
|
||||
* @param req - the request object
|
||||
* @param res - the response object
|
||||
*/
|
||||
export function resolver(req: any, res: any): any {
|
||||
return {
|
||||
async getSelf() {
|
||||
if (req.session.userId) {
|
||||
return models.User.findByPk(req.session.userId);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async getUser({userId, handle}: { userId: number, handle: string }) {
|
||||
if (handle) {
|
||||
return await dataaccess.getUserByHandle(handle);
|
||||
} else if (userId) {
|
||||
return models.User.findByPk(userId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No userId or handle provided.");
|
||||
}
|
||||
},
|
||||
async getPost({postId}: { postId: number }) {
|
||||
if (postId) {
|
||||
return await dataaccess.getPost(postId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No postId given.");
|
||||
}
|
||||
},
|
||||
async getChat({chatId}: { chatId: number }) {
|
||||
if (chatId) {
|
||||
return models.ChatRoom.findByPk(chatId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No chatId given.");
|
||||
}
|
||||
},
|
||||
async getGroup({groupId}: {groupId: number}) {
|
||||
if (groupId) {
|
||||
return models.Group.findByPk(groupId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No group id given.");
|
||||
}
|
||||
},
|
||||
async getRequest({requestId}: {requestId: number}) {
|
||||
if (requestId) {
|
||||
return models.Request.findByPk(requestId);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No requestId given.");
|
||||
}
|
||||
},
|
||||
acceptCookies() {
|
||||
req.session.cookiesAccepted = true;
|
||||
return true;
|
||||
},
|
||||
async login({email, passwordHash}: { email: string, passwordHash: string }) {
|
||||
if (email && passwordHash) {
|
||||
try {
|
||||
const user = await dataaccess.getUserByLogin(email, passwordHash);
|
||||
req.session.userId = user.id;
|
||||
return user;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No email or password given.");
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
if (req.session.user) {
|
||||
delete req.session.user;
|
||||
return true;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async register({username, email, passwordHash}: { username: string, email: string, passwordHash: string }) {
|
||||
if (username && email && passwordHash) {
|
||||
if (!is.email(email)) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError(`'${email}' is not a valid email address!`);
|
||||
}
|
||||
try {
|
||||
const user = await dataaccess.registerUser(username, email, passwordHash);
|
||||
req.session.userId = user.id;
|
||||
return user;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No username, email or password given.");
|
||||
}
|
||||
},
|
||||
async vote({postId, type}: { postId: number, type: dataaccess.VoteType }) {
|
||||
if (postId && type) {
|
||||
if (req.session.userId) {
|
||||
const post = await models.Post.findByPk(postId);
|
||||
if (post) {
|
||||
return await post.vote(req.session.userId, type);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new PostNotFoundGqlError(postId);
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No postId or type given.");
|
||||
}
|
||||
},
|
||||
async createPost({content}: { content: string }) {
|
||||
if (content) {
|
||||
if (req.session.userId) {
|
||||
const post = await dataaccess.createPost(content, req.session.userId);
|
||||
globals.internalEmitter.emit(InternalEvents.GQLPOSTCREATE, post);
|
||||
return post;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("Can't create empty post.");
|
||||
}
|
||||
},
|
||||
async deletePost({postId}: { postId: number }) {
|
||||
if (postId) {
|
||||
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);
|
||||
return new GraphQLError("User is not author of the post.");
|
||||
}
|
||||
} else {
|
||||
return new GraphQLError("No postId given.");
|
||||
}
|
||||
},
|
||||
async createChat({members}: { members: number[] }) {
|
||||
if (req.session.userId) {
|
||||
const chatMembers = [req.session.userId];
|
||||
if (members) {
|
||||
chatMembers.push(...members);
|
||||
}
|
||||
return await dataaccess.createChat(...chatMembers);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async sendMessage({chatId, content}: { chatId: number, content: string }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (chatId && content) {
|
||||
try {
|
||||
const message = await dataaccess.sendChatMessage(req.session.userId, chatId, content);
|
||||
globals.internalEmitter.emit(InternalEvents.GQLCHATMESSAGE, message);
|
||||
return message;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No chatId or content given.");
|
||||
}
|
||||
},
|
||||
async sendRequest({receiver, type}: { receiver: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (receiver && type) {
|
||||
return await dataaccess.createRequest(req.session.userId, receiver, type);
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No receiver or type given.");
|
||||
}
|
||||
},
|
||||
async denyRequest({sender, type}: { sender: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (sender && type) {
|
||||
const user = await models.User.findByPk(req.session.userId);
|
||||
await user.denyRequest(sender, type);
|
||||
return true;
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No sender or type given.");
|
||||
}
|
||||
},
|
||||
async acceptRequest({sender, type}: { sender: number, type: dataaccess.RequestType }) {
|
||||
if (!req.session.userId) {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
if (sender && type) {
|
||||
try {
|
||||
const user = await models.User.findByPk(req.session.userId);
|
||||
await user.acceptRequest(sender, type);
|
||||
return true;
|
||||
} catch (err) {
|
||||
globals.logger.warn(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError || err.message;
|
||||
}
|
||||
} else {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return new GraphQLError("No sender or type given.");
|
||||
}
|
||||
},
|
||||
async removeFriend({friendId}: {friendId: number}) {
|
||||
if (req.session.userId) {
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
return await self.removeFriend(friendId);
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async getPosts({first, offset, sort}: {first: number, offset: number, sort: dataaccess.SortType}) {
|
||||
return await dataaccess.getPosts(first, offset, sort);
|
||||
},
|
||||
async createGroup({name, members}: {name: string, members: number[]}) {
|
||||
if (req.session.userId) {
|
||||
return await dataaccess.createGroup(name, req.session.userId, members);
|
||||
} else {
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async joinGroup({id}: {id: number}) {
|
||||
if (req.session.userId) {
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.ADD);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async leaveGroup({id}: {id: number}) {
|
||||
if (req.session.userId) {
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.REMOVE);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async addGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
|
||||
if (req.session.userId) {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
if (group && !(await group.$has("rAdmins", self)) && (await group.creator()) !== self.id) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not a group admin!");
|
||||
}
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.OP);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async removeGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
|
||||
if (req.session.userId) {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
const isCreator = Number(group.creatorId) === Number(req.session.userId);
|
||||
const userIsCreator = Number(group.creatorId) === Number(userId) ;
|
||||
if (group && !isCreator && Number(userId) !== Number(req.session.userId)) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not the group creator!");
|
||||
} else if (userIsCreator) {
|
||||
res.status(status.FORBIDDEN);
|
||||
return new GraphQLError("You are not allowed to remove a creator as an admin.");
|
||||
}
|
||||
try {
|
||||
return await dataaccess
|
||||
.changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.DEOP);
|
||||
} catch (err) {
|
||||
res.status(status.BAD_REQUEST);
|
||||
return err.graphqlError;
|
||||
}
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async createEvent({name, dueDate, groupId}: {name: string, dueDate: string, groupId: number}) {
|
||||
if (req.session.userId) {
|
||||
const date = new Date(dueDate);
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
return group.$create<models.Event>("rEvent", {name, dueDate: date});
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async joinEvent({eventId}: {eventId: number}) {
|
||||
if (req.session.userId) {
|
||||
const event = await models.Event.findByPk(eventId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
await event.$add("rParticipants", self);
|
||||
return event;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
async leaveEvent({eventId}: {eventId: number}) {
|
||||
if (req.session.userId) {
|
||||
const event = await models.Event.findByPk(eventId);
|
||||
const self = await models.User.findByPk(req.session.userId);
|
||||
await event.$remove("rParticipants", self);
|
||||
return event;
|
||||
} else {
|
||||
res.status(status.UNAUTHORIZED);
|
||||
return new NotLoggedInGqlError();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,390 +1,390 @@
|
||||
type Query {
|
||||
"returns the user object for a given user id or a handle (only one required)"
|
||||
getUser(userId: ID, handle: String): User
|
||||
|
||||
"returns the logged in user"
|
||||
getSelf: Profile
|
||||
|
||||
"returns the post object for a post id"
|
||||
getPost(postId: ID!): Post
|
||||
|
||||
"returns the chat object for a chat id"
|
||||
getChat(chatId: ID!): ChatRoom
|
||||
|
||||
"return shte group object for its id"
|
||||
getGroup(groupId: ID!): Group
|
||||
|
||||
"returns the request object for its id"
|
||||
getRequest(requestId: ID!): Request
|
||||
|
||||
"find a post by the posted date or content"
|
||||
findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post]
|
||||
|
||||
"find a user by user name or handle"
|
||||
findUser(first: Int, offset: Int, name: String, handle: String): [User]
|
||||
|
||||
"returns the post filtered by the sort type with pagination."
|
||||
getPosts(first: Int=20, offset: Int=0, sort: SortType = NEW): [Post]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"Accepts the usage of cookies."
|
||||
acceptCookies: Boolean
|
||||
|
||||
"Login of the user. The passwordHash should be a sha512 hash of the password."
|
||||
login(email: String, passwordHash: String): Profile
|
||||
|
||||
"Registers the user."
|
||||
register(username: String, email: String, passwordHash: String): Profile
|
||||
|
||||
"Logout of the user."
|
||||
logout: Boolean
|
||||
|
||||
"Upvote/downvote a Post"
|
||||
vote(postId: ID!, type: VoteType!): VoteType
|
||||
|
||||
"Report the post"
|
||||
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
|
||||
|
||||
"lets you deny a request for a given request id"
|
||||
denyRequest(requestId: ID!): Boolean
|
||||
|
||||
"removes a friend"
|
||||
removeFriend(friendId: ID!): Boolean
|
||||
|
||||
"send a message in a Chatroom"
|
||||
sendMessage(chatId: ID!, content: String!): ChatMessage
|
||||
|
||||
"create the post"
|
||||
createPost(content: String!): Post!
|
||||
|
||||
"delete the post for a given post id"
|
||||
deletePost(postId: ID!): Boolean!
|
||||
|
||||
"Creates a chat between the user (and optional an other user)"
|
||||
createChat(members: [ID!]): ChatRoom!
|
||||
|
||||
"Creates a new group with a given name and additional members"
|
||||
createGroup(name: String!, members: [ID!]): Group!
|
||||
|
||||
"Joins a group with the given id"
|
||||
joinGroup(id: ID!): Group
|
||||
|
||||
"leaves the group with the given id"
|
||||
leaveGroup(id: ID!): Group
|
||||
|
||||
"adds an admin to the group"
|
||||
addGroupAdmin(groupId: ID!, userId: ID!): Group
|
||||
|
||||
"removes an admin from the group"
|
||||
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
|
||||
|
||||
"Joins a event."
|
||||
joinEvent(eventId: ID!): Event
|
||||
|
||||
"Leaves a event."
|
||||
leaveEvent(eventId: ID!): Event
|
||||
}
|
||||
|
||||
interface UserData {
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"DEPRECATED! the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
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]
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"The groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group]
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
"represents a single user account"
|
||||
type User implements UserData{
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
posts(first: Int=10, offset: Int): [Post]
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"creation date of the user account"
|
||||
joinedAt: String!
|
||||
|
||||
"all friends of the user"
|
||||
friends(first: Int=10, offset: Int=0): [User]
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group]
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
type Profile implements UserData {
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"the email of the user"
|
||||
email: String!
|
||||
|
||||
"returns the chatrooms the user joined."
|
||||
chats(first: Int=10, offset: Int): [ChatRoom]
|
||||
|
||||
"the count of the users chats"
|
||||
chatCount: Int!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
posts(first: Int=10, offset: Int): [Post!]!
|
||||
|
||||
"creation date of the user account"
|
||||
joinedAt: String!
|
||||
|
||||
"all friends of the user"
|
||||
friends(first: Int=10, offset: Int=0): [User!]!
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"all sent request for groupChats/friends/events"
|
||||
sentRequests: [Request!]!
|
||||
|
||||
"all received request for groupChats/friends/events"
|
||||
receivedRequests: [Request!]!
|
||||
|
||||
"all groups the user is an admin of"
|
||||
administratedGroups: [Group!]!
|
||||
|
||||
"all groups the user has created"
|
||||
createdGroups: [Group!]!
|
||||
|
||||
"all groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group!]!
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
"represents a single user post"
|
||||
type Post {
|
||||
|
||||
"The id of the post."
|
||||
id: ID!
|
||||
|
||||
"the text of the post"
|
||||
content: String
|
||||
|
||||
"the content of the post rendered by markdown-it"
|
||||
htmlContent: String
|
||||
|
||||
"upvotes of the Post"
|
||||
upvotes: Int!
|
||||
|
||||
"downvotes of the Post"
|
||||
downvotes: Int!
|
||||
|
||||
"the user that is the author of the Post"
|
||||
author: User!
|
||||
|
||||
"date the post was created"
|
||||
createdAt: String!
|
||||
|
||||
"the type of vote the user performed on the post"
|
||||
userVote: VoteType
|
||||
}
|
||||
|
||||
"represents a request of any type"
|
||||
type Request {
|
||||
|
||||
"Id of the request."
|
||||
id: ID!
|
||||
|
||||
"Id of the user who sended the request"
|
||||
sender: User!
|
||||
|
||||
"Id of the user who received the request"
|
||||
receiver: User!
|
||||
|
||||
"type of the request"
|
||||
type: RequestType!
|
||||
}
|
||||
|
||||
"represents a chatroom"
|
||||
type ChatRoom {
|
||||
"the socket.io namespace for the chatroom"
|
||||
namespace: String
|
||||
|
||||
"the members of the chatroom"
|
||||
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]!
|
||||
|
||||
"id of the chat"
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type ChatMessage {
|
||||
"Id of the chat message"
|
||||
id: ID!
|
||||
|
||||
"The author of the chat message."
|
||||
author: User!
|
||||
|
||||
"The chatroom the message was posted in"
|
||||
chat: ChatRoom!
|
||||
|
||||
"The timestamp when the message was posted (epoch)."
|
||||
createdAt: String!
|
||||
|
||||
"The content of the message."
|
||||
content: String!
|
||||
|
||||
"The content of the message rendered by markdown-it."
|
||||
htmlContent: String
|
||||
}
|
||||
|
||||
type Group {
|
||||
"ID of the group"
|
||||
id: ID!
|
||||
|
||||
"name of the group"
|
||||
name: String!
|
||||
|
||||
"the creator of the group"
|
||||
creator: User
|
||||
|
||||
"all admins of the group"
|
||||
admins(first: Int=10, offset: Int=0): [User]!
|
||||
|
||||
"the members of the group with pagination"
|
||||
members(first: Int = 10, offset: Int = 0): [User]!
|
||||
|
||||
"the groups chat"
|
||||
chat: ChatRoom
|
||||
|
||||
"the events of the group"
|
||||
events(first: Int=10, offset: Int=0): [Event!]!
|
||||
}
|
||||
|
||||
type Event {
|
||||
"ID of the event"
|
||||
id: ID!
|
||||
|
||||
"Name of the event"
|
||||
name: String!
|
||||
|
||||
"The date of the event."
|
||||
dueDate: String!
|
||||
|
||||
"The group the event belongs to."
|
||||
group: Group!
|
||||
|
||||
"The participants of the event."
|
||||
participants(first: Int=10, offset: Int=0): [User!]!
|
||||
}
|
||||
|
||||
"represents the type of vote performed on a post"
|
||||
enum VoteType {
|
||||
UPVOTE
|
||||
DOWNVOTE
|
||||
}
|
||||
|
||||
"""
|
||||
represents the type of request that the user has received
|
||||
Currently on Friend Requests are implemented.
|
||||
"""
|
||||
enum RequestType {
|
||||
FRIENDREQUEST
|
||||
GROUPINVITE
|
||||
EVENTINVITE
|
||||
}
|
||||
|
||||
enum SortType {
|
||||
TOP
|
||||
NEW
|
||||
}
|
||||
type Query {
|
||||
"returns the user object for a given user id or a handle (only one required)"
|
||||
getUser(userId: ID, handle: String): User
|
||||
|
||||
"returns the logged in user"
|
||||
getSelf: Profile
|
||||
|
||||
"returns the post object for a post id"
|
||||
getPost(postId: ID!): Post
|
||||
|
||||
"returns the chat object for a chat id"
|
||||
getChat(chatId: ID!): ChatRoom
|
||||
|
||||
"return shte group object for its id"
|
||||
getGroup(groupId: ID!): Group
|
||||
|
||||
"returns the request object for its id"
|
||||
getRequest(requestId: ID!): Request
|
||||
|
||||
"find a post by the posted date or content"
|
||||
findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post]
|
||||
|
||||
"find a user by user name or handle"
|
||||
findUser(first: Int, offset: Int, name: String, handle: String): [User]
|
||||
|
||||
"returns the post filtered by the sort type with pagination."
|
||||
getPosts(first: Int=20, offset: Int=0, sort: SortType = NEW): [Post]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"Accepts the usage of cookies."
|
||||
acceptCookies: Boolean
|
||||
|
||||
"Login of the user. The passwordHash should be a sha512 hash of the password."
|
||||
login(email: String, passwordHash: String): Profile
|
||||
|
||||
"Registers the user."
|
||||
register(username: String, email: String, passwordHash: String): Profile
|
||||
|
||||
"Logout of the user."
|
||||
logout: Boolean
|
||||
|
||||
"Upvote/downvote a Post"
|
||||
vote(postId: ID!, type: VoteType!): VoteType
|
||||
|
||||
"Report the post"
|
||||
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
|
||||
|
||||
"lets you deny a request for a given request id"
|
||||
denyRequest(requestId: ID!): Boolean
|
||||
|
||||
"removes a friend"
|
||||
removeFriend(friendId: ID!): Boolean
|
||||
|
||||
"send a message in a Chatroom"
|
||||
sendMessage(chatId: ID!, content: String!): ChatMessage
|
||||
|
||||
"create the post"
|
||||
createPost(content: String!): Post!
|
||||
|
||||
"delete the post for a given post id"
|
||||
deletePost(postId: ID!): Boolean!
|
||||
|
||||
"Creates a chat between the user (and optional an other user)"
|
||||
createChat(members: [ID!]): ChatRoom!
|
||||
|
||||
"Creates a new group with a given name and additional members"
|
||||
createGroup(name: String!, members: [ID!]): Group!
|
||||
|
||||
"Joins a group with the given id"
|
||||
joinGroup(id: ID!): Group
|
||||
|
||||
"leaves the group with the given id"
|
||||
leaveGroup(id: ID!): Group
|
||||
|
||||
"adds an admin to the group"
|
||||
addGroupAdmin(groupId: ID!, userId: ID!): Group
|
||||
|
||||
"removes an admin from the group"
|
||||
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
|
||||
|
||||
"Joins a event."
|
||||
joinEvent(eventId: ID!): Event
|
||||
|
||||
"Leaves a event."
|
||||
leaveEvent(eventId: ID!): Event
|
||||
}
|
||||
|
||||
interface UserData {
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"DEPRECATED! the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
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]
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"The groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group]
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
"represents a single user account"
|
||||
type User implements UserData{
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
posts(first: Int=10, offset: Int): [Post]
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"creation date of the user account"
|
||||
joinedAt: String!
|
||||
|
||||
"all friends of the user"
|
||||
friends(first: Int=10, offset: Int=0): [User]
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group]
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
type Profile implements UserData {
|
||||
"url for the Profile picture of the User"
|
||||
profilePicture: String
|
||||
|
||||
"name of the User"
|
||||
name: String!
|
||||
|
||||
"the email of the user"
|
||||
email: String!
|
||||
|
||||
"returns the chatrooms the user joined."
|
||||
chats(first: Int=10, offset: Int): [ChatRoom]
|
||||
|
||||
"the count of the users chats"
|
||||
chatCount: Int!
|
||||
|
||||
"unique identifier name from the User"
|
||||
handle: String!
|
||||
|
||||
"Id of the User"
|
||||
id: ID!
|
||||
|
||||
"the total number of posts the user posted"
|
||||
numberOfPosts: Int!
|
||||
|
||||
"the number of posts the user has created"
|
||||
postCount: Int!
|
||||
|
||||
"returns a given number of posts of a user"
|
||||
posts(first: Int=10, offset: Int): [Post!]!
|
||||
|
||||
"creation date of the user account"
|
||||
joinedAt: String!
|
||||
|
||||
"all friends of the user"
|
||||
friends(first: Int=10, offset: Int=0): [User!]!
|
||||
|
||||
"The number of friends the user has"
|
||||
friendCount: Int!
|
||||
|
||||
"all sent request for groupChats/friends/events"
|
||||
sentRequests: [Request!]!
|
||||
|
||||
"all received request for groupChats/friends/events"
|
||||
receivedRequests: [Request!]!
|
||||
|
||||
"all groups the user is an admin of"
|
||||
administratedGroups: [Group!]!
|
||||
|
||||
"all groups the user has created"
|
||||
createdGroups: [Group!]!
|
||||
|
||||
"all groups the user has joined"
|
||||
groups(first: Int=10, offset: Int=0): [Group!]!
|
||||
|
||||
"The numbef of groups the user has joined"
|
||||
groupCount: Int!
|
||||
|
||||
"the points of the user"
|
||||
points: Int!
|
||||
|
||||
"the levels of the user depending on the points"
|
||||
level: Int!
|
||||
}
|
||||
|
||||
"represents a single user post"
|
||||
type Post {
|
||||
|
||||
"The id of the post."
|
||||
id: ID!
|
||||
|
||||
"the text of the post"
|
||||
content: String
|
||||
|
||||
"the content of the post rendered by markdown-it"
|
||||
htmlContent: String
|
||||
|
||||
"upvotes of the Post"
|
||||
upvotes: Int!
|
||||
|
||||
"downvotes of the Post"
|
||||
downvotes: Int!
|
||||
|
||||
"the user that is the author of the Post"
|
||||
author: User!
|
||||
|
||||
"date the post was created"
|
||||
createdAt: String!
|
||||
|
||||
"the type of vote the user performed on the post"
|
||||
userVote: VoteType
|
||||
}
|
||||
|
||||
"represents a request of any type"
|
||||
type Request {
|
||||
|
||||
"Id of the request."
|
||||
id: ID!
|
||||
|
||||
"Id of the user who sended the request"
|
||||
sender: User!
|
||||
|
||||
"Id of the user who received the request"
|
||||
receiver: User!
|
||||
|
||||
"type of the request"
|
||||
type: RequestType!
|
||||
}
|
||||
|
||||
"represents a chatroom"
|
||||
type ChatRoom {
|
||||
"the socket.io namespace for the chatroom"
|
||||
namespace: String
|
||||
|
||||
"the members of the chatroom"
|
||||
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]!
|
||||
|
||||
"id of the chat"
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type ChatMessage {
|
||||
"Id of the chat message"
|
||||
id: ID!
|
||||
|
||||
"The author of the chat message."
|
||||
author: User!
|
||||
|
||||
"The chatroom the message was posted in"
|
||||
chat: ChatRoom!
|
||||
|
||||
"The timestamp when the message was posted (epoch)."
|
||||
createdAt: String!
|
||||
|
||||
"The content of the message."
|
||||
content: String!
|
||||
|
||||
"The content of the message rendered by markdown-it."
|
||||
htmlContent: String
|
||||
}
|
||||
|
||||
type Group {
|
||||
"ID of the group"
|
||||
id: ID!
|
||||
|
||||
"name of the group"
|
||||
name: String!
|
||||
|
||||
"the creator of the group"
|
||||
creator: User
|
||||
|
||||
"all admins of the group"
|
||||
admins(first: Int=10, offset: Int=0): [User]!
|
||||
|
||||
"the members of the group with pagination"
|
||||
members(first: Int = 10, offset: Int = 0): [User]!
|
||||
|
||||
"the groups chat"
|
||||
chat: ChatRoom
|
||||
|
||||
"the events of the group"
|
||||
events(first: Int=10, offset: Int=0): [Event!]!
|
||||
}
|
||||
|
||||
type Event {
|
||||
"ID of the event"
|
||||
id: ID!
|
||||
|
||||
"Name of the event"
|
||||
name: String!
|
||||
|
||||
"The date of the event."
|
||||
dueDate: String!
|
||||
|
||||
"The group the event belongs to."
|
||||
group: Group!
|
||||
|
||||
"The participants of the event."
|
||||
participants(first: Int=10, offset: Int=0): [User!]!
|
||||
}
|
||||
|
||||
"represents the type of vote performed on a post"
|
||||
enum VoteType {
|
||||
UPVOTE
|
||||
DOWNVOTE
|
||||
}
|
||||
|
||||
"""
|
||||
represents the type of request that the user has received
|
||||
Currently on Friend Requests are implemented.
|
||||
"""
|
||||
enum RequestType {
|
||||
FRIENDREQUEST
|
||||
GROUPINVITE
|
||||
EVENTINVITE
|
||||
}
|
||||
|
||||
enum SortType {
|
||||
TOP
|
||||
NEW
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import App from "./app";
|
||||
|
||||
/**
|
||||
* async main function wrapper.
|
||||
*/
|
||||
(async () => {
|
||||
const app = new App();
|
||||
await app.init();
|
||||
app.start();
|
||||
})();
|
||||
|
||||
import App from "./app";
|
||||
|
||||
/**
|
||||
* async main function wrapper.
|
||||
*/
|
||||
(async () => {
|
||||
const app = new App();
|
||||
await app.init();
|
||||
app.start();
|
||||
})();
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
export enum InternalEvents {
|
||||
CHATCREATE = "chatCreate",
|
||||
CHATMESSAGE = "chatMessage",
|
||||
GQLCHATMESSAGE = "graphqlChatMessage",
|
||||
REQUESTCREATE = "requestCreate",
|
||||
POSTCREATE = "postCreate",
|
||||
GQLPOSTCREATE = "graphqlPostCreate",
|
||||
}
|
||||
export enum InternalEvents {
|
||||
CHATCREATE = "chatCreate",
|
||||
CHATMESSAGE = "chatMessage",
|
||||
GQLCHATMESSAGE = "graphqlChatMessage",
|
||||
REQUESTCREATE = "requestCreate",
|
||||
POSTCREATE = "postCreate",
|
||||
GQLPOSTCREATE = "graphqlPostCreate",
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {Router} from "express";
|
||||
import {Namespace, Server} from "socket.io";
|
||||
|
||||
/**
|
||||
* Abstract Route class to be implemented by each route.
|
||||
* This class contains the socket-io Server, router and resolver
|
||||
* for each route.
|
||||
*/
|
||||
abstract class Route {
|
||||
|
||||
public router?: Router;
|
||||
protected io?: Server;
|
||||
protected ions?: Namespace;
|
||||
|
||||
public abstract async init(...params: any): Promise<any>;
|
||||
|
||||
public abstract async destroy(...params: any): Promise<any>;
|
||||
}
|
||||
|
||||
export default Route;
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {Router} from "express";
|
||||
import {Namespace, Server} from "socket.io";
|
||||
|
||||
/**
|
||||
* Abstract Route class to be implemented by each route.
|
||||
* This class contains the socket-io Server, router and resolver
|
||||
* for each route.
|
||||
*/
|
||||
abstract class Route {
|
||||
|
||||
public router?: Router;
|
||||
protected io?: Server;
|
||||
protected ions?: Namespace;
|
||||
|
||||
public abstract async init(...params: any): Promise<any>;
|
||||
|
||||
public abstract async destroy(...params: any): Promise<any>;
|
||||
}
|
||||
|
||||
export default Route;
|
||||
|
@ -1,312 +1,312 @@
|
||||
import * as crypto from "crypto";
|
||||
import * as status from "http-status";
|
||||
import {Sequelize} from "sequelize-typescript";
|
||||
import {ChatNotFoundError} from "./errors/ChatNotFoundError";
|
||||
import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError";
|
||||
import {GroupNotFoundGqlError, NotLoggedInGqlError} from "./errors/graphqlErrors";
|
||||
import {GroupNotFoundError} from "./errors/GroupNotFoundError";
|
||||
import {InvalidLoginError} from "./errors/InvalidLoginError";
|
||||
import {NoActionSpecifiedError} from "./errors/NoActionSpecifiedError";
|
||||
import {UserNotFoundError} from "./errors/UserNotFoundError";
|
||||
import globals from "./globals";
|
||||
import {InternalEvents} from "./InternalEvents";
|
||||
import * as models from "./models";
|
||||
|
||||
/**
|
||||
* Generates a new handle from the username and a base64 string of the current time.
|
||||
* @param username
|
||||
*/
|
||||
function generateHandle(username: string) {
|
||||
return `${username}.${Buffer.from(Date.now().toString()).toString("base64")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace with functions to fetch initial data for wrapping.
|
||||
*/
|
||||
namespace dataaccess {
|
||||
|
||||
let sequelize: Sequelize;
|
||||
|
||||
/**
|
||||
* Initializes everything that needs to be initialized asynchronous.
|
||||
*/
|
||||
export async function init(seq: Sequelize) {
|
||||
sequelize = seq;
|
||||
try {
|
||||
await sequelize.addModels([
|
||||
models.ChatMember,
|
||||
models.ChatMessage,
|
||||
models.ChatRoom,
|
||||
models.Friendship,
|
||||
models.Post,
|
||||
models.PostVote,
|
||||
models.Request,
|
||||
models.User,
|
||||
models.Group,
|
||||
models.GroupAdmin,
|
||||
models.GroupMember,
|
||||
models.EventParticipant,
|
||||
models.Event,
|
||||
]);
|
||||
} catch (err) {
|
||||
globals.logger.error(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user by handle.
|
||||
* @param userHandle
|
||||
*/
|
||||
export async function getUserByHandle(userHandle: string): Promise<models.User> {
|
||||
const user = await models.User.findOne({where: {handle: userHandle}});
|
||||
if (user) {
|
||||
return user;
|
||||
} else {
|
||||
throw new UserNotFoundError(userHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user by email and password
|
||||
* @param email
|
||||
* @param password
|
||||
*/
|
||||
export async function getUserByLogin(email: string, password: string): Promise<models.User> {
|
||||
const hash = crypto.createHash("sha512");
|
||||
hash.update(password);
|
||||
password = hash.digest("hex");
|
||||
const user = await models.User.findOne({where: {email}});
|
||||
if (user) {
|
||||
if (user.password === password) {
|
||||
return user;
|
||||
} else {
|
||||
throw new InvalidLoginError(email);
|
||||
}
|
||||
} else {
|
||||
throw new UserNotFoundError(email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a user with a username and password returning a user
|
||||
* @param username
|
||||
* @param email
|
||||
* @param password
|
||||
*/
|
||||
export async function registerUser(username: string, email: string, password: string): Promise<models.User> {
|
||||
const hash = crypto.createHash("sha512");
|
||||
hash.update(password);
|
||||
password = hash.digest("hex");
|
||||
const existResult = !!(await models.User.findOne({where: {username, email, password}}));
|
||||
const handle = generateHandle(username);
|
||||
if (!existResult) {
|
||||
return models.User.create({username, email, password, handle});
|
||||
} else {
|
||||
throw new EmailAlreadyRegisteredError(email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a post for a given postId.s
|
||||
* @param postId
|
||||
*/
|
||||
export async function getPost(postId: number): Promise<models.Post> {
|
||||
const post = await models.Post.findByPk(postId);
|
||||
if (post) {
|
||||
return post;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all posts sorted by new or top with pagination.
|
||||
* @param first
|
||||
* @param offset
|
||||
* @param sort
|
||||
*/
|
||||
export async function getPosts(first: number, offset: number, sort: SortType) {
|
||||
if (sort === SortType.NEW) {
|
||||
return models.Post.findAll({
|
||||
include: [{association: "rVotes"}],
|
||||
limit: first,
|
||||
offset,
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
} else {
|
||||
return await sequelize.query(
|
||||
`SELECT * FROM (
|
||||
SELECT *,
|
||||
(SELECT count(*) FROM post_votes WHERE vote_type = 'UPVOTE' AND post_id = posts.id) AS upvotes ,
|
||||
(SELECT count(*) FROM post_votes WHERE vote_type = 'DOWNVOTE' AND post_id = posts.id) AS downvotes
|
||||
FROM posts) AS a ORDER BY (a.upvotes - a.downvotes) DESC LIMIT ? OFFSET ?`,
|
||||
{replacements: [first, offset], mapToModel: true, model: models.Post}) as models.Post[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a post
|
||||
* @param content
|
||||
* @param authorId
|
||||
* @param type
|
||||
*/
|
||||
export async function createPost(content: string, authorId: number, type?: string): Promise<models.Post> {
|
||||
type = type || "MISC";
|
||||
const post = await models.Post.create({content, authorId});
|
||||
globals.internalEmitter.emit(InternalEvents.POSTCREATE, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a post
|
||||
* @param postId
|
||||
*/
|
||||
export async function deletePost(postId: number): Promise<boolean> {
|
||||
await (await models.Post.findByPk(postId)).destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chatroom containing two users
|
||||
* @param members
|
||||
*/
|
||||
export async function createChat(...members: number[]): Promise<models.ChatRoom> {
|
||||
return sequelize.transaction(async (t) => {
|
||||
const chat = await models.ChatRoom.create({}, {transaction: t, include: [models.User]});
|
||||
for (const member of members) {
|
||||
const user = await models.User.findByPk(member);
|
||||
await chat.$add("rMember", user, {transaction: t});
|
||||
}
|
||||
await chat.save({transaction: t});
|
||||
globals.internalEmitter.emit(InternalEvents.CHATCREATE, chat);
|
||||
return chat;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message into a chat.
|
||||
* @param authorId
|
||||
* @param chatId
|
||||
* @param content
|
||||
*/
|
||||
export async function sendChatMessage(authorId: number, chatId: number, content: string) {
|
||||
const chat = await models.ChatRoom.findByPk(chatId);
|
||||
if (chat) {
|
||||
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 rChats.
|
||||
*/
|
||||
export async function getAllChats(): Promise<models.ChatRoom[]> {
|
||||
return models.ChatRoom.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to a user.
|
||||
* @param sender
|
||||
* @param receiver
|
||||
* @param requestType
|
||||
*/
|
||||
export async function createRequest(sender: number, receiver: number, requestType?: RequestType) {
|
||||
requestType = requestType || RequestType.FRIENDREQUEST;
|
||||
|
||||
const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType});
|
||||
globals.internalEmitter.emit(InternalEvents.REQUESTCREATE, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new group.
|
||||
* @param name
|
||||
* @param creator
|
||||
* @param members
|
||||
*/
|
||||
export async function createGroup(name: string, creator: number, members: number[]): Promise<models.Group> {
|
||||
members = members || [];
|
||||
return sequelize.transaction(async (t) => {
|
||||
members.push(creator);
|
||||
const groupChat = await createChat(...members);
|
||||
const group = await models.Group.create({name, creatorId: creator, chatId: groupChat.id}, {transaction: t});
|
||||
const creatorUser = await models.User.findByPk(creator, {transaction: t});
|
||||
await group.$add("rAdmins", creatorUser, {transaction: t});
|
||||
for (const member of members) {
|
||||
const user = await models.User.findByPk(member, {transaction: t});
|
||||
await group.$add("rMembers", user, {transaction: t});
|
||||
}
|
||||
return group;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the membership of a user
|
||||
* @param groupId
|
||||
* @param userId
|
||||
* @param action
|
||||
*/
|
||||
export async function changeGroupMembership(groupId: number, userId: number, action: MembershipChangeAction):
|
||||
Promise<models.Group> {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
if (group) {
|
||||
const user = await models.User.findByPk(userId);
|
||||
if (user) {
|
||||
if (action === MembershipChangeAction.ADD) {
|
||||
await group.$add("rMembers", user);
|
||||
} else if (action === MembershipChangeAction.REMOVE) {
|
||||
await group.$remove("rMembers", user);
|
||||
} else if (action === MembershipChangeAction.OP) {
|
||||
await group.$add("rAdmins", user);
|
||||
} else if (action === MembershipChangeAction.DEOP) {
|
||||
await group.$remove("rAdmins", user);
|
||||
} else {
|
||||
throw new NoActionSpecifiedError(MembershipChangeAction);
|
||||
}
|
||||
return group;
|
||||
} else {
|
||||
throw new UserNotFoundError(userId);
|
||||
}
|
||||
} else {
|
||||
throw new GroupNotFoundError(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of votes that can be performed on a post.
|
||||
*/
|
||||
export enum VoteType {
|
||||
UPVOTE = "UPVOTE",
|
||||
DOWNVOTE = "DOWNVOTE",
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of request that can be created.
|
||||
*/
|
||||
export enum RequestType {
|
||||
FRIENDREQUEST = "FRIENDREQUEST",
|
||||
GROUPINVITE = "GROUPINVITE",
|
||||
EVENTINVITE = "EVENTINVITE",
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of sorting in the feed.
|
||||
*/
|
||||
export enum SortType {
|
||||
TOP = "TOP",
|
||||
NEW = "NEW",
|
||||
}
|
||||
|
||||
export enum MembershipChangeAction {
|
||||
ADD,
|
||||
REMOVE,
|
||||
OP,
|
||||
DEOP,
|
||||
}
|
||||
}
|
||||
|
||||
export default dataaccess;
|
||||
import * as crypto from "crypto";
|
||||
import * as status from "http-status";
|
||||
import {Sequelize} from "sequelize-typescript";
|
||||
import {ChatNotFoundError} from "./errors/ChatNotFoundError";
|
||||
import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError";
|
||||
import {GroupNotFoundGqlError, NotLoggedInGqlError} from "./errors/graphqlErrors";
|
||||
import {GroupNotFoundError} from "./errors/GroupNotFoundError";
|
||||
import {InvalidLoginError} from "./errors/InvalidLoginError";
|
||||
import {NoActionSpecifiedError} from "./errors/NoActionSpecifiedError";
|
||||
import {UserNotFoundError} from "./errors/UserNotFoundError";
|
||||
import globals from "./globals";
|
||||
import {InternalEvents} from "./InternalEvents";
|
||||
import * as models from "./models";
|
||||
|
||||
/**
|
||||
* Generates a new handle from the username and a base64 string of the current time.
|
||||
* @param username
|
||||
*/
|
||||
function generateHandle(username: string) {
|
||||
return `${username}.${Buffer.from(Date.now().toString()).toString("base64")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace with functions to fetch initial data for wrapping.
|
||||
*/
|
||||
namespace dataaccess {
|
||||
|
||||
let sequelize: Sequelize;
|
||||
|
||||
/**
|
||||
* Initializes everything that needs to be initialized asynchronous.
|
||||
*/
|
||||
export async function init(seq: Sequelize) {
|
||||
sequelize = seq;
|
||||
try {
|
||||
await sequelize.addModels([
|
||||
models.ChatMember,
|
||||
models.ChatMessage,
|
||||
models.ChatRoom,
|
||||
models.Friendship,
|
||||
models.Post,
|
||||
models.PostVote,
|
||||
models.Request,
|
||||
models.User,
|
||||
models.Group,
|
||||
models.GroupAdmin,
|
||||
models.GroupMember,
|
||||
models.EventParticipant,
|
||||
models.Event,
|
||||
]);
|
||||
} catch (err) {
|
||||
globals.logger.error(err.message);
|
||||
globals.logger.debug(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user by handle.
|
||||
* @param userHandle
|
||||
*/
|
||||
export async function getUserByHandle(userHandle: string): Promise<models.User> {
|
||||
const user = await models.User.findOne({where: {handle: userHandle}});
|
||||
if (user) {
|
||||
return user;
|
||||
} else {
|
||||
throw new UserNotFoundError(userHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user by email and password
|
||||
* @param email
|
||||
* @param password
|
||||
*/
|
||||
export async function getUserByLogin(email: string, password: string): Promise<models.User> {
|
||||
const hash = crypto.createHash("sha512");
|
||||
hash.update(password);
|
||||
password = hash.digest("hex");
|
||||
const user = await models.User.findOne({where: {email}});
|
||||
if (user) {
|
||||
if (user.password === password) {
|
||||
return user;
|
||||
} else {
|
||||
throw new InvalidLoginError(email);
|
||||
}
|
||||
} else {
|
||||
throw new UserNotFoundError(email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a user with a username and password returning a user
|
||||
* @param username
|
||||
* @param email
|
||||
* @param password
|
||||
*/
|
||||
export async function registerUser(username: string, email: string, password: string): Promise<models.User> {
|
||||
const hash = crypto.createHash("sha512");
|
||||
hash.update(password);
|
||||
password = hash.digest("hex");
|
||||
const existResult = !!(await models.User.findOne({where: {username, email, password}}));
|
||||
const handle = generateHandle(username);
|
||||
if (!existResult) {
|
||||
return models.User.create({username, email, password, handle});
|
||||
} else {
|
||||
throw new EmailAlreadyRegisteredError(email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a post for a given postId.s
|
||||
* @param postId
|
||||
*/
|
||||
export async function getPost(postId: number): Promise<models.Post> {
|
||||
const post = await models.Post.findByPk(postId);
|
||||
if (post) {
|
||||
return post;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all posts sorted by new or top with pagination.
|
||||
* @param first
|
||||
* @param offset
|
||||
* @param sort
|
||||
*/
|
||||
export async function getPosts(first: number, offset: number, sort: SortType) {
|
||||
if (sort === SortType.NEW) {
|
||||
return models.Post.findAll({
|
||||
include: [{association: "rVotes"}],
|
||||
limit: first,
|
||||
offset,
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
} else {
|
||||
return await sequelize.query(
|
||||
`SELECT * FROM (
|
||||
SELECT *,
|
||||
(SELECT count(*) FROM post_votes WHERE vote_type = 'UPVOTE' AND post_id = posts.id) AS upvotes ,
|
||||
(SELECT count(*) FROM post_votes WHERE vote_type = 'DOWNVOTE' AND post_id = posts.id) AS downvotes
|
||||
FROM posts) AS a ORDER BY (a.upvotes - a.downvotes) DESC LIMIT ? OFFSET ?`,
|
||||
{replacements: [first, offset], mapToModel: true, model: models.Post}) as models.Post[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a post
|
||||
* @param content
|
||||
* @param authorId
|
||||
* @param type
|
||||
*/
|
||||
export async function createPost(content: string, authorId: number, type?: string): Promise<models.Post> {
|
||||
type = type || "MISC";
|
||||
const post = await models.Post.create({content, authorId});
|
||||
globals.internalEmitter.emit(InternalEvents.POSTCREATE, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a post
|
||||
* @param postId
|
||||
*/
|
||||
export async function deletePost(postId: number): Promise<boolean> {
|
||||
await (await models.Post.findByPk(postId)).destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chatroom containing two users
|
||||
* @param members
|
||||
*/
|
||||
export async function createChat(...members: number[]): Promise<models.ChatRoom> {
|
||||
return sequelize.transaction(async (t) => {
|
||||
const chat = await models.ChatRoom.create({}, {transaction: t, include: [models.User]});
|
||||
for (const member of members) {
|
||||
const user = await models.User.findByPk(member);
|
||||
await chat.$add("rMember", user, {transaction: t});
|
||||
}
|
||||
await chat.save({transaction: t});
|
||||
globals.internalEmitter.emit(InternalEvents.CHATCREATE, chat);
|
||||
return chat;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message into a chat.
|
||||
* @param authorId
|
||||
* @param chatId
|
||||
* @param content
|
||||
*/
|
||||
export async function sendChatMessage(authorId: number, chatId: number, content: string) {
|
||||
const chat = await models.ChatRoom.findByPk(chatId);
|
||||
if (chat) {
|
||||
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 rChats.
|
||||
*/
|
||||
export async function getAllChats(): Promise<models.ChatRoom[]> {
|
||||
return models.ChatRoom.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to a user.
|
||||
* @param sender
|
||||
* @param receiver
|
||||
* @param requestType
|
||||
*/
|
||||
export async function createRequest(sender: number, receiver: number, requestType?: RequestType) {
|
||||
requestType = requestType || RequestType.FRIENDREQUEST;
|
||||
|
||||
const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType});
|
||||
globals.internalEmitter.emit(InternalEvents.REQUESTCREATE, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new group.
|
||||
* @param name
|
||||
* @param creator
|
||||
* @param members
|
||||
*/
|
||||
export async function createGroup(name: string, creator: number, members: number[]): Promise<models.Group> {
|
||||
members = members || [];
|
||||
return sequelize.transaction(async (t) => {
|
||||
members.push(creator);
|
||||
const groupChat = await createChat(...members);
|
||||
const group = await models.Group.create({name, creatorId: creator, chatId: groupChat.id}, {transaction: t});
|
||||
const creatorUser = await models.User.findByPk(creator, {transaction: t});
|
||||
await group.$add("rAdmins", creatorUser, {transaction: t});
|
||||
for (const member of members) {
|
||||
const user = await models.User.findByPk(member, {transaction: t});
|
||||
await group.$add("rMembers", user, {transaction: t});
|
||||
}
|
||||
return group;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the membership of a user
|
||||
* @param groupId
|
||||
* @param userId
|
||||
* @param action
|
||||
*/
|
||||
export async function changeGroupMembership(groupId: number, userId: number, action: MembershipChangeAction):
|
||||
Promise<models.Group> {
|
||||
const group = await models.Group.findByPk(groupId);
|
||||
if (group) {
|
||||
const user = await models.User.findByPk(userId);
|
||||
if (user) {
|
||||
if (action === MembershipChangeAction.ADD) {
|
||||
await group.$add("rMembers", user);
|
||||
} else if (action === MembershipChangeAction.REMOVE) {
|
||||
await group.$remove("rMembers", user);
|
||||
} else if (action === MembershipChangeAction.OP) {
|
||||
await group.$add("rAdmins", user);
|
||||
} else if (action === MembershipChangeAction.DEOP) {
|
||||
await group.$remove("rAdmins", user);
|
||||
} else {
|
||||
throw new NoActionSpecifiedError(MembershipChangeAction);
|
||||
}
|
||||
return group;
|
||||
} else {
|
||||
throw new UserNotFoundError(userId);
|
||||
}
|
||||
} else {
|
||||
throw new GroupNotFoundError(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of votes that can be performed on a post.
|
||||
*/
|
||||
export enum VoteType {
|
||||
UPVOTE = "UPVOTE",
|
||||
DOWNVOTE = "DOWNVOTE",
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of request that can be created.
|
||||
*/
|
||||
export enum RequestType {
|
||||
FRIENDREQUEST = "FRIENDREQUEST",
|
||||
GROUPINVITE = "GROUPINVITE",
|
||||
EVENTINVITE = "EVENTINVITE",
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the types of sorting in the feed.
|
||||
*/
|
||||
export enum SortType {
|
||||
TOP = "TOP",
|
||||
NEW = "NEW",
|
||||
}
|
||||
|
||||
export enum MembershipChangeAction {
|
||||
ADD,
|
||||
REMOVE,
|
||||
OP,
|
||||
DEOP,
|
||||
}
|
||||
}
|
||||
|
||||
export default dataaccess;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import {GraphQLError} from "graphql";
|
||||
|
||||
/**
|
||||
* Base error class.
|
||||
*/
|
||||
export class BaseError extends Error {
|
||||
public readonly graphqlError: GraphQLError;
|
||||
|
||||
constructor(message?: string, friendlyMessage?: string) {
|
||||
super(message);
|
||||
this.graphqlError = new GraphQLError(friendlyMessage || message);
|
||||
}
|
||||
}
|
||||
import {GraphQLError} from "graphql";
|
||||
|
||||
/**
|
||||
* Base error class.
|
||||
*/
|
||||
export class BaseError extends Error {
|
||||
public readonly graphqlError: GraphQLError;
|
||||
|
||||
constructor(message?: string, friendlyMessage?: string) {
|
||||
super(message);
|
||||
this.graphqlError = new GraphQLError(friendlyMessage || message);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class ChatNotFoundError extends BaseError {
|
||||
constructor(chatId: number) {
|
||||
super(`Chat with id ${chatId} not found.`);
|
||||
}
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class ChatNotFoundError extends BaseError {
|
||||
constructor(chatId: number) {
|
||||
super(`Chat with id ${chatId} not found.`);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class EmailAlreadyRegisteredError extends BaseError {
|
||||
constructor(email: string) {
|
||||
super(`A user for '${email}' does already exist.`);
|
||||
}
|
||||
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class EmailAlreadyRegisteredError extends BaseError {
|
||||
constructor(email: string) {
|
||||
super(`A user for '${email}' does already exist.`);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class GroupNotFoundError extends BaseError {
|
||||
constructor(groupId: number) {
|
||||
super(`Group ${groupId} not found!`);
|
||||
}
|
||||
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class GroupNotFoundError extends BaseError {
|
||||
constructor(groupId: number) {
|
||||
super(`Group ${groupId} not found!`);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class InvalidLoginError extends BaseError {
|
||||
constructor(email: (string)) {
|
||||
super(`Invalid login data for ${email}.`);
|
||||
}
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class InvalidLoginError extends BaseError {
|
||||
constructor(email: (string)) {
|
||||
super(`Invalid login data for ${email}.`);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class NoActionSpecifiedError extends BaseError {
|
||||
constructor(actions?: any) {
|
||||
if (actions) {
|
||||
super(`No action of '${Object.keys(actions).join(", ")}'`);
|
||||
} else {
|
||||
super("No action specified!");
|
||||
}
|
||||
}
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class NoActionSpecifiedError extends BaseError {
|
||||
constructor(actions?: any) {
|
||||
if (actions) {
|
||||
super(`No action of '${Object.keys(actions).join(", ")}'`);
|
||||
} else {
|
||||
super("No action specified!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import dataaccess from "../dataaccess";
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
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.`);
|
||||
}
|
||||
|
||||
}
|
||||
import dataaccess from "../dataaccess";
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
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.`);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class UserNotFoundError extends BaseError {
|
||||
constructor(username: (string|number)) {
|
||||
super(`User ${username} not found!`);
|
||||
}
|
||||
}
|
||||
import {BaseError} from "./BaseError";
|
||||
|
||||
export class UserNotFoundError extends BaseError {
|
||||
constructor(username: (string|number)) {
|
||||
super(`User ${username} not found!`);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import {GraphQLError} from "graphql";
|
||||
|
||||
export class NotLoggedInGqlError extends GraphQLError {
|
||||
constructor() {
|
||||
super("Not logged in");
|
||||
}
|
||||
}
|
||||
|
||||
export class PostNotFoundGqlError extends GraphQLError {
|
||||
constructor(postId: number) {
|
||||
super(`Post '${postId}' not found!`);
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupNotFoundGqlError extends GraphQLError {
|
||||
constructor(groupId: number) {
|
||||
super(`Group '${groupId}' not found!`);
|
||||
}
|
||||
}
|
||||
import {GraphQLError} from "graphql";
|
||||
|
||||
export class NotLoggedInGqlError extends GraphQLError {
|
||||
constructor() {
|
||||
super("Not logged in");
|
||||
}
|
||||
}
|
||||
|
||||
export class PostNotFoundGqlError extends GraphQLError {
|
||||
constructor(postId: number) {
|
||||
super(`Post '${postId}' not found!`);
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupNotFoundGqlError extends GraphQLError {
|
||||
constructor(groupId: number) {
|
||||
super(`Group '${groupId}' not found!`);
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,63 @@
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Partly taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {EventEmitter} from "events";
|
||||
import * as fsx from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as winston from "winston";
|
||||
require('winston-daily-rotate-file');
|
||||
|
||||
const configPath = "config.yaml";
|
||||
const defaultConfig = __dirname + "/../default-config.yaml";
|
||||
|
||||
// ensure that the config exists by copying the default config.
|
||||
if (!(fsx.pathExistsSync(configPath))) {
|
||||
fsx.copySync(defaultConfig, configPath);
|
||||
} else {
|
||||
const conf = yaml.safeLoad(fsx.readFileSync(configPath, "utf-8"));
|
||||
const defConf = yaml.safeLoad(fsx.readFileSync(defaultConfig, "utf-8"));
|
||||
fsx.writeFileSync(configPath, yaml.safeDump(Object.assign(defConf, conf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines global variables to be used.
|
||||
*/
|
||||
namespace globals {
|
||||
export const config = yaml.safeLoad(fsx.readFileSync("config.yaml", "utf-8"));
|
||||
// @ts-ignore
|
||||
export const logger = winston.createLogger({
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.colorize(),
|
||||
winston.format.printf(({level, message, timestamp}) => {
|
||||
return `${timestamp} ${level}: ${message}`;
|
||||
}),
|
||||
),
|
||||
level: config.logging.level,
|
||||
}),
|
||||
// @ts-ignore
|
||||
new (winston.transports.DailyRotateFile)({
|
||||
dirname: "logs",
|
||||
filename: "gv-%DATE%.log",
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.printf(({level, message, timestamp}) => {
|
||||
return `${timestamp} ${level}: ${message}`;
|
||||
}),
|
||||
),
|
||||
json: false,
|
||||
maxFiles: "7d",
|
||||
zippedArchive: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
export const internalEmitter = new EventEmitter();
|
||||
}
|
||||
|
||||
export default globals;
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Partly taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {EventEmitter} from "events";
|
||||
import * as fsx from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as winston from "winston";
|
||||
require('winston-daily-rotate-file');
|
||||
|
||||
const configPath = "config.yaml";
|
||||
const defaultConfig = __dirname + "/../default-config.yaml";
|
||||
|
||||
// ensure that the config exists by copying the default config.
|
||||
if (!(fsx.pathExistsSync(configPath))) {
|
||||
fsx.copySync(defaultConfig, configPath);
|
||||
} else {
|
||||
const conf = yaml.safeLoad(fsx.readFileSync(configPath, "utf-8"));
|
||||
const defConf = yaml.safeLoad(fsx.readFileSync(defaultConfig, "utf-8"));
|
||||
fsx.writeFileSync(configPath, yaml.safeDump(Object.assign(defConf, conf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines global variables to be used.
|
||||
*/
|
||||
namespace globals {
|
||||
export const config = yaml.safeLoad(fsx.readFileSync("config.yaml", "utf-8"));
|
||||
// @ts-ignore
|
||||
export const logger = winston.createLogger({
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.colorize(),
|
||||
winston.format.printf(({level, message, timestamp}) => {
|
||||
return `${timestamp} ${level}: ${message}`;
|
||||
}),
|
||||
),
|
||||
level: config.logging.level,
|
||||
}),
|
||||
// @ts-ignore
|
||||
new (winston.transports.DailyRotateFile)({
|
||||
dirname: "logs",
|
||||
filename: "gv-%DATE%.log",
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.printf(({level, message, timestamp}) => {
|
||||
return `${timestamp} ${level}: ${message}`;
|
||||
}),
|
||||
),
|
||||
json: false,
|
||||
maxFiles: "7d",
|
||||
zippedArchive: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
export const internalEmitter = new EventEmitter();
|
||||
}
|
||||
|
||||
export default globals;
|
||||
|
@ -1,36 +1,36 @@
|
||||
import * as MarkdownIt from "markdown-it/lib";
|
||||
import globals from "./globals";
|
||||
|
||||
namespace markdown {
|
||||
|
||||
const md = new MarkdownIt();
|
||||
|
||||
for (const pluginName of globals.config.markdown.plugins) {
|
||||
try {
|
||||
const plugin = require(pluginName);
|
||||
if (plugin) {
|
||||
md.use(plugin);
|
||||
}
|
||||
} catch (err) {
|
||||
globals.logger.warn(`Markdown-it plugin '${pluginName}' not found!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the markdown string inline (without blocks).
|
||||
* @param markdownString
|
||||
*/
|
||||
export function renderInline(markdownString: string) {
|
||||
return md.renderInline(markdownString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the markdown string.
|
||||
* @param markdownString
|
||||
*/
|
||||
export function render(markdownString: string) {
|
||||
return md.render(markdownString);
|
||||
}
|
||||
}
|
||||
|
||||
export default markdown;
|
||||
import * as MarkdownIt from "markdown-it/lib";
|
||||
import globals from "./globals";
|
||||
|
||||
namespace markdown {
|
||||
|
||||
const md = new MarkdownIt();
|
||||
|
||||
for (const pluginName of globals.config.markdown.plugins) {
|
||||
try {
|
||||
const plugin = require(pluginName);
|
||||
if (plugin) {
|
||||
md.use(plugin);
|
||||
}
|
||||
} catch (err) {
|
||||
globals.logger.warn(`Markdown-it plugin '${pluginName}' not found!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the markdown string inline (without blocks).
|
||||
* @param markdownString
|
||||
*/
|
||||
export function renderInline(markdownString: string) {
|
||||
return md.renderInline(markdownString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the markdown string.
|
||||
* @param markdownString
|
||||
*/
|
||||
export function render(markdownString: string) {
|
||||
return md.render(markdownString);
|
||||
}
|
||||
}
|
||||
|
||||
export default markdown;
|
||||
|
@ -1,16 +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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,44 +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"));
|
||||
}
|
||||
}
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +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");
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,36 @@
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {EventParticipant} from "./EventParticipant";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Event extends Model<Event> {
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public name: string;
|
||||
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public dueDate: Date;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
|
||||
@BelongsTo(() => Group, "groupId")
|
||||
public rGroup: Group;
|
||||
|
||||
@BelongsToMany(() => User, () => EventParticipant)
|
||||
public rParticipants: User[];
|
||||
|
||||
public async group(): Promise<Group> {
|
||||
return await this.$get("rGroup") as Group;
|
||||
}
|
||||
|
||||
public async participants({first, offset}: {first: number, offset: number}): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rParticipants", {limit, offset}) as User[];
|
||||
}
|
||||
}
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {EventParticipant} from "./EventParticipant";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Event extends Model<Event> {
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public name: string;
|
||||
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public dueDate: Date;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
|
||||
@BelongsTo(() => Group, "groupId")
|
||||
public rGroup: Group;
|
||||
|
||||
@BelongsToMany(() => User, () => EventParticipant)
|
||||
public rParticipants: User[];
|
||||
|
||||
public async group(): Promise<Group> {
|
||||
return await this.$get("rGroup") as Group;
|
||||
}
|
||||
|
||||
public async participants({first, offset}: {first: number, offset: number}): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rParticipants", {limit, offset}) as User[];
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {Event} from "./Event";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class EventParticipant extends Model<EventParticipant> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Event)
|
||||
@Column({allowNull: false})
|
||||
public eventId: number;
|
||||
}
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {Event} from "./Event";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class EventParticipant extends Model<EventParticipant> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Event)
|
||||
@Column({allowNull: false})
|
||||
public eventId: number;
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import {Column, ForeignKey, Model, NotNull, PrimaryKey, Table} from "sequelize-typescript";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Friendship extends Model<Friendship> {
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@PrimaryKey
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@PrimaryKey
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public friendId: number;
|
||||
}
|
||||
import {Column, ForeignKey, Model, NotNull, PrimaryKey, Table} from "sequelize-typescript";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Friendship extends Model<Friendship> {
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@PrimaryKey
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@PrimaryKey
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public friendId: number;
|
||||
}
|
||||
|
@ -1,64 +1,64 @@
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, HasMany, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {ChatRoom} from "./ChatRoom";
|
||||
import {Event} from "./Event";
|
||||
import {GroupAdmin} from "./GroupAdmin";
|
||||
import {GroupMember} from "./GroupMember";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Group extends Model<Group> {
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public name: string;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public creatorId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => ChatRoom)
|
||||
@Column({allowNull: false})
|
||||
public chatId: number;
|
||||
|
||||
@BelongsTo(() => User, "creatorId")
|
||||
public rCreator: User;
|
||||
|
||||
@BelongsToMany(() => User, () => GroupAdmin)
|
||||
public rAdmins: User[];
|
||||
|
||||
@BelongsToMany(() => User, () => GroupMember)
|
||||
public rMembers: User[];
|
||||
|
||||
@BelongsTo(() => ChatRoom)
|
||||
public rChat: ChatRoom;
|
||||
|
||||
@HasMany(() => Event, "groupId")
|
||||
public rEvents: Event[];
|
||||
|
||||
public async creator(): Promise<User> {
|
||||
return await this.$get("rCreator") as User;
|
||||
}
|
||||
|
||||
public async admins({first, offset}: { first: number, offset: number }): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rAdmins", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
public async members({first, offset}: { first: number, offset: number }): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rMembers", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
public async chat(): Promise<ChatRoom> {
|
||||
return await this.$get("rChat") as ChatRoom;
|
||||
}
|
||||
|
||||
public async events({first, offset}: { first: number, offset: number }): Promise<Event[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rEvents", {limit, offset}) as Event[];
|
||||
}
|
||||
}
|
||||
import {BelongsTo, BelongsToMany, Column, ForeignKey, HasMany, Model, NotNull, Table} from "sequelize-typescript";
|
||||
import {ChatRoom} from "./ChatRoom";
|
||||
import {Event} from "./Event";
|
||||
import {GroupAdmin} from "./GroupAdmin";
|
||||
import {GroupMember} from "./GroupMember";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Group extends Model<Group> {
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public name: string;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public creatorId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => ChatRoom)
|
||||
@Column({allowNull: false})
|
||||
public chatId: number;
|
||||
|
||||
@BelongsTo(() => User, "creatorId")
|
||||
public rCreator: User;
|
||||
|
||||
@BelongsToMany(() => User, () => GroupAdmin)
|
||||
public rAdmins: User[];
|
||||
|
||||
@BelongsToMany(() => User, () => GroupMember)
|
||||
public rMembers: User[];
|
||||
|
||||
@BelongsTo(() => ChatRoom)
|
||||
public rChat: ChatRoom;
|
||||
|
||||
@HasMany(() => Event, "groupId")
|
||||
public rEvents: Event[];
|
||||
|
||||
public async creator(): Promise<User> {
|
||||
return await this.$get("rCreator") as User;
|
||||
}
|
||||
|
||||
public async admins({first, offset}: { first: number, offset: number }): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rAdmins", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
public async members({first, offset}: { first: number, offset: number }): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rMembers", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
public async chat(): Promise<ChatRoom> {
|
||||
return await this.$get("rChat") as ChatRoom;
|
||||
}
|
||||
|
||||
public async events({first, offset}: { first: number, offset: number }): Promise<Event[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rEvents", {limit, offset}) as Event[];
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class GroupAdmin extends Model<GroupAdmin> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
}
|
||||
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class GroupAdmin extends Model<GroupAdmin> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class GroupMember extends Model<GroupMember> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
}
|
||||
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class GroupMember extends Model<GroupMember> {
|
||||
@NotNull
|
||||
@ForeignKey(() => User)
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@NotNull
|
||||
@ForeignKey(() => Group)
|
||||
@Column({allowNull: false})
|
||||
public groupId: number;
|
||||
}
|
||||
|
@ -1,70 +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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,41 +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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,289 +1,289 @@
|
||||
import * as sqz from "sequelize";
|
||||
import {
|
||||
BelongsTo,
|
||||
BelongsToMany,
|
||||
Column,
|
||||
CreatedAt,
|
||||
HasMany,
|
||||
Model,
|
||||
NotNull,
|
||||
Table,
|
||||
Unique,
|
||||
UpdatedAt,
|
||||
} from "sequelize-typescript";
|
||||
import {RequestNotFoundError} from "../errors/RequestNotFoundError";
|
||||
import {UserNotFoundError} from "../errors/UserNotFoundError";
|
||||
import {ChatMember} from "./ChatMember";
|
||||
import {ChatMessage} from "./ChatMessage";
|
||||
import {ChatRoom} from "./ChatRoom";
|
||||
import {Event} from "./Event";
|
||||
import {EventParticipant} from "./EventParticipant";
|
||||
import {Friendship} from "./Friendship";
|
||||
import {Group} from "./Group";
|
||||
import {GroupAdmin} from "./GroupAdmin";
|
||||
import {GroupMember} from "./GroupMember";
|
||||
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, "userId")
|
||||
public rFriends: User[];
|
||||
|
||||
@BelongsToMany(() => User, () => Friendship, "friendId")
|
||||
public rFriendOf: User[];
|
||||
|
||||
@BelongsToMany(() => Post, () => PostVote)
|
||||
public votes: Array<Post & {PostVote: PostVote}>;
|
||||
|
||||
@BelongsToMany(() => ChatRoom, () => ChatMember)
|
||||
public rChats: ChatRoom[];
|
||||
|
||||
@BelongsToMany(() => Group, () => GroupAdmin)
|
||||
public rAdministratedGroups: Group[];
|
||||
|
||||
@BelongsToMany(() => Event, () => EventParticipant)
|
||||
public rEvents: Event[];
|
||||
|
||||
@BelongsToMany(() => Group, () => GroupMember)
|
||||
public rGroups: Group[];
|
||||
|
||||
@HasMany(() => Post, "authorId")
|
||||
public rPosts: Post[];
|
||||
|
||||
@HasMany(() => Request, "senderId")
|
||||
public rSentRequests: Request[];
|
||||
|
||||
@HasMany(() => Request, "receiverId")
|
||||
public rReceivedRequests: Request[];
|
||||
|
||||
@HasMany(() => ChatMessage, "authorId")
|
||||
public messages: ChatMessage[];
|
||||
|
||||
@HasMany(() => Group, "creatorId")
|
||||
public rCreatedGroups: Group[];
|
||||
|
||||
@CreatedAt
|
||||
public readonly createdAt!: Date;
|
||||
|
||||
@UpdatedAt
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
/**
|
||||
* The name of the user
|
||||
*/
|
||||
public get name(): string {
|
||||
return this.getDataValue("username");
|
||||
}
|
||||
|
||||
/**
|
||||
* The date the user joined the network
|
||||
*/
|
||||
public get joinedAt(): Date {
|
||||
return this.getDataValue("createdAt");
|
||||
}
|
||||
|
||||
/**
|
||||
* The points of the user
|
||||
*/
|
||||
public get points(): number {
|
||||
return this.rankpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* The level of the user which is the points divided by 100
|
||||
*/
|
||||
public get level(): number {
|
||||
return Math.ceil(this.rankpoints / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* All friends of the user
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async friends({first, offset}: {first: number, offset: number}): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rFriendOf", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of the users friends.
|
||||
*/
|
||||
public async friendCount(): Promise<number> {
|
||||
return this.$count("rFriends");
|
||||
}
|
||||
|
||||
/**
|
||||
* The chats the user has joined
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async chats({first, offset}: {first: number, offset: number}): Promise<ChatRoom[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rChats", {limit, offset}) as ChatRoom[];
|
||||
}
|
||||
|
||||
/**
|
||||
* the number of chats the user has
|
||||
*/
|
||||
public async chatCount(): Promise<number> {
|
||||
return this.$count("rChats");
|
||||
}
|
||||
|
||||
/**
|
||||
* All active requests the user ha ssent
|
||||
*/
|
||||
public async sentRequests(): Promise<Request[]> {
|
||||
return await this.$get("rSentRequests") as Request[];
|
||||
}
|
||||
|
||||
/**
|
||||
* All requests the user has received
|
||||
*/
|
||||
public async receivedRequests(): Promise<Request[]> {
|
||||
return await this.$get("rReceivedRequests") as Request[];
|
||||
}
|
||||
|
||||
public async posts({first, offset}: {first: number, offset: number}): Promise<Post[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rPosts", {limit, offset}) as Post[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* use {@link postCount} instead
|
||||
*/
|
||||
public async numberOfPosts(): Promise<number> {
|
||||
return this.postCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* number of posts the user created
|
||||
*/
|
||||
public async postCount(): Promise<number> {
|
||||
return this.$count("rPosts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user is the admin of
|
||||
*/
|
||||
public async administratedGroups(): Promise<Group[]> {
|
||||
return await this.$get("rAdministratedGroups") as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user has created
|
||||
*/
|
||||
public async createdGroups(): Promise<Group[]> {
|
||||
return await this.$get("rCreatedGroups") as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user is a member of
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async groups({first, offset}: {first: number, offset: number}): Promise<Group[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rGroups", {limit, offset}) as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of groups the user has joined
|
||||
*/
|
||||
public async groupCount(): Promise<number> {
|
||||
return this.$count("rGroups");
|
||||
}
|
||||
|
||||
/**
|
||||
* Events the user has joined
|
||||
*/
|
||||
public async events(): Promise<Event[]> {
|
||||
return await this.$get("rEvents") as Event[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of events the user is participating in.
|
||||
*/
|
||||
public async eventCount(): Promise<number> {
|
||||
return this.$count("rEvents");
|
||||
}
|
||||
|
||||
/**
|
||||
* Denys a request the user has received
|
||||
* @param sender
|
||||
* @param type
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a request the user has received
|
||||
* @param sender
|
||||
* @param type
|
||||
*/
|
||||
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 Friendship.bulkCreate([
|
||||
{userId: this.id, friendId: sender},
|
||||
{userId: sender, friendId: this.id},
|
||||
], {ignoreDuplicates: true});
|
||||
await request.destroy();
|
||||
}
|
||||
} else {
|
||||
throw new RequestNotFoundError(sender, this.id, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a user from the users friends
|
||||
* @param friendId
|
||||
*/
|
||||
public async removeFriend(friendId: number) {
|
||||
const friend = await User.findByPk(friendId);
|
||||
if (friend) {
|
||||
await this.$remove("rFriends", friend);
|
||||
await this.$remove("rFriendOf", friend);
|
||||
return true;
|
||||
} else {
|
||||
throw new UserNotFoundError(friendId);
|
||||
}
|
||||
}
|
||||
}
|
||||
import * as sqz from "sequelize";
|
||||
import {
|
||||
BelongsTo,
|
||||
BelongsToMany,
|
||||
Column,
|
||||
CreatedAt,
|
||||
HasMany,
|
||||
Model,
|
||||
NotNull,
|
||||
Table,
|
||||
Unique,
|
||||
UpdatedAt,
|
||||
} from "sequelize-typescript";
|
||||
import {RequestNotFoundError} from "../errors/RequestNotFoundError";
|
||||
import {UserNotFoundError} from "../errors/UserNotFoundError";
|
||||
import {ChatMember} from "./ChatMember";
|
||||
import {ChatMessage} from "./ChatMessage";
|
||||
import {ChatRoom} from "./ChatRoom";
|
||||
import {Event} from "./Event";
|
||||
import {EventParticipant} from "./EventParticipant";
|
||||
import {Friendship} from "./Friendship";
|
||||
import {Group} from "./Group";
|
||||
import {GroupAdmin} from "./GroupAdmin";
|
||||
import {GroupMember} from "./GroupMember";
|
||||
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, "userId")
|
||||
public rFriends: User[];
|
||||
|
||||
@BelongsToMany(() => User, () => Friendship, "friendId")
|
||||
public rFriendOf: User[];
|
||||
|
||||
@BelongsToMany(() => Post, () => PostVote)
|
||||
public votes: Array<Post & {PostVote: PostVote}>;
|
||||
|
||||
@BelongsToMany(() => ChatRoom, () => ChatMember)
|
||||
public rChats: ChatRoom[];
|
||||
|
||||
@BelongsToMany(() => Group, () => GroupAdmin)
|
||||
public rAdministratedGroups: Group[];
|
||||
|
||||
@BelongsToMany(() => Event, () => EventParticipant)
|
||||
public rEvents: Event[];
|
||||
|
||||
@BelongsToMany(() => Group, () => GroupMember)
|
||||
public rGroups: Group[];
|
||||
|
||||
@HasMany(() => Post, "authorId")
|
||||
public rPosts: Post[];
|
||||
|
||||
@HasMany(() => Request, "senderId")
|
||||
public rSentRequests: Request[];
|
||||
|
||||
@HasMany(() => Request, "receiverId")
|
||||
public rReceivedRequests: Request[];
|
||||
|
||||
@HasMany(() => ChatMessage, "authorId")
|
||||
public messages: ChatMessage[];
|
||||
|
||||
@HasMany(() => Group, "creatorId")
|
||||
public rCreatedGroups: Group[];
|
||||
|
||||
@CreatedAt
|
||||
public readonly createdAt!: Date;
|
||||
|
||||
@UpdatedAt
|
||||
public readonly updatedAt!: Date;
|
||||
|
||||
/**
|
||||
* The name of the user
|
||||
*/
|
||||
public get name(): string {
|
||||
return this.getDataValue("username");
|
||||
}
|
||||
|
||||
/**
|
||||
* The date the user joined the network
|
||||
*/
|
||||
public get joinedAt(): Date {
|
||||
return this.getDataValue("createdAt");
|
||||
}
|
||||
|
||||
/**
|
||||
* The points of the user
|
||||
*/
|
||||
public get points(): number {
|
||||
return this.rankpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* The level of the user which is the points divided by 100
|
||||
*/
|
||||
public get level(): number {
|
||||
return Math.ceil(this.rankpoints / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* All friends of the user
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async friends({first, offset}: {first: number, offset: number}): Promise<User[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rFriendOf", {limit, offset}) as User[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of the users friends.
|
||||
*/
|
||||
public async friendCount(): Promise<number> {
|
||||
return this.$count("rFriends");
|
||||
}
|
||||
|
||||
/**
|
||||
* The chats the user has joined
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async chats({first, offset}: {first: number, offset: number}): Promise<ChatRoom[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rChats", {limit, offset}) as ChatRoom[];
|
||||
}
|
||||
|
||||
/**
|
||||
* the number of chats the user has
|
||||
*/
|
||||
public async chatCount(): Promise<number> {
|
||||
return this.$count("rChats");
|
||||
}
|
||||
|
||||
/**
|
||||
* All active requests the user ha ssent
|
||||
*/
|
||||
public async sentRequests(): Promise<Request[]> {
|
||||
return await this.$get("rSentRequests") as Request[];
|
||||
}
|
||||
|
||||
/**
|
||||
* All requests the user has received
|
||||
*/
|
||||
public async receivedRequests(): Promise<Request[]> {
|
||||
return await this.$get("rReceivedRequests") as Request[];
|
||||
}
|
||||
|
||||
public async posts({first, offset}: {first: number, offset: number}): Promise<Post[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rPosts", {limit, offset}) as Post[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* use {@link postCount} instead
|
||||
*/
|
||||
public async numberOfPosts(): Promise<number> {
|
||||
return this.postCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* number of posts the user created
|
||||
*/
|
||||
public async postCount(): Promise<number> {
|
||||
return this.$count("rPosts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user is the admin of
|
||||
*/
|
||||
public async administratedGroups(): Promise<Group[]> {
|
||||
return await this.$get("rAdministratedGroups") as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user has created
|
||||
*/
|
||||
public async createdGroups(): Promise<Group[]> {
|
||||
return await this.$get("rCreatedGroups") as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the user is a member of
|
||||
* @param first
|
||||
* @param offset
|
||||
*/
|
||||
public async groups({first, offset}: {first: number, offset: number}): Promise<Group[]> {
|
||||
const limit = first || 10;
|
||||
offset = offset || 0;
|
||||
return await this.$get("rGroups", {limit, offset}) as Group[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of groups the user has joined
|
||||
*/
|
||||
public async groupCount(): Promise<number> {
|
||||
return this.$count("rGroups");
|
||||
}
|
||||
|
||||
/**
|
||||
* Events the user has joined
|
||||
*/
|
||||
public async events(): Promise<Event[]> {
|
||||
return await this.$get("rEvents") as Event[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of events the user is participating in.
|
||||
*/
|
||||
public async eventCount(): Promise<number> {
|
||||
return this.$count("rEvents");
|
||||
}
|
||||
|
||||
/**
|
||||
* Denys a request the user has received
|
||||
* @param sender
|
||||
* @param type
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a request the user has received
|
||||
* @param sender
|
||||
* @param type
|
||||
*/
|
||||
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 Friendship.bulkCreate([
|
||||
{userId: this.id, friendId: sender},
|
||||
{userId: sender, friendId: this.id},
|
||||
], {ignoreDuplicates: true});
|
||||
await request.destroy();
|
||||
}
|
||||
} else {
|
||||
throw new RequestNotFoundError(sender, this.id, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a user from the users friends
|
||||
* @param friendId
|
||||
*/
|
||||
public async removeFriend(friendId: number) {
|
||||
const friend = await User.findByPk(friendId);
|
||||
if (friend) {
|
||||
await this.$remove("rFriends", friend);
|
||||
await this.$remove("rFriendOf", friend);
|
||||
return true;
|
||||
} else {
|
||||
throw new UserNotFoundError(friendId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
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";
|
||||
export {Group} from "./Group";
|
||||
export {GroupAdmin} from "./GroupAdmin";
|
||||
export {GroupMember} from "./GroupMember";
|
||||
export {Event} from "./Event";
|
||||
export {EventParticipant} from "./EventParticipant";
|
||||
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";
|
||||
export {Group} from "./Group";
|
||||
export {GroupAdmin} from "./GroupAdmin";
|
||||
export {GroupMember} from "./GroupMember";
|
||||
export {Event} from "./Event";
|
||||
export {EventParticipant} from "./EventParticipant";
|
||||
|
@ -1,11 +1,11 @@
|
||||
export namespace is {
|
||||
const emailRegex = /\S+?@\S+?(\.\S+?)?\.\w{2,3}(.\w{2-3})?/g;
|
||||
|
||||
/**
|
||||
* Tests if a string is a valid email.
|
||||
* @param testString
|
||||
*/
|
||||
export function email(testString: string) {
|
||||
return emailRegex.test(testString);
|
||||
}
|
||||
}
|
||||
export namespace is {
|
||||
const emailRegex = /\S+?@\S+?(\.\S+?)?\.\w{2,3}(.\w{2-3})?/g;
|
||||
|
||||
/**
|
||||
* Tests if a string is a valid email.
|
||||
* @param testString
|
||||
*/
|
||||
export function email(testString: string) {
|
||||
return emailRegex.test(testString);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
body
|
||||
font-family: Arial, serif
|
||||
|
||||
#server-error
|
||||
*
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
text-align: center
|
||||
code
|
||||
font-size: 2em
|
||||
body
|
||||
font-family: Arial, serif
|
||||
|
||||
#server-error
|
||||
*
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
text-align: center
|
||||
code
|
||||
font-size: 2em
|
||||
|
@ -1,98 +1,98 @@
|
||||
import {Router} from "express";
|
||||
import {Namespace, Server} from "socket.io";
|
||||
import dataaccess from "../lib/dataaccess";
|
||||
import globals from "../lib/globals";
|
||||
import {InternalEvents} from "../lib/InternalEvents";
|
||||
import {ChatMessage, ChatRoom, Post, Request, User} from "../lib/models";
|
||||
import Route from "../lib/Route";
|
||||
|
||||
/**
|
||||
* list of chatroom socket namespaces.
|
||||
*/
|
||||
const chatRooms: Namespace[] = [];
|
||||
|
||||
/**
|
||||
* Class for the home route.
|
||||
*/
|
||||
class HomeRoute extends Route {
|
||||
/**
|
||||
* Constructor, creates new router.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.router = Router();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous init for socket.io.
|
||||
* @param io - the io instance
|
||||
*/
|
||||
public async init(io: Server) {
|
||||
this.io = io;
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
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}));
|
||||
} else {
|
||||
socket.emit("error", "Not logged in!");
|
||||
}
|
||||
});
|
||||
globals.internalEmitter.on(InternalEvents.REQUESTCREATE, async (request: Request) => {
|
||||
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}));
|
||||
});
|
||||
});
|
||||
|
||||
const chats = await dataaccess.getAllChats();
|
||||
for (const chat of chats) {
|
||||
chatRooms[chat.id] = this.getChatSocketNamespace(chat.id);
|
||||
}
|
||||
globals.internalEmitter.on(InternalEvents.CHATCREATE, (chat: ChatRoom) => {
|
||||
chatRooms[chat.id] = this.getChatSocketNamespace(chat.id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the instance by dereferencing the router and resolver.
|
||||
*/
|
||||
public async destroy(): Promise<void> {
|
||||
this.router = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace socket for a chat socket.
|
||||
* @param chatId
|
||||
*/
|
||||
private getChatSocketNamespace(chatId: number) {
|
||||
if (chatRooms[chatId]) {
|
||||
return chatRooms[chatId];
|
||||
}
|
||||
const chatNs = this.io.of(`/chat/${chatId}`);
|
||||
chatNs.on("connection", (socket) => {
|
||||
socket.on("chatMessage", async (content) => {
|
||||
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}));
|
||||
} else {
|
||||
socket.emit("error", "Not logged in!");
|
||||
}
|
||||
});
|
||||
globals.internalEmitter.on(InternalEvents.GQLCHATMESSAGE, async (message: ChatMessage) => {
|
||||
if ((await message.$get("chat") as ChatRoom).id === chatId) {
|
||||
socket.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent}));
|
||||
}
|
||||
});
|
||||
});
|
||||
return chatNs;
|
||||
}
|
||||
}
|
||||
|
||||
export default HomeRoute;
|
||||
import {Router} from "express";
|
||||
import {Namespace, Server} from "socket.io";
|
||||
import dataaccess from "../lib/dataaccess";
|
||||
import globals from "../lib/globals";
|
||||
import {InternalEvents} from "../lib/InternalEvents";
|
||||
import {ChatMessage, ChatRoom, Post, Request, User} from "../lib/models";
|
||||
import Route from "../lib/Route";
|
||||
|
||||
/**
|
||||
* list of chatroom socket namespaces.
|
||||
*/
|
||||
const chatRooms: Namespace[] = [];
|
||||
|
||||
/**
|
||||
* Class for the home route.
|
||||
*/
|
||||
class HomeRoute extends Route {
|
||||
/**
|
||||
* Constructor, creates new router.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.router = Router();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous init for socket.io.
|
||||
* @param io - the io instance
|
||||
*/
|
||||
public async init(io: Server) {
|
||||
this.io = io;
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
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}));
|
||||
} else {
|
||||
socket.emit("error", "Not logged in!");
|
||||
}
|
||||
});
|
||||
globals.internalEmitter.on(InternalEvents.REQUESTCREATE, async (request: Request) => {
|
||||
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}));
|
||||
});
|
||||
});
|
||||
|
||||
const chats = await dataaccess.getAllChats();
|
||||
for (const chat of chats) {
|
||||
chatRooms[chat.id] = this.getChatSocketNamespace(chat.id);
|
||||
}
|
||||
globals.internalEmitter.on(InternalEvents.CHATCREATE, (chat: ChatRoom) => {
|
||||
chatRooms[chat.id] = this.getChatSocketNamespace(chat.id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the instance by dereferencing the router and resolver.
|
||||
*/
|
||||
public async destroy(): Promise<void> {
|
||||
this.router = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace socket for a chat socket.
|
||||
* @param chatId
|
||||
*/
|
||||
private getChatSocketNamespace(chatId: number) {
|
||||
if (chatRooms[chatId]) {
|
||||
return chatRooms[chatId];
|
||||
}
|
||||
const chatNs = this.io.of(`/chat/${chatId}`);
|
||||
chatNs.on("connection", (socket) => {
|
||||
socket.on("chatMessage", async (content) => {
|
||||
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}));
|
||||
} else {
|
||||
socket.emit("error", "Not logged in!");
|
||||
}
|
||||
});
|
||||
globals.internalEmitter.on(InternalEvents.GQLCHATMESSAGE, async (message: ChatMessage) => {
|
||||
if ((await message.$get("chat") as ChatRoom).id === chatId) {
|
||||
socket.emit("chatMessage", Object.assign(message, {htmlContent: message.htmlContent}));
|
||||
}
|
||||
});
|
||||
});
|
||||
return chatNs;
|
||||
}
|
||||
}
|
||||
|
||||
export default HomeRoute;
|
||||
|
@ -1,34 +1,34 @@
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {Router} from "express";
|
||||
import {Server} from "socket.io";
|
||||
|
||||
import HomeRoute from "./home";
|
||||
|
||||
const homeRoute = new HomeRoute();
|
||||
|
||||
/**
|
||||
* Namespace to manage the routes of the server.
|
||||
* Allows easier assignments of graphql endpoints, socket.io connections and routers when
|
||||
* used with {@link Route}.
|
||||
*/
|
||||
namespace routes {
|
||||
export const router = Router();
|
||||
|
||||
router.use("/", homeRoute.router);
|
||||
|
||||
/**
|
||||
* Assigns the io listeners or namespaces to the routes
|
||||
* @param io
|
||||
*/
|
||||
export const ioListeners = async (io: Server) => {
|
||||
await homeRoute.init(io);
|
||||
};
|
||||
}
|
||||
|
||||
export default routes;
|
||||
/**
|
||||
* @author Trivernis
|
||||
* @remarks
|
||||
*
|
||||
* Taken from {@link https://github.com/Trivernis/whooshy}
|
||||
*/
|
||||
|
||||
import {Router} from "express";
|
||||
import {Server} from "socket.io";
|
||||
|
||||
import HomeRoute from "./home";
|
||||
|
||||
const homeRoute = new HomeRoute();
|
||||
|
||||
/**
|
||||
* Namespace to manage the routes of the server.
|
||||
* Allows easier assignments of graphql endpoints, socket.io connections and routers when
|
||||
* used with {@link Route}.
|
||||
*/
|
||||
namespace routes {
|
||||
export const router = Router();
|
||||
|
||||
router.use("/", homeRoute.router);
|
||||
|
||||
/**
|
||||
* Assigns the io listeners or namespaces to the routes
|
||||
* @param io
|
||||
*/
|
||||
export const ioListeners = async (io: Server) => {
|
||||
await homeRoute.init(io);
|
||||
};
|
||||
}
|
||||
|
||||
export default routes;
|
||||
|
@ -1,12 +1,12 @@
|
||||
html
|
||||
head
|
||||
link(href="/stylesheets/style.css" rel="stylesheet" type="text/css")
|
||||
body
|
||||
div#server-error
|
||||
div
|
||||
h1 Page not found!
|
||||
div
|
||||
code 404
|
||||
div
|
||||
h1 The page "#{url}" was not found.
|
||||
|
||||
html
|
||||
head
|
||||
link(href="/stylesheets/style.css" rel="stylesheet" type="text/css")
|
||||
body
|
||||
div#server-error
|
||||
div
|
||||
h1 Page not found!
|
||||
div
|
||||
code 404
|
||||
div
|
||||
h1 The page "#{url}" was not found.
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
html
|
||||
head
|
||||
link(href="/stylesheets/style.css" rel="stylesheet" type="text/css")
|
||||
body
|
||||
div#server-error
|
||||
div
|
||||
h1 Internal server error!
|
||||
div
|
||||
code 500
|
||||
div
|
||||
h2 Oops the server couldn't handle that.
|
||||
div
|
||||
p You might want to report this.
|
||||
html
|
||||
head
|
||||
link(href="/stylesheets/style.css" rel="stylesheet" type="text/css")
|
||||
body
|
||||
div#server-error
|
||||
div
|
||||
h1 Internal server error!
|
||||
div
|
||||
code 500
|
||||
div
|
||||
h2 Oops the server couldn't handle that.
|
||||
div
|
||||
p You might want to report this.
|
||||
|
@ -1,24 +1,24 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "./dist",
|
||||
"sourceMap": true,
|
||||
"target": "es2018",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "./dist",
|
||||
"sourceMap": true,
|
||||
"target": "es2018",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [],
|
||||
"rules": {
|
||||
"max-line-length": {
|
||||
"options": [120]
|
||||
},
|
||||
"new-parens": true,
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"cyclomatic-complexity": true,
|
||||
"brace-style": "1tbs",
|
||||
"semicolon": true,
|
||||
"indent": [true, "spaces", 4],
|
||||
"no-shadowed-variable": true,
|
||||
"no-console": {
|
||||
"severity": "warning",
|
||||
"options": ["debug", "info", "log", "time", "timeEnd", "trace"]
|
||||
},
|
||||
"no-namespace": false,
|
||||
"no-internal-module": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-var-requires": false
|
||||
},
|
||||
"jsRules": {
|
||||
"max-line-length": {
|
||||
"options": [120]
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [],
|
||||
"rules": {
|
||||
"max-line-length": {
|
||||
"options": [120]
|
||||
},
|
||||
"new-parens": true,
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"cyclomatic-complexity": true,
|
||||
"brace-style": "1tbs",
|
||||
"semicolon": true,
|
||||
"indent": [true, "spaces", 4],
|
||||
"no-shadowed-variable": true,
|
||||
"no-console": {
|
||||
"severity": "warning",
|
||||
"options": ["debug", "info", "log", "time", "timeEnd", "trace"]
|
||||
},
|
||||
"no-namespace": false,
|
||||
"no-internal-module": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-var-requires": false
|
||||
},
|
||||
"jsRules": {
|
||||
"max-line-length": {
|
||||
"options": [120]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue