Added: tests, signin api, not working docker
This commit is contained in:
@ -1,5 +1,81 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Request, Response } from 'express';
|
||||
import bcrypt from 'bcrypt';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import env from '../config/environment';
|
||||
import Docs from '../services/docsService';
|
||||
import User from '../models/User';
|
||||
import {Log} from 'nork'
|
||||
import { IUser, signupExam, ISignin, signinExam } from '../validators/authValidator';
|
||||
|
||||
export function signup_post(req: Request, res: Response) {
|
||||
res.send("logged in");
|
||||
new Docs('user', 'signup', '/api/v1/auth/signup', 'POST', 'user signup api', undefined, signupExam, 'status object');
|
||||
export async function signup_post(req: Request, res: Response) {
|
||||
try {
|
||||
const payload: IUser = req.body;
|
||||
|
||||
payload.password = await bcrypt.hash(payload.password, 12);
|
||||
const user = new User(payload);
|
||||
await user.save();
|
||||
|
||||
res.status(201).json(Log.info(201, 'user was successfully signed up'));
|
||||
} catch (err: any) {
|
||||
if (err.code == 11000) {
|
||||
res.status(400).json(Log.error(400, 'this user already exists'));
|
||||
return;
|
||||
}
|
||||
Log.error(500, err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
new Docs('user', 'signin', '/api/v1/auth/signin', 'POST', 'user signin api', undefined, signinExam, 'status object');
|
||||
export async function signin_post(req: Request, res: Response) {
|
||||
try {
|
||||
const payload: ISignin = req.body;
|
||||
|
||||
const user = await User.findOne({ email: payload.email });
|
||||
if (!user) {
|
||||
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
|
||||
res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
|
||||
res.status(401).json(Log.error(401, 'email or password is wrong'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (await bcrypt.compare(payload.password, user.password)) {
|
||||
const maxAge = 3 * 24 * 60 * 60; // 3 days in seconds
|
||||
const createToken = (id: any) => {
|
||||
return jwt.sign({ id }, env.JWT_SECRET, {
|
||||
expiresIn: maxAge
|
||||
});
|
||||
};
|
||||
|
||||
const token = createToken(user._id);
|
||||
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
|
||||
res.cookie('auth', true, { httpOnly: false, maxAge: maxAge * 1000 });
|
||||
|
||||
res.json(Log.info(200, 'user is logged in'));
|
||||
return;
|
||||
}
|
||||
|
||||
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
|
||||
res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
|
||||
res.status(401).json(Log.error(401, 'email or password is wrong'));
|
||||
} catch (err: any) {
|
||||
Log.error(500, err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
new Docs('user', 'logout', '/api/v1/auth/logout', 'POST', 'user logout api', undefined, {}, 'status object');
|
||||
export function logout_post(req: Request, res: Response) {
|
||||
res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
|
||||
res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
|
||||
res.json(Log.info(200, 'user was logged out'));
|
||||
}
|
||||
|
||||
new Docs('user', 'status', '/api/v1/auth/status', 'GET', 'user login status api', undefined, undefined, 'status code | user object');
|
||||
export function status_get(req: Request, res: Response) {
|
||||
let userObject = res.locals.user;
|
||||
userObject.password = undefined;
|
||||
userObject.__v = undefined;
|
||||
res.status(200).json(Log.info(200, 'user is logged in', userObject));
|
||||
}
|
15
api/src/middlewares/validateRequest.ts
Normal file
15
api/src/middlewares/validateRequest.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { object, AnySchema } from 'yup';
|
||||
import { Log } from 'nork';
|
||||
|
||||
const validate = (schema: AnySchema) => async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
await schema.validate(req.body);
|
||||
|
||||
next();
|
||||
} catch (err: any) {
|
||||
return res.status(400).json(Log.error(400, 'validation error', err));
|
||||
}
|
||||
};
|
||||
|
||||
export default validate;
|
26
api/src/models/User.ts
Normal file
26
api/src/models/User.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import path from 'path';
|
||||
import { Schema, model } from 'mongoose';
|
||||
import { IUser } from '../validators/authValidator';
|
||||
|
||||
const schema = new Schema<IUser>(
|
||||
{
|
||||
username: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export default model(path.basename(__filename).split('.')[0], schema);
|
@ -1,6 +1,7 @@
|
||||
import { Router } from "express";
|
||||
import * as authController from "../controllers/authController";
|
||||
//import authValidator from "../validators/authValidator";
|
||||
import validate from '../middlewares/validateRequest'
|
||||
import * as AuthVal from '../validators/authValidator'
|
||||
//import handleValidation from "../middlewares/handleValidation";
|
||||
//import { requireAuth } from "../middlewares/authMiddleware";
|
||||
|
||||
@ -8,7 +9,7 @@ const router = Router();
|
||||
|
||||
//const mws = [requireAuth, handleValidation.handleValidationError];
|
||||
|
||||
router.post("/signup", authController.signup_post);
|
||||
router.post("/auth/signup",validate(AuthVal.signup) , authController.signup_post);
|
||||
|
||||
//router.post(
|
||||
// "/login",
|
@ -1,9 +1,9 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import path from "path";
|
||||
import authRoutes from "./authRoutes";
|
||||
import api_v1 from "./api_v1";
|
||||
export const router = Router();
|
||||
|
||||
router.use("/api/auth", authRoutes);
|
||||
router.use("/api/v1", api_v1);
|
||||
|
||||
//router.get("*", (req: Request, res: Response) => {
|
||||
// res.sendFile(path.join(__dirname, "../views/index.html"));
|
||||
|
@ -1,26 +1,40 @@
|
||||
import http from "http";
|
||||
import { app } from "./app";
|
||||
import env from "./config/environment";
|
||||
//const env = {
|
||||
// APP_PORT: 8080,
|
||||
// APP_HOSTNAME: "127.0.0.1",
|
||||
//};
|
||||
import mongoose from 'mongoose' // TODO: dopsat nork module pro db
|
||||
import { Log } from "nork";
|
||||
//import database from './config/database'
|
||||
|
||||
const port: number = env.APP_PORT || 8080;
|
||||
const hostname: string = env.APP_HOSTNAME || "localhost";
|
||||
|
||||
export const server = http.createServer(app);
|
||||
|
||||
// Server
|
||||
export function runServer(): void {
|
||||
server.listen(port, hostname, () => {
|
||||
Log.info(200, `Server is listening on http://${hostname}:${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
//if (!env.NORK.database) {
|
||||
runServer();
|
||||
//export function runServer(): void {
|
||||
// server.listen(port, hostname, () => {
|
||||
// Log.info(200, `Server is listening on http://${hostname}:${port}`);
|
||||
// });
|
||||
//}
|
||||
//
|
||||
////if (!env.NORK.database) {
|
||||
//runServer();
|
||||
//} else {
|
||||
// const db_connection = database()
|
||||
// runServer()
|
||||
//}
|
||||
|
||||
|
||||
|
||||
(async () => {
|
||||
if (!process.env.DOCS_GEN) {
|
||||
try {
|
||||
await mongoose.connect(env.DB_URI);
|
||||
Log.info(200, 'connected to db');
|
||||
server.listen(port, () => {
|
||||
Log.info(200, `Server is listening on http://localhost:${port}`);
|
||||
});
|
||||
} catch (err: any) {
|
||||
Log.error(500, err);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
62
api/src/services/docsService.ts
Normal file
62
api/src/services/docsService.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
|
||||
export type ApiResponse = 'status object' | Object | string;
|
||||
|
||||
export class Docs {
|
||||
private name: string;
|
||||
private operation: string;
|
||||
private route: string;
|
||||
private method: HttpMethods;
|
||||
private parameters?: Object[];
|
||||
private description?: string;
|
||||
private body?: Object;
|
||||
private response?: ApiResponse;
|
||||
|
||||
public constructor(name: string, operation: string, route: string, method: HttpMethods, description?: string, parameters?: Object[], body?: Object, response?: ApiResponse) {
|
||||
this.name = name;
|
||||
this.operation = operation;
|
||||
this.route = route;
|
||||
this.method = method;
|
||||
description ? (this.description = description) : null;
|
||||
parameters ? (this.parameters = parameters) : null;
|
||||
body ? (this.body = body) : null;
|
||||
response ? (this.response = response) : null;
|
||||
|
||||
this.generate();
|
||||
}
|
||||
|
||||
private generate(): boolean {
|
||||
if (!process.env.DOCS_GEN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const jsonPath = path.join(__dirname, '../public/api.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json')).toString());
|
||||
|
||||
const jsonAPI = () => JSON.parse(fs.readFileSync(jsonPath).toString());
|
||||
const genJsonAPI = () => fs.writeFileSync(jsonPath, JSON.stringify({ version: pkg.version, endpoints: {} }));
|
||||
|
||||
if (!fs.existsSync(path.join(__dirname, '../public'))) {
|
||||
fs.mkdirSync(path.join(__dirname, '../public'));
|
||||
}
|
||||
|
||||
if (!fs.existsSync(jsonPath)) {
|
||||
genJsonAPI();
|
||||
}
|
||||
|
||||
if (jsonAPI().version != pkg.version) {
|
||||
genJsonAPI();
|
||||
}
|
||||
|
||||
const data = jsonAPI();
|
||||
data.endpoints[this.name] ? undefined : (data.endpoints[this.name] = {});
|
||||
data.endpoints[this.name][this.operation] = this;
|
||||
fs.writeFileSync(jsonPath, JSON.stringify(data));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default Docs;
|
@ -1,48 +0,0 @@
|
||||
import { Sequelize } from 'sequelize';
|
||||
|
||||
let sequelize: Sequelize | null = null;
|
||||
|
||||
const connectDB = async () => {
|
||||
sequelize = new Sequelize({
|
||||
dialect: 'mariadb',
|
||||
host: 'localhost',
|
||||
username: 'your_username',
|
||||
password: 'your_password',
|
||||
database: 'your_database',
|
||||
});
|
||||
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('Connection to the database has been established successfully.');
|
||||
} catch (error) {
|
||||
console.error('Unable to connect to the database:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const dropDB = async () => {
|
||||
if (sequelize) {
|
||||
try {
|
||||
await sequelize.drop();
|
||||
console.log('Database dropped successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error dropping database:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dropTables = async () => {
|
||||
if (sequelize) {
|
||||
try {
|
||||
await sequelize.sync({ force: true });
|
||||
console.log('All tables dropped successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error dropping tables:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { connectDB, dropDB, dropTables };
|
30
api/src/utils/test_mongodb.ts
Normal file
30
api/src/utils/test_mongodb.ts
Normal file
@ -0,0 +1,30 @@
|
||||
const mongoose = require('mongoose');
|
||||
const { MongoMemoryServer } = require('mongodb-memory-server');
|
||||
|
||||
let mongo: any = null;
|
||||
|
||||
const connectDB = async () => {
|
||||
mongo = await MongoMemoryServer.create({ binary: { os: { os: 'linux', dist: 'ubuntu', release: '18.04' } } }); // TODO: check that host OS is Void Linux, else remove the argument
|
||||
const uri = mongo.getUri();
|
||||
|
||||
await mongoose.connect(uri);
|
||||
};
|
||||
|
||||
const dropDB = async () => {
|
||||
if (mongo) {
|
||||
await mongoose.connection.dropDatabase();
|
||||
await mongoose.connection.close();
|
||||
await mongo.stop();
|
||||
}
|
||||
};
|
||||
|
||||
const dropCollections = async () => {
|
||||
if (mongo) {
|
||||
const collections = await mongoose.connection.db.collections();
|
||||
for (let collection of collections) {
|
||||
await collection.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { connectDB, dropDB, dropCollections };
|
34
api/src/validators/authValidator.ts
Normal file
34
api/src/validators/authValidator.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import * as yup from 'yup';
|
||||
import YupPassword from 'yup-password';
|
||||
YupPassword(yup);
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
interface mongooseAddition {
|
||||
_id?: Schema.Types.ObjectId;
|
||||
createdAt?: Schema.Types.Date;
|
||||
updatedAt?: Schema.Types.Date;
|
||||
}
|
||||
|
||||
// SignUp
|
||||
export const signup = yup.object({
|
||||
username: yup.string().required(),
|
||||
email: yup.string().email().required(),
|
||||
password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required()
|
||||
});
|
||||
export interface IUser extends yup.InferType<typeof signup>, mongooseAddition {}
|
||||
export const signupExam: IUser = {
|
||||
username: 'testuser',
|
||||
email: 'text@example.com',
|
||||
password: 'Test1234'
|
||||
};
|
||||
|
||||
// SignIn
|
||||
export const signin = yup.object({
|
||||
email: yup.string().email().required(),
|
||||
password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required()
|
||||
});
|
||||
export interface ISignin extends yup.InferType<typeof signin>, mongooseAddition {}
|
||||
export const signinExam: ISignin = {
|
||||
email: 'text@example.com',
|
||||
password: 'Test1234'
|
||||
};
|
Reference in New Issue
Block a user