Commit 569a1e4f authored by Цой Данил's avatar Цой Данил 💬

Started working with backend/specially user methods: login, create and etc

parent 671f3ad8
APP_PORT=8000
PG_HOST=localhost
MONGO_CLIENT_URL=mongodb://localhost/TsoyDanilMusicDB
MONGO_CLIENT_URL=mongodb://localhost/TsoyDanilChatDB
ENV_SALT=10
SECRET_KEY=key
\ No newline at end of file
......@@ -25,8 +25,7 @@
"sequelize": "^6.29.3",
"sequelize-typescript": "^2.1.5",
"shortid": "^2.2.16",
"uuid": "^9.0.0",
"ws": "^8.13.0"
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
......@@ -2660,26 +2659,6 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
......@@ -4707,12 +4686,6 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"requires": {}
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
......
......@@ -27,8 +27,7 @@
"sequelize": "^6.29.3",
"sequelize-typescript": "^2.1.5",
"shortid": "^2.2.16",
"uuid": "^9.0.0",
"ws": "^8.13.0"
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
......
import express, { Router, Request, Response } from 'express'
export class HealthCheckController {
private router: Router
constructor() {
this.router = express.Router()
this.router.get('/', this.checkHealth)
}
public getRouter = (): Router => {
return this.router
}
private checkHealth = (req: Request, res: Response): void => {
res.send('Server is OK')
}
}
export const healthCheckController = new HealthCheckController()
\ No newline at end of file
export const enum EStatuses {
SUCCESS = 'Success',
FAILURE = 'Failure'
}
\ No newline at end of file
import jwt from 'jsonwebtoken'
import { Types } from 'mongoose'
export const generateJWT = (payload: {[key: string]: number | string | boolean | Types.ObjectId}) => {
return jwt.sign(payload, process.env.SECRET_KEY || '', {expiresIn: '24h'})
}
import { Types } from "mongoose";
import IUser from "./IUser";
export default interface IMessage{
_id: Types.ObjectId
author: IUser['username']
message: string
}
\ No newline at end of file
import IMessage from "./IMessage";
export default interface IMessageDto{
author: IMessage['author']
message: IMessage['message']
}
\ No newline at end of file
import { Request } from "express";
import { JwtPayload } from "jsonwebtoken";
export default interface IModifiedRequest extends Request {
verifiedData: string | JwtPayload
}
\ No newline at end of file
import { EStatuses } from "../enum/EStatuses";
export default interface IResponse<T = null> {
status: EStatuses
result: T
message: string
}
\ No newline at end of file
import {Document, Types} from "mongoose"
export default interface IUser extends Document {
_id: Types.ObjectId
username: string
password: string
checkPassword: (pass: string) => boolean
}
\ No newline at end of file
import IUser from "./IUser";
export default interface IUserCreateDto {
username: IUser['username']
password: IUser['password']
}
import { Types } from "mongoose";
import IUSer from "./IUser";
export default interface IUserGetDto {
_id: Types.ObjectId
username: IUSer['username']
token: string
}
\ No newline at end of file
import { NextFunction, Request, Response } from "express";
import { EStatuses } from "../enum/EStatuses";
import IResponse from "../interfaces/IResponse";
import jwt from 'jsonwebtoken'
import IModifiedRequest from "../interfaces/IModifiedRequest";
export const auth = (expressReq: Request, res: Response, next: NextFunction) => {
const req = expressReq as IModifiedRequest
if (req.method === 'OPTIONS') {
next()
}
try{
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token){
throw new Error('Token not provided')
}
const verifiedData = jwt.verify(token, process.env.SECRET_KEY as string);
req.verifiedData = verifiedData
next()
} catch(err: unknown){
const error = err as Error
const response: IResponse<undefined> = {
status: EStatuses.FAILURE,
result: undefined,
message: error.message
}
res.status(418).send(response)
}
}
\ No newline at end of file
import mongoose, { Schema } from "mongoose";
import bcrypt from "bcrypt";
import IUser from "../interfaces/IUser";
const UserSchema: Schema = new Schema<IUser>({
username: {
type: String,
unique: true,
trim: true,
minlength: 1,
required: [true, 'Username should exist']
},
password: {
type: String,
trim: true,
minlength: 1,
required: [true, 'Password should exist']
}
},{versionKey: false})
UserSchema.pre('save', async function(next){
if (!this.isModified('password')) return next()
const salt = await bcrypt.genSalt()
const hash = await bcrypt.hash(this.password, salt)
this.password = hash
next()
})
UserSchema.set('toJSON', {transform(doc, ret, options) {
delete ret.password
return ret
}})
UserSchema.methods.checkPassword = async function(password: string) {
return await bcrypt.compare(password, this.password);
}
export const User = mongoose.model<IUser>('User', UserSchema)
\ No newline at end of file
import dotenv from 'dotenv'
import mongoose, { Mongoose } from 'mongoose'
import IResponse from '../interfaces/IResponse'
import { EStatuses } from '../enum/EStatuses'
import IUserGetDto from '../interfaces/IUserGetDto'
import { generateJWT } from '../helpers/generateJWT'
import IUserCreateDto from '../interfaces/IUserCreateDto'
import { User } from '../models/User'
dotenv.config()
export class MongooseDB {
private client: Mongoose | null = null
public close = async() => {
if (!this.client) return
await this.client.disconnect();
}
public init = async (): Promise<void> => {
try {
this.client = await mongoose.connect(process.env.MONGO_CLIENT_URL || '')
console.log('Server connected to MongoDB');
} catch (err) {
const error = err as Error;
console.error('Connected error MongooseDB:', error);
}
}
public createUser = async (userDto: IUserCreateDto): Promise<IResponse<IUserGetDto | null>> => {
try {
const user = new User(userDto)
await user.save()
const data: IUserGetDto = {
_id: user._id,
username: user.username,
token: generateJWT({_id: user._id, username: user.username})
}
const response: IResponse<IUserGetDto> = {
status: EStatuses.SUCCESS,
result: data,
message: 'User added'
}
return response
} catch(err: unknown){
const error = err as Error
const response: IResponse<null> = {
status: EStatuses.FAILURE,
result: null,
message: error.message
}
return response
}
}
public loginUser = async(userDto: IUserCreateDto): Promise<IResponse<IUserGetDto | null>> => {
try{
const user = await User.findOne({username: userDto.username})
if (!user) throw new Error('User not found')
const isMatch: boolean = await user.checkPassword(userDto.password)
if (!isMatch) throw new Error('Wrong password')
const data = {
_id: user._id,
username: user.username,
token: generateJWT({_id: user._id, username: user.username})
}
await user.save()
const response: IResponse<IUserGetDto> = {
status: EStatuses.SUCCESS,
result: data,
message: 'Access granted'
}
return response
} catch(err: unknown){
const error = err as Error
const response: IResponse<null> = {
status: EStatuses.FAILURE,
result: null,
message: error.message
}
return response
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment