5 Commits
master ... dev

16 changed files with 66 additions and 104 deletions

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Benjamin Kyd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -30,6 +30,6 @@ export class APIErrors extends API {
endpoint() { endpoint() {
this.res this.res
.status(this.errors.status.code) .status(this.errors.status.code)
.end(JSON.stringify(this.errors, false, 4)); .send(JSON.stringify(this.errors, false, 4));
} }
} }

View File

@@ -29,6 +29,8 @@ export class NoteAPI extends API {
} }
endpoint() { endpoint() {
this.res.status(201).end(JSON.stringify(this.response, false, 4)); this.res
.status(201)
.send(JSON.stringify(this.response, false, 4));
} }
} }

View File

@@ -28,6 +28,8 @@ export class PermaLinkAPI extends API {
} }
endpoint() { endpoint() {
this.res.status(201).end(JSON.stringify(this.response, false, 4)); this.res
.status(201)
.send(JSON.stringify(this.response, false, 4));
} }
} }

View File

@@ -42,7 +42,9 @@ export class UserAPI extends API {
} }
} }
endpoint() { async endpoint() {
this.res.status(200).end(JSON.stringify(this.response, false, 4)); await this.res
.status(200)
.send(JSON.stringify(this.response, false, 4));
} }
} }

View File

@@ -2,6 +2,7 @@ import {ControllerHandler} from './controllerHandler';
import {API} from './api/api'; import {API} from './api/api';
import {Database} from '../models/database/database' import {Database} from '../models/database/database'
import {User} from '../models/user/user'; import {User} from '../models/user/user';
import { Logger } from '../models/logger';
export class LoginController extends ControllerHandler { export class LoginController extends ControllerHandler {
static async authenticate(req, res, next) { static async authenticate(req, res, next) {
@@ -17,11 +18,7 @@ export class LoginController extends ControllerHandler {
if (!password) errors.addError(400, 'Bad request', 'A password is required'); if (!password) errors.addError(400, 'Bad request', 'A password is required');
if (!username && !email) errors.addError(400, 'Bad request', 'A username or email is required'); if (!username && !email) errors.addError(400, 'Bad request', 'A username or email is required');
if (errors.count() > 0) { if (errors.count() > 0) return next(errors);
errors.endpoint();
next();
return;
}
let user; let user;
if (!username /*If they're loging in with email*/) { if (!username /*If they're loging in with email*/) {
@@ -34,21 +31,15 @@ export class LoginController extends ControllerHandler {
email = user.email; email = user.email;
} }
if (errors.count() > 0) { if (errors.count() > 0) return next(errors);
errors.endpoint();
next();
return;
}
const match = await User.Password.compare(password, user.password); const match = await User.Password.compare(password, user.password);
if (!match) { if (!match) {
errors.addError(401, 'Unauthorized', 'Incorrect password for user'); errors.addError(401, 'Unauthorized', 'Incorrect password for user');
errors.endpoint(); return next(errors);
next();
return;
} }
const response = new API.user(res, user.id, username, email, new Date(parseInt(user.lastupdated)).toLocaleString()); let response = new API.user(res, user.id, username, email, new Date(parseInt(user.lastupdated)).toLocaleString());
let token = await Database.Authorization.getTokenByID(user.id); let token = await Database.Authorization.getTokenByID(user.id);
if (token == -1) { if (token == -1) {
@@ -60,7 +51,7 @@ export class LoginController extends ControllerHandler {
} }
response.Token = token.token; response.Token = token.token;
response.endpoint(); await response.endpoint();
next(); next();
} }
} }

View File

@@ -8,15 +8,15 @@ export class AuthMiddleWare extends MiddleWare {
const errors = new API.errors(res); const errors = new API.errors(res);
if (!req.headers.authorization) { if (!req.headers.authorization) {
errors.addError(403, 'Forbidden', 'You cannot access this resource without authorization').endpoint(); errors.addError(403, 'Forbidden', 'You cannot access this resource without authorization');
return; return next(errors);
} }
const token = req.headers.authorization; const token = req.headers.authorization;
const user = await Auth.getUserFromToken(token); const user = await Auth.getUserFromToken(token);
if (user == -1) { if (user == -1 || !user.id) {
errors.addError(403, 'Forbidden', 'You cannot access this resource without authorization').endpoint(); errors.addError(403, 'Forbidden', 'You cannot access this resource without authorization');
return; return next(errors);
} }
req.user = user; req.user = user;

View File

@@ -1,6 +1,8 @@
import {Logger} from '../../../models/logger';
export class ErrorHandler { export class ErrorHandler {
static async newError(err, req, res, next) { static async newError(err, req, res, next) {
// Logger.error(JSON.stringify(err));
err.endpoint();
} }
} }

View File

@@ -3,10 +3,10 @@ import stringify from 'json-stringify-safe';
import {Logger} from '../../models/logger'; import {Logger} from '../../models/logger';
export class MiddleWare { export class MiddleWare {
static async end(req, res, next) { // static async end(req, res, next) {
await MiddleWare.RateLimits.request(req, res, next); // await MiddleWare.RateLimits.request(req, res, next);
await MiddleWare.analytics(req, res, next); // await MiddleWare.analytics(req, res, next);
} // }
static analytics(req, res, next) { static analytics(req, res, next) {
// TODO: Send data such as IP to an anyaltitics model // TODO: Send data such as IP to an anyaltitics model

View File

@@ -11,8 +11,9 @@ export class RateLimits extends MiddleWare {
MiddleWare.analytics(req, res, next); MiddleWare.analytics(req, res, next);
if (!buckets[ip]) { if (!buckets[ip]) {
Logger.debug(`New rate limiting bucket`); Logger.debug(`New rate limiting bucket for ${ip}`);
RateLimits.newBucket(ip); RateLimits.newBucket(ip);
next();
return; return;
} }
@@ -25,6 +26,7 @@ export class RateLimits extends MiddleWare {
} }
buckets[ip].tokens.pop(); buckets[ip].tokens.pop();
next();
} }
static newBucket(ip) { static newBucket(ip) {

View File

@@ -1,53 +1,40 @@
import {ControllerHandler} from './controllerHandler'; import {ControllerHandler} from './controllerHandler';
import {API} from './api/api'; import {API} from './api/api';
import {Notes} from '../models/notes/notes'; import {Notes} from '../models/notes/notes';
import {Logger} from '../models/logger'
export class NoteController extends ControllerHandler { export class NoteController extends ControllerHandler {
static async newNote(req, res, next) { static async newNote(req, res, next) {
const errors = new API.errors(res); const errors = new API.errors(res);
const content = req.body.text || null; const content = req.body.content || null;
const creatorid = req.user.id || undefined;
const group = req.body.parentgroup || undefined; const group = req.body.parentgroup || undefined;
let order = req.body.order || undefined; let order = req.body.order || undefined;
const user = req.user || undefined; const user = req.user || undefined;
if (!creatorid || !user) {
errors.addError(403, 'Forbidden');
errors.endpoint();
next();
return;
}
if (!order) { if (!order) {
errors.addError(422, 'Unprocessable entity'); errors.addError(422, 'Unprocessable entity', 'Unprocessable entity, no order provided');
errors.endpoint(); return next(errors);
next();
return;
} }
const id = await Notes.genID(); const id = await Notes.genID();
let success; let success;
if (!group) { if (!group) {
success = await Notes.newNote(id, content, creatorid, order); success = await Notes.newNote(id, content, req.user, order);
} else { } else {
const doesExist = await Notes.doesGroupExist(user.id, parentgroup); const doesExist = await Notes.doesGroupExist(user.id, parentgroup);
if (!doesExist) { if (!doesExist) {
errors.addError(422, 'Unprocessable entity', 'You are trying to create a note for a group that does not exist'); errors.addError(422, 'Unprocessable entity', 'You are trying to create a note for a group that does not exist');
errors.endpoint(); return next(errors);
next();
return;
} }
success = await Notes.newGroupedNote(id, content, creatorid, order, parentgroup); success = await Notes.newGroupedNote(id, content, req.user, order, parentgroup);
} }
if (success == -1) { if (success == -1) {
errors.addError(500, 'Internal server error'); errors.addError(500, 'Internal server error');
errors.endpoint(); return next(errors);
next();
return;
} }
new API.note(res, user, id, content, order, parentgroup).endpoint(); new API.note(res, user, id, content, order, parentgroup).endpoint();
@@ -58,10 +45,10 @@ export class NoteController extends ControllerHandler {
// id: id, // id: id,
// content: content, // content: content,
// parentgroup: parentgroup, // parentgroup: parentgroup,
// creatorid: creatorid, // req.user: req.user,
// order: order, // order: order,
// catergory: null, // catergory: null,
// endpoint: null, // endpoint: null,
// lastupdated: new Date().getTime() // lastupdated: new Date().getTime()
// static async newNote(id, content, creatorid, order, parentgroup) { // static async newNote(id, content, req.user, order, parentgroup) {

View File

@@ -11,8 +11,7 @@ export class PermaNoteController extends ControllerHandler {
const content = req.body.content || undefined; const content = req.body.content || undefined;
if (!content) { if (!content) {
errors.addError(422, 'Unprocessable entity', 'There is no content'); errors.addError(422, 'Unprocessable entity', 'There is no content');
errors.endpoint(); return next(errors);
return;
} }
const uid = await PermaLink.genUID() || new Date().getTime(); const uid = await PermaLink.genUID() || new Date().getTime();
@@ -21,8 +20,7 @@ export class PermaNoteController extends ControllerHandler {
const success = await Database.PermaNotes.newNote(uid, endpoint, content); const success = await Database.PermaNotes.newNote(uid, endpoint, content);
if (success == -1) { if (success == -1) {
errors.addError(500, 'Internal server error'); errors.addError(500, 'Internal server error');
errors.endpoint(); return next(errors);
return;
} }
new API.permalink(res, content, uid, endpoint).endpoint(); new API.permalink(res, content, uid, endpoint).endpoint();
@@ -32,9 +30,7 @@ export class PermaNoteController extends ControllerHandler {
static async getPermaNote(req, res, next) { static async getPermaNote(req, res, next) {
const endpoint = req.params.endpoint || undefined; const endpoint = req.params.endpoint || undefined;
if (!endpoint) { if (!endpoint) return;
return;
}
const data = await Database.PermaNotes.getNoteByEndpoint(endpoint); const data = await Database.PermaNotes.getNoteByEndpoint(endpoint);
if (data == -1) { if (data == -1) {

View File

@@ -17,7 +17,7 @@ export class Router {
app.post('/user', [MiddleWare.RateLimits.request, Controllers.UserController.newUser]); app.post('/user', [MiddleWare.RateLimits.request, Controllers.UserController.newUser]);
app.post('/login', [MiddleWare.RateLimits.request, Controllers.LoginController.authenticate]); app.post('/login', [MiddleWare.RateLimits.request, Controllers.LoginController.authenticate]);
app.get('/auth/user/:id', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]); app.get('/user/:id', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]);
app.delete('/auth/user/:id', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]); app.delete('/auth/user/:id', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]);
app.post('/unauth/permanote', [MiddleWare.RateLimits.request, Controllers.PermaNoteController.newPermaNote]); app.post('/unauth/permanote', [MiddleWare.RateLimits.request, Controllers.PermaNoteController.newPermaNote]);
@@ -35,13 +35,17 @@ export class Router {
app.delete('/auth/deletenote', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]); app.delete('/auth/deletenote', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]);
app.delete('/auth/deletegroup', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]); app.delete('/auth/deletegroup', [MiddleWare.RateLimits.request, MiddleWare.Auth.authUser]);
app.get('*', [MiddleWare.RateLimits.request, StatusCodes.pageNotFound]);
app.use(ErrorHandler.newError); app.use(ErrorHandler.newError);
app.get('*', [MiddleWare.RateLimits.request, StatusCodes.pageNotFound]);
app.post('*', [MiddleWare.RateLimits.request, StatusCodes.pageNotFound]);
app.delete('*', [MiddleWare.RateLimits.request, StatusCodes.pageNotFound]);
Logger.info('HTTP endpoints settup'); Logger.info('HTTP endpoints settup');
} }
static frontPage(req, res, next) { static async frontPage(req, res, next) {
// const err = "lol";
// next(err);
res.end('DEVELOPMENT SERVER'); res.end('DEVELOPMENT SERVER');
} }
} }

View File

@@ -1,5 +1,6 @@
export class StatusCodes { export class StatusCodes {
static pageNotFound(req, res) { static pageNotFound(req, res, next) {
res.status(404).end('404 Page not found'); res.status(404).send('404 Page not found');
next();
} }
} }

View File

@@ -27,11 +27,7 @@ export class UserController extends ControllerHandler {
if (await Database.Users.getUser('username', username) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that username allready exists'); if (await Database.Users.getUser('username', username) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that username allready exists');
if (await Database.Users.getUser('email', email) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that email allready exists'); if (await Database.Users.getUser('email', email) != -1) errors.addError(422, 'Unprocessable entity', 'A user with that email allready exists');
if (errors.count() > 0) { if (errors.count() > 0) return next(errors);
errors.endpoint();
next();
return;
}
const response = new API.user(res, id, username, email, new Date().toLocaleString()); const response = new API.user(res, id, username, email, new Date().toLocaleString());
@@ -49,9 +45,7 @@ export class UserController extends ControllerHandler {
const success = await user.insert(); const success = await user.insert();
if (success == -1) { if (success == -1) {
errors.addError(500, 'Internal server error'); errors.addError(500, 'Internal server error');
errors.endpoint(); return next(errors);
next();
return;
} }
response.endpoint(); response.endpoint();

View File

@@ -2,8 +2,8 @@ import {Database} from '../database/database';
export class Auth { export class Auth {
static async getUserFromToken(token) { static async getUserFromToken(token) {
const id = await Database.auth.getIDByToken(token); const id = await Database.Authorization.getIDByToken(token);
if (id == -1) return id; if (id == -1) return id;
return await Database.users.getUserByID(id.id); return await Database.Users.getUserByID(id);
} }
} }