User endpoints created
Invoices can now be fetched
This commit is contained in:
@@ -11,6 +11,7 @@ import { hashPassword, randomPepper, randomString } from './helper/crypto';
|
||||
import { InvoiceScheduler } from './helper/invoiceScheduler';
|
||||
import { User } from './models/user/user.model';
|
||||
import { invoiceRouter } from './routes/invoice';
|
||||
import { userRouter } from './routes/user';
|
||||
|
||||
// Load .env
|
||||
dconfig({ debug: true, encoding: 'UTF-8' });
|
||||
@@ -110,6 +111,7 @@ async function run() {
|
||||
|
||||
app.get('/', (req, res) => res.status(200).send('OK'));
|
||||
app.use('/invoice', invoiceRouter);
|
||||
app.use('/user', userRouter);
|
||||
|
||||
app.listen(config.http.port, config.http.host, () => {
|
||||
logger.info(`HTTP server started on port ${config.http.host}:${config.http.port}`);
|
||||
|
||||
@@ -76,4 +76,45 @@ export async function createInvoice(req: Request, res: Response) {
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// GET /invoice/
|
||||
// GET /invoice/:id
|
||||
export async function getInvoice(req: Request, res: Response) {
|
||||
const invoiceId = req.params.id;
|
||||
|
||||
// If an id is provided
|
||||
if (invoiceId !== undefined) {
|
||||
const invoice: any = await Invoice.findById(invoiceId);
|
||||
if (invoice === null) {
|
||||
res.status(404).send();
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).send(invoice);
|
||||
return;
|
||||
}
|
||||
|
||||
let skip = req.query.skip;
|
||||
let limit = req.query.limit;
|
||||
let sortQuery = req.query.sort; // Either 'newest' (DESC) or 'oldest' (ASC)
|
||||
let sort = 1;
|
||||
|
||||
if (skip === undefined) skip = '0';
|
||||
if (limit === undefined || Number(limit) > 100) limit = '100';
|
||||
if (sortQuery !== undefined) {
|
||||
if (sortQuery === 'newest') sort = -1;
|
||||
else if (sortQuery === 'newest') sort = 1;
|
||||
else {
|
||||
res.status(400).send({ message: 'Unkown sort parameter. "sort" can only be "newest" or "oldest"' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const invoices = await Invoice.find({})
|
||||
.limit(Number(limit))
|
||||
.skip(Number(skip))
|
||||
.sort({ createdAt: sort });
|
||||
|
||||
res.status(200).send(invoices);
|
||||
}
|
||||
142
src/controllers/user.ts
Normal file
142
src/controllers/user.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { decode, sign, verify } from 'jsonwebtoken';
|
||||
|
||||
import { JWT_SECRET, logger } from '../app';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import { config } from '../../config';
|
||||
import { hashPassword, randomPepper, randomString, verifyPassword } from '../helper/crypto';
|
||||
import { User } from '../models/user/user.model';
|
||||
import { LibrePayRequest } from '../helper/request';
|
||||
|
||||
export async function getUser(req: LibrePayRequest, res: Response) {
|
||||
let user: any = req.params.id === undefined ? req.user : await User.findById(req.params.id);
|
||||
|
||||
if (user === null) {
|
||||
res.status(404).send();
|
||||
return;
|
||||
}
|
||||
|
||||
user.password = undefined;
|
||||
user.salt = undefined;
|
||||
user.__v = undefined;
|
||||
|
||||
res.status(200).send(user);
|
||||
}
|
||||
|
||||
export async function createUser(req: LibrePayRequest, res: Response) {
|
||||
const name = req.body.name;
|
||||
const password = req.body.password;
|
||||
const type = req.body.type;
|
||||
|
||||
if (name === undefined || password === undefined || type === undefined) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
|
||||
if (await User.countDocuments({ name }) === 1) {
|
||||
res.status(409).send();
|
||||
return;
|
||||
}
|
||||
|
||||
const salt = randomString(config.authentification.salt_length);
|
||||
const hashedPassword = await hashPassword(password + salt + randomPepper()).catch(error => {
|
||||
res.status(400).send({ message: 'Provided password is too weak and cannot be used.' });
|
||||
return;
|
||||
}) as string;
|
||||
|
||||
const newUser = await User.create({
|
||||
name,
|
||||
password: hashedPassword,
|
||||
salt,
|
||||
lastLogin: new Date(0)
|
||||
});
|
||||
|
||||
// Create setup token that the new user can use to change his password.
|
||||
const setupToken = jwt.sign({ setupForUser: newUser._id }, JWT_SECRET, { expiresIn: '1d' });
|
||||
|
||||
res.status(200).send({ setupToken });
|
||||
}
|
||||
|
||||
export async function DeleteUser(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
|
||||
export async function PatchUser(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
|
||||
export async function loginUser(req: Request, res: Response) {
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
const twoFA = req.body.twoFA;
|
||||
|
||||
const user = await User.findOne({ name: username });
|
||||
|
||||
// Check if user exists
|
||||
if (user == undefined) {
|
||||
setTimeout(() => {
|
||||
res.status(404).send({ message: "Either the username or password is wrong." });
|
||||
}, Math.random() * 1500 + 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if 2FA is turned on (the attack doesn't know yet if the password is wrong)
|
||||
if (user.twoFASecret != undefined) {
|
||||
if (twoFA == undefined) {
|
||||
res.status(401).send({ message: "2FA code is required." });
|
||||
return;
|
||||
}
|
||||
// TODO: Implement 2FA logic here
|
||||
}
|
||||
|
||||
// Check if password is wrong
|
||||
if (!await verifyPassword(password + user.salt, user.password)) {
|
||||
res.status(404).send({ message: 'Either the username or password is wrong.' });
|
||||
return;
|
||||
}
|
||||
|
||||
// We're good. Create JWT token.
|
||||
const token = sign({ user: user._id }, JWT_SECRET, { expiresIn: '30d' });
|
||||
|
||||
user.lastLogin = new Date(Date.now());
|
||||
await user.save();
|
||||
|
||||
logger.info(`User ${user.name} logged in.`)
|
||||
res.status(200).send({ token });
|
||||
}
|
||||
|
||||
/**
|
||||
* This middleware validates any tokens that are required to access most of the endpoints.
|
||||
* Note: This validation doesn't contain any permission checking.
|
||||
*/
|
||||
export async function MW_User(req: LibrePayRequest, res: Response, next: () => void) {
|
||||
if (req.headers.token === undefined) {
|
||||
res.status(401).send({ message: "Token not specified" });
|
||||
return;
|
||||
}
|
||||
const token = req.headers.token.toString();
|
||||
|
||||
try {
|
||||
// Verify token
|
||||
if(await verify(token, JWT_SECRET, { algorithms: ['HS256'] })) {
|
||||
// Token is valid, now look if user is in db (in case he got deleted)
|
||||
const id = decode(token, { json: true })!.user;
|
||||
const db = await User.findById(id);
|
||||
|
||||
if (db !== undefined && db !== null) {
|
||||
req.user = db
|
||||
next();
|
||||
return;
|
||||
} else {
|
||||
res.status(401).send({ message: "Token is not valid" });
|
||||
}
|
||||
} else {
|
||||
res.status(401).send({ message: "Token is not valid" });
|
||||
}
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
res.status(500).send({ message: "We failed validating your token for some reason." });
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,7 @@ export class InvoiceScheduler {
|
||||
return;
|
||||
}
|
||||
|
||||
decoded.result.vout.forEach(output => {
|
||||
//console.log('Output:', output.scriptPubKey);
|
||||
|
||||
decoded.result.vout.forEach(output => {
|
||||
// Loop over each output and check if the address of one matches the one of an invoice.
|
||||
this.pendingInvoices.forEach(invoice => {
|
||||
// We found our transaction
|
||||
|
||||
6
src/helper/request.ts
Normal file
6
src/helper/request.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Request } from "express";
|
||||
import { IUser } from "../models/user/user.interface";
|
||||
|
||||
export interface LibrePayRequest extends Request {
|
||||
user?: IUser
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Router } from "express";
|
||||
import { createInvoice } from "../controllers/invoice";
|
||||
import { createInvoice, getInvoice } from "../controllers/invoice";
|
||||
import { MW_User } from "../controllers/user";
|
||||
|
||||
const invoiceRouter = Router()
|
||||
|
||||
invoiceRouter.get('/:id');
|
||||
invoiceRouter.post('/', createInvoice);
|
||||
invoiceRouter.get('/:id', getInvoice);
|
||||
invoiceRouter.get('/', MW_User, getInvoice);
|
||||
invoiceRouter.post('/', MW_User, createInvoice);
|
||||
|
||||
export { invoiceRouter };
|
||||
10
src/routes/user.ts
Normal file
10
src/routes/user.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express";
|
||||
import { MW_User, loginUser, getUser } from "../controllers/user";
|
||||
|
||||
const userRouter = Router()
|
||||
|
||||
userRouter.get('/login', loginUser);
|
||||
userRouter.get('/', MW_User, getUser);
|
||||
userRouter.get('/:id', MW_User, getUser);
|
||||
|
||||
export { userRouter };
|
||||
Reference in New Issue
Block a user