API-10: Adding PartyPlaner register endpoint
- Also various other improvements
This commit is contained in:
parent
7df8b5ad8e
commit
8295deaf6a
9
app.ts
9
app.ts
|
@ -2,6 +2,10 @@ import express from 'express';
|
|||
import * as http from 'http';
|
||||
import * as bodyparser from 'body-parser';
|
||||
import * as dotenv from 'dotenv';
|
||||
// Router imports
|
||||
import {partyPlanerRouter} from "./src/models/partyplaner/PartyPlaner.router";
|
||||
import {highlightMarkerRouter} from "./src/models/twitch-highlight-marker/HighlightMarker.router";
|
||||
import {dhbwServiceRouter} from "./src/models/dhbw-service/DHBWService.router";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
|
@ -12,11 +16,6 @@ if (!process.env.PORT) {
|
|||
|
||||
const port: number = parseInt(process.env.PORT, 10);
|
||||
|
||||
// Router imports
|
||||
import {partyPlanerRouter} from "./src/models/partyplaner/PartyPlaner.router";
|
||||
import {highlightMarkerRouter} from "./src/models/twitch-highlight-marker/HighlightMarker.router";
|
||||
import { dhbwServiceRouter } from './src/models/dhbw-service/DHBWServiceRouter';
|
||||
|
||||
const app: express.Application = express();
|
||||
const server: http.Server = http.createServer(app);
|
||||
|
||||
|
|
1513
package-lock.json
generated
1513
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -13,12 +13,15 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.0.1",
|
||||
"debug": "^4.3.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"mariadb": "^2.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^3.0.1",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/express": "^4.17.11",
|
||||
"source-map-support": "^0.5.19",
|
||||
|
|
|
@ -12,11 +12,11 @@ export const dhbwServiceRouter = express.Router();
|
|||
// Sub-Endpoints
|
||||
dhbwServiceRouter.use('/generalInfo', generalInfoRouter);
|
||||
|
||||
generalInfoRouter.get('/', async (req: Request, res: Response) => {
|
||||
dhbwServiceRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`Pluto Development DHBW Service App API Endpoint`);
|
||||
res.status(200).send('Pluto Development DHBW Service App API Endpoint');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
|
@ -10,18 +10,18 @@ export const generalInfoRouter = express.Router();
|
|||
|
||||
generalInfoRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`GET generalInfo v2.1`);
|
||||
res.status(200).send('GET generalInfo v2.1');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
||||
generalInfoRouter.post('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`POST generalInfo v2.1`);
|
||||
res.status(200).send('POST generalInfo v2.1');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import express, {Request, Response} from 'express';
|
||||
import {dataRouter} from "./data/Data.router";
|
||||
import {registerRouter} from "./register/Register.router";
|
||||
|
||||
/**
|
||||
* Router Definition
|
||||
|
@ -11,12 +12,13 @@ export const partyPlanerRouter = express.Router();
|
|||
|
||||
// Sub-Endpoints
|
||||
partyPlanerRouter.use('/data', dataRouter);
|
||||
partyPlanerRouter.use('/register', registerRouter);
|
||||
|
||||
partyPlanerRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`Pluto Development PartyPlaner API Endpoint V2`);
|
||||
res.status(200).send('Pluto Development PartyPlaner API Endpoint V2');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -10,18 +10,18 @@ export const dataRouter = express.Router();
|
|||
|
||||
dataRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`GET data`);
|
||||
res.status(200).send('GET data');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
||||
dataRouter.post('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`POST data`);
|
||||
res.status(200).send('POST data');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
|
67
src/models/partyplaner/register/Register.router.ts
Normal file
67
src/models/partyplaner/register/Register.router.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Required External Modules and Interfaces
|
||||
*/
|
||||
import express, {Request, Response} from 'express';
|
||||
import * as UserService from '../userService/user.service';
|
||||
|
||||
/**
|
||||
* Router Definition
|
||||
*/
|
||||
export const registerRouter = express.Router();
|
||||
|
||||
registerRouter.post('/:isDevCall', async (req: Request, res: Response) => {
|
||||
try {
|
||||
let username: string = '';
|
||||
let email: string = '';
|
||||
let firstName: string = '';
|
||||
let lastName: string = '';
|
||||
let password: string = '';
|
||||
let useDev: boolean = (req.params.isDevCall ?? '') === 'dev'; // TBD
|
||||
|
||||
// API accepts both JSON in body and HTTP parameters
|
||||
if (req.headers['content-type'] === 'application/json') {
|
||||
username = req.body.username;
|
||||
email = req.body.email;
|
||||
firstName = req.body.firstName;
|
||||
lastName = req.body.lastName;
|
||||
password = req.body.password;
|
||||
} else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||
username = (req.query.username ?? '').toString();
|
||||
email = (req.query.email ?? '').toString();
|
||||
firstName = (req.query.firstName ?? '').toString();
|
||||
lastName = (req.query.lastName ?? '').toString();
|
||||
password = (req.query.password ?? '').toString();
|
||||
}
|
||||
let userIP = req.socket.remoteAddress ?? '';
|
||||
let deviceInfo = req.headers['user-agent'] ?? '';
|
||||
|
||||
if (username === '' || email === '' || firstName === '' || lastName === '' || password === '') {
|
||||
res.status(400).send({
|
||||
'status': 'WRONG_PARAMS',
|
||||
'message': 'Missing or wrong parameters'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for invalid username / email
|
||||
const status = await UserService.checkUsernameAndEmail(useDev, username, email);
|
||||
if (status.hasProblems) {
|
||||
// Username and/or email are duplicates, return error
|
||||
res.status(400).send({
|
||||
'message': status.messages[0],
|
||||
'status': status.status[0],
|
||||
'additionalMessages': status.messages.slice(1),
|
||||
'additionalStatus': status.status.slice(1)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create user
|
||||
let session = await UserService.registerUser(useDev, username, email, firstName, lastName, password, userIP, deviceInfo);
|
||||
|
||||
res.status(201).send(session);
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
200
src/models/partyplaner/userService/user.service.ts
Normal file
200
src/models/partyplaner/userService/user.service.ts
Normal file
|
@ -0,0 +1,200 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import {Guid} from 'guid-typescript';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const mariadb = require('mariadb');
|
||||
const prod_pool = mariadb.createPool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.PARTYPLANER_PROD_DATABASE,
|
||||
connectionLimit: 5
|
||||
});
|
||||
const dev_pool = mariadb.createPool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.PARTYPLANER_DEV_DATABASE,
|
||||
connectionLimit: 5
|
||||
});
|
||||
|
||||
export const getExistingUsernamesAndEmails = async (useDev: boolean): Promise<any> => {
|
||||
let conn;
|
||||
try {
|
||||
if (useDev) {
|
||||
conn = await dev_pool.getConnection();
|
||||
} else {
|
||||
conn = await prod_pool.getConnection();
|
||||
}
|
||||
|
||||
const rows = await conn.query('SELECT username, email FROM users');
|
||||
|
||||
let usernames: string[] = [];
|
||||
let emails: string[] = [];
|
||||
|
||||
for (let row in rows) {
|
||||
if (row !== 'meta') {
|
||||
usernames.push(rows[row].username);
|
||||
emails.push(rows[row].email);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'usernames': usernames,
|
||||
'emails': emails
|
||||
};
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
if (conn) {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in the checkUsernameAndEmail method as return value
|
||||
*/
|
||||
export interface Session {
|
||||
userId: string;
|
||||
sessionId: string;
|
||||
sessionKey: string;
|
||||
}
|
||||
|
||||
export const registerUser = async (useDev: boolean, username: string, email: string, firstName: string, lastName: string, password: string, ip: string, deviceInfo: string): Promise<Session> => {
|
||||
let conn;
|
||||
try {
|
||||
if (useDev) {
|
||||
conn = await dev_pool.getConnection();
|
||||
} else {
|
||||
conn = await prod_pool.getConnection();
|
||||
}
|
||||
|
||||
const pwHash = bcrypt.hashSync(password, 10);
|
||||
const sessionKey = Guid.create().toString();
|
||||
const sessionKeyHash = bcrypt.hashSync(sessionKey, 10);
|
||||
const verifyEmailCode = Guid.create().toString();
|
||||
|
||||
// Create user
|
||||
const userQuery = 'INSERT INTO users (username, email, first_name, last_name, password_hash, verify_email_code, registration_date, last_login) VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW()) RETURNING user_id';
|
||||
const userIdRes = await conn.query(userQuery, [username, email, firstName, lastName, pwHash, verifyEmailCode]);
|
||||
await conn.commit();
|
||||
|
||||
// Get user id of the created user
|
||||
let userId: number = -1;
|
||||
for (const row in userIdRes) {
|
||||
if (row !== 'meta' && userIdRes[row].user_id != null) {
|
||||
userId = userIdRes[row].user_id;
|
||||
}
|
||||
}
|
||||
|
||||
let deviceType = 'unknown';
|
||||
if (deviceInfo.includes('PartyPlaner') || deviceInfo.includes('Dart')) {
|
||||
deviceType = 'mobile';
|
||||
} else {
|
||||
deviceType = 'desktop';
|
||||
}
|
||||
|
||||
// Create session
|
||||
const sessionQuery = 'INSERT INTO sessions (user_id, session_key_hash, created_Date, last_login, valid_until, valid_days, last_ip, device_info, type) VALUES (?,?,NOW(),NOW(),DATE_ADD(NOW(), INTERVAL 365 DAY),365,?, ?, ?) RETURNING session_id';
|
||||
const sessionIdRes = await conn.query(sessionQuery, [userId, sessionKeyHash, ip, deviceInfo, deviceType]);
|
||||
await conn.commit();
|
||||
|
||||
// Get session id of the created session
|
||||
let sessionId: number = -1;
|
||||
for (const row in sessionIdRes) {
|
||||
if (row !== 'meta' && sessionIdRes[row].session_id != null) {
|
||||
sessionId = sessionIdRes[row].session_id;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'userId': userId.toString(),
|
||||
'sessionId': sessionId.toString(),
|
||||
'sessionKey': sessionKey
|
||||
};
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
if (conn) {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in the checkUsernameAndEmail method as return value
|
||||
*/
|
||||
export interface Status {
|
||||
hasProblems: boolean;
|
||||
messages: string[];
|
||||
status: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given username and email are valid and not yet used by another user
|
||||
* @param useDev If the dev database has to be used
|
||||
* @param username The username to check
|
||||
* @param email The email to check
|
||||
*/
|
||||
export const checkUsernameAndEmail = async (useDev: boolean, username: string, email: string): Promise<Status> => {
|
||||
let conn;
|
||||
try {
|
||||
if (useDev) {
|
||||
conn = await dev_pool.getConnection();
|
||||
} else {
|
||||
conn = await prod_pool.getConnection();
|
||||
}
|
||||
const usernameQuery = 'SELECT username FROM users WHERE username = ?';
|
||||
const emailQuery = 'SELECT email FROM users WHERE email = ?';
|
||||
const usernameRes = await conn.query(usernameQuery, username);
|
||||
const emailRes = await conn.query(emailQuery, email);
|
||||
|
||||
let res: Status = {
|
||||
hasProblems: false,
|
||||
messages: [],
|
||||
status: []
|
||||
};
|
||||
|
||||
const usernameRegex = RegExp('^[a-zA-Z0-9\\-\\_]{4,20}$'); // Can contain a-z, A-Z, 0-9, -, _ and has to be 4-20 chars long
|
||||
if (!usernameRegex.test(username)) {
|
||||
// Username doesn't match requirements
|
||||
res.hasProblems = true;
|
||||
res.messages.push('The username contains illegal characters or is not the allowed size');
|
||||
res.status.push('INVALID_USERNAME');
|
||||
}
|
||||
|
||||
const emailRegex = RegExp('^[a-zA-Z0-9\\-\\_.]{1,30}\\@[a-zA-Z0-9\\-.]{1,20}\\.[a-z]{1,20}$'); // Normal email regex, user@plutodev.de
|
||||
if (!emailRegex.test(email)) {
|
||||
// Username doesn't match requirements
|
||||
res.hasProblems = true;
|
||||
res.messages.push('The email contains illegal characters or is not the allowed size');
|
||||
res.status.push('INVALID_EMAIL');
|
||||
}
|
||||
|
||||
if (usernameRes.length > 0) {
|
||||
// Username is a duplicate
|
||||
res.hasProblems = true;
|
||||
res.messages.push('This username is already in use');
|
||||
res.status.push('DUPLICATE_USERNAME');
|
||||
}
|
||||
|
||||
if (emailRes.length > 0) {
|
||||
// Email is a duplicate
|
||||
res.hasProblems = true;
|
||||
res.messages.push('This email is already in use');
|
||||
res.status.push('DUPLICATE_EMAIL');
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
if (conn) {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -13,9 +13,9 @@ highlightMarkerRouter.use('/addHighlight', addHighlightRouter);
|
|||
|
||||
highlightMarkerRouter.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`Pluto Development Twitch Highlight Marker API Endpoint`);
|
||||
res.status(200).send('Pluto Development Twitch Highlight Marker API Endpoint');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,10 +11,10 @@ export const addHighlightRouter = express.Router();
|
|||
|
||||
addHighlightRouter.get('/', (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).send(`GET endpoint not defined.`);
|
||||
res.status(200).send('GET endpoint not defined.');
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -23,23 +23,23 @@ addHighlightRouter.post('/', (req: Request, res: Response) => {
|
|||
// Check input params
|
||||
const body = req.body;
|
||||
|
||||
if(body.access_key !== process.env.TWITCH_HIGHLIGHTS_ACCESS_KEY){
|
||||
if (body.access_key !== process.env.TWITCH_HIGHLIGHTS_ACCESS_KEY) {
|
||||
// Unauthorized, return error
|
||||
res.type('application/json');
|
||||
res.status(403).send('{"status": "error", "description": "Unauthorized."}');
|
||||
} else if(!body.streamer || !body.stream_id || !body.stream_game || !body.timestamp || !body.description || !body.username){
|
||||
res.status(403).send({'status': 'error', 'description': 'Unauthorized.'});
|
||||
} else if (!body.streamer || !body.stream_id || !body.stream_game || !body.timestamp || !body.description || !body.username) {
|
||||
// Missing params, return error
|
||||
res.type('application/json');
|
||||
res.status(400).send('{"status": "error", "description": "Missing parameters."}');
|
||||
res.status(400).send({'status': 'error', 'description': 'Missing parameters.'});
|
||||
} else {
|
||||
// Everything fine, return success
|
||||
AddHighlightService.createHighlightEntry(body);
|
||||
|
||||
res.type('application/json');
|
||||
res.status(200).send('{"status": "success", "description": ""}');
|
||||
res.status(200).send({'status': 'success', 'description': ''});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error handling a request: ' + e.message);
|
||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
res.status(500).send({'message': 'Internal Server Error. Try again later.'});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -13,7 +13,6 @@ const pool = mariadb.createPool({
|
|||
|
||||
export const createHighlightEntry = async (req_body: any) => {
|
||||
let conn;
|
||||
let price: any;
|
||||
try {
|
||||
conn = await pool.getConnection();
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user