diff --git a/src/Api/package.json b/src/Api/package.json index f2cf2e8..2769247 100644 --- a/src/Api/package.json +++ b/src/Api/package.json @@ -11,9 +11,11 @@ "author": "", "license": "ISC", "devDependencies": { + "@types/bcrypt": "^5.0.0", "@types/cors": "^2.8.13", "@types/debug": "^4.1.7", "@types/express": "^4.17.16", + "@types/jsonwebtoken": "^9.0.1", "@types/morgan": "^1.9.4", "nodemon": "^2.0.20", "ts-node": "^10.9.1", @@ -24,8 +26,11 @@ "@types/mongoose": "^5.11.97", "@types/request": "^2.48.8", "axios": "^1.2.6", + "bcrypt": "^5.1.0", "cors": "^2.8.5", "express-winston": "^4.2.0", + "joi": "^17.8.1", + "jsonwebtoken": "^9.0.0", "mongodb": "^5.0.0", "mongoose": "^6.9.0", "morgan": "^1.10.0", diff --git a/src/Api/src/app.ts b/src/Api/src/app.ts index dd14310..d3f8d36 100644 --- a/src/Api/src/app.ts +++ b/src/Api/src/app.ts @@ -6,6 +6,7 @@ import cors from 'cors'; import Controller from './controller/Icontroller'; // import ErrorMiddleware from './middleware/error.middleware'; import bodyParser from 'body-parser'; +import mongoose from 'mongoose'; // to secure // import helmet from 'helmet'; @@ -24,7 +25,7 @@ class App { this.port = port; this.dataBase = null; - // this.initialiseDatabase(); + this.initialiseDatabase(); this.initialiseMiddleware(); this.initialiseControllers(controllers); @@ -61,7 +62,14 @@ class App { console.log(`⚡️[server] : App listening on the port ${this.port}`); }); } - + + private initialiseDatabase(): void { + const { MONGO_USER, MONGO_PASSWORD, MONGO_PATH } = process.env; + const uri = "mongodb+srv://fladDevDb:ZslYlNRWIOUU7i6o@fladcluster.b29tytu.mongodb.net/?retryWrites=true&w=majority" + mongoose.connect(uri) + .then(() => console.log("Connect to MongoDB database successfully")) + .catch(err => console.log("Error connecting : "+ err )); + } } diff --git a/src/Api/src/controller/spotify-controller/spotifyCtrl.ts b/src/Api/src/controller/spotify-controller/spotifyCtrl.ts index b826ee0..a26f33b 100644 --- a/src/Api/src/controller/spotify-controller/spotifyCtrl.ts +++ b/src/Api/src/controller/spotify-controller/spotifyCtrl.ts @@ -23,6 +23,8 @@ class SpotifyController implements Controller { this.router.get(`${this.path}/callback`,this.getAccessToken); // this.router.post(`${this.path}/refresh`,this.getRefreshToken); this.router.get(`${this.path}/play/:musicId`, this.getMusic); + this.router.get(`${this.path}/spot`, this.getSpot); + } // need to put in ENvironement file @@ -134,85 +136,49 @@ class SpotifyController implements Controller { } } - // }; - + public getMusic(){ return null; } - // private spotifyRequest = (params : AuthReqBody) => { - // return new Promise(() => { - // console.log("============ on est laaaa sa mer"); - // var code = req.query.code || null; - // var state = req.query.state || null; + public getSpot = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise => { + const spots = [ + { + name: "blue", + sourceUrl: "https://cdns-images.dzcdn.net/images/artist/399e7e760d8fedf3cc2891e9c0c41658/200x200-000000-80-0-0.jpg", + index: 3 + }, + { + name: "strange history", + sourceUrl: "https://images.genius.com/339dfe2a7c0adf9a5d08febf29a845f4.1000x1000x1.jpg", + index: 7 + }, + { + name: "oboy album", + sourceUrl: "https://i.pinimg.com/originals/ad/cc/d5/adccd58a0d0ff516a6114703cd05810e.jpg", + index: 1 + } + ]; + try { + res.send(spots); + + } catch (error) { + console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee'); + console.log(error); + next(new HttpException(400, 'On peut pas avoir darray mec')); + } } - // axios.post(this.API_URL, { - // form: params, - // headers: { - // "Authorization": "Basic " + new Buffer(this.CLIENT_ID + ":" + this.CLIENT_SECRET).toString('base64') - // }, - // json: true - // }); - // }).then(resp => { - // if (resp.statusCode != 200) { - // return Promise.reject({ - // statusCode: resp.statusCode, - // body: resp.body - // }); - // } - // return Promise.resolve(resp.body); - // }) - // .catch(err => { - // return Promise.reject({ - // statusCode: 500, - // body: err.stringify({}) - // }); - // }); - // }; private getAccessToken = async ( req: Request, res: Response, next: NextFunction ): Promise => { - - // console.log("heheh"); - // try { - // var code = req.query.code; - // var state = req.query.state; - // console.log("================================================================================================================================"); - // console.log(req); - // console.log("================================================================================================================================"); - - // if (state === null) { - // next(new HttpException(400, 'Cannot create twerk')); - // } else { - // const resp : any = await axios.post('https://accounts.spotify.com/api/token',{ - // form: { - // code: code, - // redirect_uri: this.CALLBACK_URL, - // // code_verifier : this.ENCRYPTION_SECRET.stringCrypt, - // grant_type: 'authorization_code' - // }, - // headers: { - // 'Authorization': 'Basic ' + (new Buffer(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')), - // },json: true} - // ); - // if (resp.statusCode != 200) { - // console.log(resp.statusCode, resp.body); - // } - // else{ - // console.log("error"); - // console.log(resp.statusCode, resp.body); - // } - - // } - // // }); - // } catch (error) { - // console.log(error); - // next(new HttpException(400, 'Cannot create spot')); - // } var code = req.query.code; var state = req.query.state || null; @@ -249,39 +215,9 @@ class SpotifyController implements Controller { } - // axios({ - // method: 'post', - // url: 'https://accounts.spotify.com/api/token', - // data: { - // firstName: 'Fred', - // lastName: 'Flintstone' - // }, - // headers: { - // 'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')), - // 'Content-Type' : 'application/x-www-form-urlencoded' - // }, - // }); - // request.post(authOptions, function(error, response, body) { - // if (!error && response.statusCode === 200) { - // var access_token = body.access_token; - // console.log(access_token); - // res.redirect(200, '/') - // } - // console.log(error); - // }); }; - - private encrypt(text :any){ - return CryptoJS.AES.encrypt(text, this.ENCRYPTION_SECRET.stringCrypt).toString(); - }; - - private decrypt(text: any) { - console.log("errer"); - var bytes = CryptoJS.AES.decrypt(text, this.ENCRYPTION_SECRET.stringCrypt); - return bytes.toString(CryptoJS.enc.Utf8); - }; } export default SpotifyController; diff --git a/src/Api/src/controller/user-controller/userCtrl.ts b/src/Api/src/controller/user-controller/userCtrl.ts index f7f5bc8..004e20e 100644 --- a/src/Api/src/controller/user-controller/userCtrl.ts +++ b/src/Api/src/controller/user-controller/userCtrl.ts @@ -1,11 +1,15 @@ import { Router, Request, Response, NextFunction, RequestHandler } from 'express'; import Controller from '../Icontroller'; import HttpException from '../../middleware/exeption/httpExeption'; +// import LocationService from '../../service/LocationService'; +import IUser from '../../database/schema/User/UserInterface'; +import UserService from '../../service/UserService'; +import validator from '../../database/schema/User/UserValidation' +import validationMiddleware from '../../middleware/validation/ValidatorMiddleware'; +import authenticator from '../../middleware/authMiddleware' import LocationService from '../../service/LocationService'; - - -class TaskController implements Controller { - public path = '/task'; +class UserController implements Controller { + public path = '/users'; public router = Router(); private userService = new UserService(); private locationService = new LocationService(); @@ -15,131 +19,193 @@ class TaskController implements Controller { } private initialiseRoutes(): void { - //create - this.router.post(`${this.path}`,this.createUser); - - // // get One - this.router.get (`${this.path}/:userId`, this.getUserById); - // // get All - this.router.get (`${this.path}`, this.getAllUsers); + this.router.post( + `${this.path}/register`, + validationMiddleware(validator.register), + this.register + ); + this.router.post( + `${this.path}/login`, + validationMiddleware(validator.login), + this.login + ); + this.router.get(`${this.path}`, authenticator, this.getUser); + this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext); - //update One - this.router.put (`${this.path}/:userId`, this.updateUser); - //Delete One - this.router.delete (`${this.path}/:userId`, this.deleteUser); + // //create + // this.router.post(`${this.path}`,this.createUser); + + // // // get One + // this.router.get (`${this.path}/:userId`, this.getUserById); + // // // get All + // this.router.get (`${this.path}`, this.getAllUsers); + // //update One + // this.router.put (`${this.path}/:userId`, this.updateUser); + // //Delete One + // this.router.delete (`${this.path}/:userId`, this.deleteUser); } - private createUser = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - try { + // private createUser = async ( + // req: Request, + // res: Response, + // next: NextFunction + // ): Promise => { + // try { - console.log(req.body); - const reqBody:CreateTaskReqBody = Object.assign({}, req.body); - checkIfIsValidCreateTaskReqBody(reqBody); - await this.userService.createUserById(reqBody.fin - ); + // console.log(req.body); + // const reqBody:CreateTaskReqBody = Object.assign({}, req.body); + // checkIfIsValidCreateTaskReqBody(reqBody); + // await this.userService.createUserById(reqBody.fin + // ); - res.status(200).send({ status: "Success", msg: "Success add" }); + // res.status(200).send({ status: "Success", msg: "Success add" }); - } catch (error) { - next(new HttpException(400, 'Cannot create post')); - } - }; - private readonly getUserById: RequestHandler = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - try { - const id = req.params.taskId; - const userId = req.params.userId; + // } catch (error) { + // next(new HttpException(400, 'Cannot create post')); + // } + // }; + // private readonly getUserById: RequestHandler = async ( + // req: Request, + // res: Response, + // next: NextFunction + // ): Promise => { + // try { + // const id = req.params.taskId; + // const userId = req.params.userId; - const data = await this.userService.getUserById(id, userId); - res.status(201).send(data); + // const data = await this.userService.getUserById(id, userId); + // res.status(201).send(data); - } - catch(error){ - next(new HttpException(400, 'Cannot create post')); - } + // } + // catch(error){ + // next(new HttpException(400, 'Cannot create post')); + // } - } - private readonly getAllUsers: RequestHandler = async ( + // } + // private readonly getAllUsers: RequestHandler = async ( + // req: Request, + // res: Response, + // next: NextFunction + // ): Promise => { + // try { + // const userId = req.params.userId; + // const tasks = await this.userService.getUsers(userId); + // const responseList = tasks.map(task => new TaskResumedRes(task)); + // res.status(201).send(responseList); + + // } + // catch(error){ + // next(new HttpException(400, 'Cannot get user task')); + // } + + // } + + // private deleteUser = async ( + // req: Request, + // res: Response, + // next: NextFunction + // ): Promise => { + // try { + // const id = req.params.taskId; + // const userId = req.params.userId; + // await this.userService.DeleteUser(id, userId); + // return res.status(200).send({ status: "Success", msg: "Data Removed" }); + // } catch (error) { + // next(new HttpException(400, 'Cannot create post')); + // } + // }; + + // private updateUser = async ( + // req: Request, + // res: Response, + // next: NextFunction + // ): Promise => { + // try { + + // const taskId = req.params.taskId; + // const userId = req.params.userId; + // const reqBody:CreateTaskReqBody = Object.assign({}, req.body); + + // const updatedTask = await this.userService.UpdateTask( + // // req.auth!.uid, + // taskId, + // userId, + // // firebase.auth().currentUser.getIdToken() + // reqBody.nom, + // reqBody.description, + // reqBody.logo, + // reqBody.duration, + // reqBody.done, + // // reqBody.tags, + // reqBody.repepat, + // reqBody.deb, + // reqBody.fin + // ); + // // res.send('Success add'); + // // res.status(201).json({ task }); + // res.status(204).send(`Update a new contact: ${updatedTask}`); + // } catch (error) { + // console.log(error); + // next(new HttpException(403, 'Cannot create post')); + // } + // }; + + + private register = async ( req: Request, res: Response, next: NextFunction ): Promise => { try { - const userId = req.params.userId; - const tasks = await this.userService.getUsers(userId); - const responseList = tasks.map(task => new TaskResumedRes(task)); - res.status(201).send(responseList); + // the FladId should be created by the Userservice + const { name, email, password,idFlad, idSpotify } = req.body; + const token = await this.userService.register( + name, + email, + password, + idFlad, + idSpotify + ); + + res.status(201).json({ token }); + } catch (error : any) { + next(new HttpException(400, error.message)); } - catch(error){ - next(new HttpException(400, 'Cannot get user task')); - } - - } - - private deleteUser = async ( + }; + + private login = async ( req: Request, res: Response, next: NextFunction ): Promise => { try { - const id = req.params.taskId; - const userId = req.params.userId; - await this.userService.DeleteUser(id, userId); - return res.status(200).send({ status: "Success", msg: "Data Removed" }); - } catch (error) { - next(new HttpException(400, 'Cannot create post')); + const { email, password } = req.body; + + const token = await this.userService.login(email, password); + + res.status(200).json({ token }); + } catch (error : any) { + next(new HttpException(400, error.message)); } }; - - private updateUser = async ( + + private getUser = ( req: Request, res: Response, next: NextFunction - ): Promise => { - try { - - const taskId = req.params.taskId; - const userId = req.params.userId; - const reqBody:CreateTaskReqBody = Object.assign({}, req.body); - - const updatedTask = await this.userService.UpdateTask( - // req.auth!.uid, - taskId, - userId, - // firebase.auth().currentUser.getIdToken() - reqBody.nom, - reqBody.description, - reqBody.logo, - reqBody.duration, - reqBody.done, - // reqBody.tags, - reqBody.repepat, - reqBody.deb, - reqBody.fin - ); - // res.send('Success add'); - // res.status(201).json({ task }); - res.status(204).send(`Update a new contact: ${updatedTask}`); - } catch (error) { - console.log(error); - next(new HttpException(403, 'Cannot create post')); + ): Response | void => { + if (!req.user) { + return next(new HttpException(404, 'No logged in user')); } + + res.status(200).send({ data: req.user }); }; - - - private getUserNext: RequestHandler = async ( + private getUserNext = async ( req: Request, res: Response, next: NextFunction @@ -152,18 +218,29 @@ class TaskController implements Controller { console.log('Impossible de convertir la chaîne en nombre'); } //} - const userId = req.body.userId; + const userId = req.user.idFlad; const data = await this.locationService.getNearUser(userId,latitude,longitude); console.log(data); res.status(201).send(data); } - catch(error){ - next(new HttpException(400, 'Cannot create post')); + catch(error : any){ + next(new HttpException(400, 'Cannot create get netUser')); } } + } -export default TaskController; \ No newline at end of file +export default UserController; + + + +declare global { + namespace Express { + export interface Request { + user: IUser; + } + } +} \ No newline at end of file diff --git a/src/Api/src/database/MongoDataBase.ts b/src/Api/src/database/MongoDataBase.ts index 1e9bd7d..dd8f5d3 100644 --- a/src/Api/src/database/MongoDataBase.ts +++ b/src/Api/src/database/MongoDataBase.ts @@ -1,3 +1,4 @@ -const uri = "mongodb+srv://fladDevDb:ZslYlNRWIOUU7i6o@fladcluster.b29tytu.mongodb.net/?retryWrites=true&w=majority" -export default db = new MongoClient(uri); \ No newline at end of file +// export default db = new MongoClient(uri); + + diff --git a/src/Api/src/database/schema/LocationSchema.ts b/src/Api/src/database/schema/LocationSchema.ts new file mode 100644 index 0000000..795d45f --- /dev/null +++ b/src/Api/src/database/schema/LocationSchema.ts @@ -0,0 +1,36 @@ +import { Schema, model } from 'mongoose'; + + +const locationSchema = new Schema( + { + + idFlad: { + type: String, + required: true, + unique: true, + }, + + latitude: { + type: Number, + required: true, + }, + longitude: { + type: Number, + required: true, + }, + + + }, + { timestamps: true } +); + +// fladDevDb +// ZslYlNRWIOUU7i6o +export default model('Location', locationSchema); + +export interface ILocation extends Document { + idFlad: string; + latitude : number; + longitude: number; + +} \ No newline at end of file diff --git a/src/Api/src/database/schema/NotificationSchema.ts b/src/Api/src/database/schema/NotificationSchema.ts index 7aa9525..7207503 100644 --- a/src/Api/src/database/schema/NotificationSchema.ts +++ b/src/Api/src/database/schema/NotificationSchema.ts @@ -1,6 +1,8 @@ -const notificationSchema = new mongoose.Schema({ +import { Schema, model } from 'mongoose'; + +const notificationSchema = new Schema({ type: {type: String, required: true}, content: {type: String, required: true} }); -export default {Annonce: mongoose.model("nofitication", notificationSchema)} \ No newline at end of file +export default {Notification: model("nofitication", notificationSchema)} \ No newline at end of file diff --git a/src/Api/src/database/schema/Token/IToken.ts b/src/Api/src/database/schema/Token/IToken.ts new file mode 100644 index 0000000..ff6e4af --- /dev/null +++ b/src/Api/src/database/schema/Token/IToken.ts @@ -0,0 +1,8 @@ +import { Schema } from 'mongoose'; + +interface IToken extends Object { + id: Schema.Types.ObjectId; + expiresIn: number; +} + +export default IToken; \ No newline at end of file diff --git a/src/Api/src/database/schema/User/UserInterface.ts b/src/Api/src/database/schema/User/UserInterface.ts new file mode 100644 index 0000000..2308f88 --- /dev/null +++ b/src/Api/src/database/schema/User/UserInterface.ts @@ -0,0 +1,11 @@ +import { Document } from 'mongoose'; + +export default interface IUser extends Document { + email: string; + name: string; + password: string; + idFlad : string; + idSpotify : string; + + isValidPassword(password: string): Promise; +} \ No newline at end of file diff --git a/src/Api/src/database/schema/User/UserSchema.ts b/src/Api/src/database/schema/User/UserSchema.ts new file mode 100644 index 0000000..8f8cdd5 --- /dev/null +++ b/src/Api/src/database/schema/User/UserSchema.ts @@ -0,0 +1,79 @@ +// maye this file should me the UserModel like we had in php cause it's here we verrify the password +import IUser from "./UserInterface"; +import { Schema, model } from 'mongoose'; +import bcrypt from 'bcrypt'; +// const userSchema: Schema = new Schema({ +// pseudo: {type: String, index: { unique: true }}, +// email: {type: String}, +// idDafl: {type: String, index: { unique: true }}, +// idSpotify: {type: String}, +// password: {type: String}, +// prenom: {type: String, default: ""}, +// description: {type: String, default: ""}, +// nom: {type: String, default: ""}, +// ville: {type: String, default: ""}, +// profilPic: {type: String}, +// noteList: [], +// notifications: [], +// friends: {type: [String] }, +// favoris: [], +// conversations: {type: [String] } +// }); + +const userSchema = new Schema( + { + + idFlad: { + type: String, + required: true, + unique: true, + }, + idSpotify: { + type: String, + required: true, + unique: true, + }, + name: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + // this mean that we identify user by email + unique: true, + // delete the whitespace + trim: true, + }, + password: { + type: String, + }, + + }, + { timestamps: true } +); + +// this means that we hash the user password before saving it to the database +userSchema.pre('save', async function (next) { + if (!this.isModified('password')) { + //just had that to be sure that the api still going + return next(); + } + + const hash = await bcrypt.hash(this.password, 8); + + this.password = hash; + + next(); +}); + +userSchema.methods.isValidPassword = async function ( + password: string +): Promise< boolean | Error> { + return await bcrypt.compare(password, this.password); +}; + +// fladDevDb +// ZslYlNRWIOUU7i6o +export default model('User', userSchema); +// export const User: Model = model('User', userSchema); diff --git a/src/Api/src/database/schema/User/UserValidation.ts b/src/Api/src/database/schema/User/UserValidation.ts new file mode 100644 index 0000000..ec48660 --- /dev/null +++ b/src/Api/src/database/schema/User/UserValidation.ts @@ -0,0 +1,18 @@ +import Joi from 'joi'; + +const register = Joi.object({ + name: Joi.string().max(30).required(), + + email: Joi.string().email().required(), + + password: Joi.string().min(6).required(), + // can add an field like confimPassword and cheked that the password is equal to the confirmPassword + +}); + +const login = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().required(), +}); + +export default { register, login }; \ No newline at end of file diff --git a/src/Api/src/database/schema/UserSchema.ts b/src/Api/src/database/schema/UserSchema.ts deleted file mode 100644 index 27153b8..0000000 --- a/src/Api/src/database/schema/UserSchema.ts +++ /dev/null @@ -1,21 +0,0 @@ - -const userSchema: Schema = new mongoose.Schema({ - pseudo: {type: String, index: { unique: true }}, - email: {type: String}, - idDafl: {type: String, index: { unique: true }}, - idSpotify: {type: String}, - password: {type: String}, - prenom: {type: String, default: ""}, - description: {type: String, default: ""}, - nom: {type: String, default: ""}, - ville: {type: String, default: ""}, - profilPic: {type: String}, - noteList: [], - notifications: [], - friends: {type: [String] }, - favoris: [], - conversations: {type: [String] } -}); -// fladDevDb -// ZslYlNRWIOUU7i6o -export const User: Model = model('User', userSchema); diff --git a/src/Api/src/middleware/authMiddleware.ts b/src/Api/src/middleware/authMiddleware.ts new file mode 100644 index 0000000..bb0f875 --- /dev/null +++ b/src/Api/src/middleware/authMiddleware.ts @@ -0,0 +1,45 @@ +import { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; +import IToken from '../database/schema/Token/IToken'; +import UserSchema from '../database/schema/User/UserSchema'; +import token from '../model/token'; +import HttpException from './exeption/httpExeption'; + +async function authenticatedMiddleware( + req: Request, + res: Response, + next: NextFunction +): Promise { + const bearer = req.headers.authorization; + + if (!bearer || !bearer.startsWith('Bearer ')) { + return next(new HttpException(401, 'Unauthorised')); + } + + const accessToken = bearer.split('Bearer ')[1].trim(); + try { + const payload: IToken | jwt.JsonWebTokenError = await token.verifyToken( + accessToken + ); + + if (payload instanceof jwt.JsonWebTokenError) { + return next(new HttpException(401, 'Unauthorised')); + } + + const user = await UserSchema.findById(payload.id) + .select('-password') + .exec(); + + if (!user) { + return next(new HttpException(401, 'Unauthorised')); + } + + req.user = user; + + return next(); + } catch (error) { + return next(new HttpException(401, 'Unauthorised')); + } +} + +export default authenticatedMiddleware; \ No newline at end of file diff --git a/src/Api/src/middleware/validation/ValidatorMiddleware.ts b/src/Api/src/middleware/validation/ValidatorMiddleware.ts new file mode 100644 index 0000000..51e58f9 --- /dev/null +++ b/src/Api/src/middleware/validation/ValidatorMiddleware.ts @@ -0,0 +1,33 @@ +import { Request, Response, NextFunction, RequestHandler } from 'express'; +import Joi from 'joi'; + +function validationMiddleware(schema: Joi.Schema): RequestHandler { + return async ( + req: Request, + res: Response, + next: NextFunction + ): Promise => { + const validationOptions = { + abortEarly: false, + allowUnknown: true, + stripUnknown: true, + }; + + try { + const value = await schema.validateAsync( + req.body, + validationOptions + ); + req.body = value; + next(); + } catch (e: any) { + const errors: string[] = []; + e.details.forEach((error: Joi.ValidationErrorItem) => { + errors.push(error.message); + }); + res.status(400).send({ errors: errors }); + } + }; +} + +export default validationMiddleware; \ No newline at end of file diff --git a/src/Api/src/model/token.ts b/src/Api/src/model/token.ts new file mode 100644 index 0000000..ebc6f65 --- /dev/null +++ b/src/Api/src/model/token.ts @@ -0,0 +1,27 @@ +import jwt from 'jsonwebtoken'; +import IUser from '../database/schema/User/UserInterface'; +import IToken from '../database/schema/Token/IToken'; + +export const createToken = (user: IUser): string => { + return jwt.sign({ id: user._id }, "foo" as jwt.Secret, { + expiresIn: '100d', + }); +}; + +export const verifyToken = async ( + token: string +): Promise => { + return new Promise((resolve, reject) => { + jwt.verify( + token, + "foo" as jwt.Secret, + (err, payload) => { + if (err) return reject(err); + + resolve(payload as IToken); + } + ); + }); +}; + +export default { createToken, verifyToken }; \ No newline at end of file diff --git a/src/Api/src/server.ts b/src/Api/src/server.ts index 87e362b..1b3e87b 100644 --- a/src/Api/src/server.ts +++ b/src/Api/src/server.ts @@ -1,9 +1,10 @@ import App from "./app"; import SpotifyController from "./controller/spotify-controller/spotifyCtrl"; import PingController from "./controller/TestCtrl"; +import UserController from "./controller/user-controller/userCtrl"; const app = new App( - [new PingController(), new SpotifyController()], + [new PingController(), new SpotifyController(), new UserController()], Number(8080) // Number(process.env.PORT) diff --git a/src/Api/src/service/LocationService.ts b/src/Api/src/service/LocationService.ts index dd12d59..077c0a5 100644 --- a/src/Api/src/service/LocationService.ts +++ b/src/Api/src/service/LocationService.ts @@ -1,45 +1,48 @@ // import db from '../database'; import { Place, PlacePosition, Position, UserLocation } from '../model/locationModel'; import axios from 'axios'; +import LocationSchema from "../database/schema/LocationSchema"; class LocationService { - private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A"; - public async getNearUser(uuid : string, latitude : number, longitude : number) + private locationCollection = LocationSchema; + // private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A"; + public async getNearUser(idFlad : string, latitude : number, longitude : number) { - const UserCollectionref = db.collection('UserPosition'); - db.collection('UserPosition').doc(uuid).set({uuid : uuid, latitude : latitude, longitude : longitude}); - // const newPosition = { - // name: 'Los Angeles', - // state: 'CA', - // country: 'USA' - // }; - const snapshot = await UserCollectionref.where("uuid", "!=", uuid).get(); - if (snapshot.empty) { - console.log('No matching documents.'); - return; - } + await this.locationCollection.findOneAndUpdate( + { idFlad }, + { idFlad, latitude, longitude }, + { upsert: true } + ); + + const snapshot = await this.locationCollection.find({ idFlad: { $ne: idFlad } }); + if (snapshot.length === 0) { + console.log('No matching documents.'); + return; + } let dbUsersList:UserLocation[] = []; snapshot.forEach(doc => { - dbUsersList.push(new UserLocation(doc.data().uuid,doc.data().latitude,doc.data().longitude)); - console.log(doc.id, '=>', doc.data()); + dbUsersList.push(new UserLocation(doc.idFlad,doc.latitude,doc.longitude)); + console.log(doc.idFlad, '=>', doc); }); - - let listUser: string[] = []; //Set the listUser to an empty list + // missing the curent music + let listUser: string[] = []; dbUsersList.forEach(user => { console.log(user); - const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude); //With the function meters, determinate the distance between the current user and the user who is in the actual row + const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude); console.log(user.uuid,dist); - if (dist <= 5) { //If the user in the actual row is less than 100 meters away of the current user + if (dist <= 100) { - listUser.push(user.uuid); //Add the username and the ID of the song that user who is in the actual row is listening + listUser.push(user.uuid); } - }); + }); + + - return listUser; //Return an array - // $listUser[] = ['user' => $userID, 'music' => $idMusic]; //Add the username and the ID of the song that user who is in the actual row is listening + return listUser; + // $listUser[] = {userID,idMusic}; } diff --git a/src/Api/src/service/UserService.ts b/src/Api/src/service/UserService.ts index e69de29..682a963 100644 --- a/src/Api/src/service/UserService.ts +++ b/src/Api/src/service/UserService.ts @@ -0,0 +1,63 @@ +import UserSchema from "../database/schema/User/UserSchema"; +import token from "../model/token"; + + +class UserService { + private user = UserSchema; + + /** + * Register a new user + */ + public async register( + name: string, + email: string, + password: string, + idFlad : string, + idSpotify : string + ): Promise { + try { + const user = await this.user.create({ + name, + email, + password, + idFlad, + idSpotify + }); + + const accessToken = token.createToken(user); + + return accessToken; + } catch (error : any) { + throw new Error(error.message); + } + } + + /** + * Attempt to login a user + */ + public async login( + email: string, + password: string + ): Promise { + try { + // should maybe creat a method base on id and other information for better security + // need to view with Emre + const user = await this.user.findOne({ email }); + // const user = await this.user.findById(idFlad); + + if (!user) { + throw new Error('Unable to find user with that email address'); + } + + if (await user.isValidPassword(password)) { + return token.createToken(user); + } else { + throw new Error('Wrong credentials given'); + } + } catch (error) { + throw new Error('Unable to create user'); + } + } +} + +export default UserService; \ No newline at end of file diff --git a/src/FLAD/App.tsx b/src/FLAD/App.tsx index 1a25a6d..9ac5ad2 100644 --- a/src/FLAD/App.tsx +++ b/src/FLAD/App.tsx @@ -8,20 +8,50 @@ import { NavigationContainer } from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Animated, Dimensions, ImageBackground, StyleSheet, Text, View } from 'react-native'; import Card from './components/Card'; -import Login from './pages/login'; -import Spot from './pages/spot'; +// import Login from './screens/login'; +import Spot from './screens/spot'; +import StackNavigation from './navigation/Navigation'; export default function App() { const Stack = createBottomTabNavigator(); +const {width : wWidht} = Dimensions.get("window"); return ( - + + ); } +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + alignItems: 'center', + justifyContent: 'center', + }, + card: { + borderRadius : 8, + shadowRadius : 20, + shadowColor : '#' + }, + image: { + width: 320, + height: 440, + borderRadius: 18, + resizeMode : "cover", + placeholder: "assets/images/loadingPlaceholder.gif" + }, + gradient : { + position : "absolute", + top : 0, + left : 0, + right : 0, + height : 209, + } +}); \ No newline at end of file diff --git a/src/FLAD/Model/Music.tsx b/src/FLAD/Model/Music.tsx index fe933c9..cc20984 100644 --- a/src/FLAD/Model/Music.tsx +++ b/src/FLAD/Model/Music.tsx @@ -1,4 +1,4 @@ -class Music { +export default class Music { private id : string; private name : string; private artist : string; @@ -10,4 +10,5 @@ class Music { this.artist = artist; this.linkCover = linkCover; } + } diff --git a/src/FLAD/Model/Spot.tsx b/src/FLAD/Model/Spot.tsx index 53d37d0..cc8fa6d 100644 --- a/src/FLAD/Model/Spot.tsx +++ b/src/FLAD/Model/Spot.tsx @@ -1,6 +1,7 @@ + class Spot { private userId : string; - public music : Music;; + public music : Music; constructor(userId : string, music : Music){ this.userId = userId; this.music = music; diff --git a/src/FLAD/Model/User.tsx b/src/FLAD/Model/User.tsx index 39fad6d..1e7f2bd 100644 --- a/src/FLAD/Model/User.tsx +++ b/src/FLAD/Model/User.tsx @@ -1,6 +1,6 @@ class User { //attributes from DAFL - private idDafl : any;; + private idFlad : any;; private idSpotify : any; //constructors constructor(){ diff --git a/src/FLAD/assets/icons/light_like.riv b/src/FLAD/assets/icons/light_like.riv deleted file mode 100644 index 8326bd4..0000000 Binary files a/src/FLAD/assets/icons/light_like.riv and /dev/null differ diff --git a/src/FLAD/assets/icons/splash.png b/src/FLAD/assets/icons/splash.png index 8e0dc29..7126405 100644 Binary files a/src/FLAD/assets/icons/splash.png and b/src/FLAD/assets/icons/splash.png differ diff --git a/src/FLAD/assets/icons/splashs.png b/src/FLAD/assets/icons/splashs.png new file mode 100644 index 0000000..8e0dc29 Binary files /dev/null and b/src/FLAD/assets/icons/splashs.png differ diff --git a/src/FLAD/components/Card.tsx b/src/FLAD/components/Card.tsx index 65d9691..9ca470b 100644 --- a/src/FLAD/components/Card.tsx +++ b/src/FLAD/components/Card.tsx @@ -14,23 +14,17 @@ const SCREEN_WIDTH = Dimensions.get('window').width interface CardProps { title: string; image: any; - onSwipe: (direction: "left" | "right") => void; } type ContextType = { translateX: number; translateY: number; }; -const Card = ({ title, image, onSwipe} : CardProps) => { +const Card : React.FC = ({ title, image} : CardProps) => { const translateX = useSharedValue(0); const translateY = useSharedValue(0); - - const hapti = (() => { - - // Haptics.NotificationFeedbackType.Success - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium) - }); + const onGestureEvent = useAnimatedGestureHandler< PanGestureHandlerGestureEvent, ContextType @@ -47,10 +41,9 @@ const Card = ({ title, image, onSwipe} : CardProps) => { // translateX.value = withSpring(0); // translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints )) if (translateX.value > 160) { - hapti() - onSwipe("right"); + // onSwipe("right"); } else if (translateX.value < -160) { - onSwipe("left"); + // onSwipe("left"); } else { translateX.value = withSpring(0); translateY.value = withSpring(0); @@ -75,8 +68,6 @@ const Card = ({ title, image, onSwipe} : CardProps) => { ( translateX.value, [-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 2], [1, 0, 0]); - - return { opacity : opacityl, }; @@ -86,7 +77,7 @@ const Card = ({ title, image, onSwipe} : CardProps) => { const opacityl = interpolate ( translateX.value, [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4], - [0.85, 1, 1]); + [0.75, 1, 0.75]); return { opacity : opacityl, @@ -95,7 +86,7 @@ const Card = ({ title, image, onSwipe} : CardProps) => { const opacDStyle = useAnimatedStyle(() => { const opacityl = interpolate ( translateY.value, - [-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 4], + [-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 2], [0, 0, 1]); return { opacity : opacityl, @@ -133,9 +124,7 @@ const Card = ({ title, image, onSwipe} : CardProps) => { return ( - - - + { },opacRStyle]} > { },opacLStyle]} > @@ -171,7 +160,7 @@ const Card = ({ title, image, onSwipe} : CardProps) => { > @@ -187,8 +176,7 @@ const styles = StyleSheet.create({ card : { justifyContent : 'center', alignItems : 'center', - borderColor : 'red', - borderWidth : 3, + }, image : { borderRadius : 24, @@ -202,6 +190,3 @@ const styles = StyleSheet.create({ export default Card; - - - diff --git a/src/FLAD/components/button/button.tsx b/src/FLAD/components/button/button.tsx index 7125873..737bba3 100644 --- a/src/FLAD/components/button/button.tsx +++ b/src/FLAD/components/button/button.tsx @@ -4,7 +4,7 @@ import Animated,{ Extrapolate, interpolate, useAnimatedGestureHandler, useAnimat import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; import * as Haptics from 'expo-haptics'; -import Icons from '../../assets/icons/icon'; +import Icons from '../../assets/icons/icons/icon'; import Rive, { Fit, RiveRef } from 'rive-react-native'; const {width : wWidht} = Dimensions.get("window"); diff --git a/src/FLAD/data/data.ts b/src/FLAD/data/data.ts index 805a6e5..6dcc56d 100644 --- a/src/FLAD/data/data.ts +++ b/src/FLAD/data/data.ts @@ -1,12 +1,12 @@ export const cards = [{ - name : "blue", - sourceUrl : "https://cdns-images.dzcdn.net/images/artist/399e7e760d8fedf3cc2891e9c0c41658/200x200-000000-80-0-0.jpg", - index : 3 - }, - { - name : "her", - sourceUrl : "https://images.unsplash.com/photo-1484589065579-248aad0d8b13?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1359&q=80", - index : 3 - } - - ] \ No newline at end of file + name : "blue", + sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", + index : 3 +}, +{ + name : "her", + sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", + index : 3 +} + +] \ No newline at end of file diff --git a/src/FLAD/navigation/Navigation.tsx b/src/FLAD/navigation/Navigation.tsx index b53d1bc..79cc464 100644 --- a/src/FLAD/navigation/Navigation.tsx +++ b/src/FLAD/navigation/Navigation.tsx @@ -4,6 +4,7 @@ import Home from '../screens/spot'; import FavoritePage from '../screens/favoritePage'; import { createStackNavigator } from '@react-navigation/stack'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +// import Spot from '../screens/spot'; export default function StackNavigation() { const Stack = createBottomTabNavigator(); @@ -12,10 +13,10 @@ export default function StackNavigation() { + }} > { if (direction === 'right') { // Swiped right - console.log("===================") } else if (direction === 'left') { // Swiped left } // update the state of the cards state when it remove thisy setCards(cards.filter((_, i) => i !== index)); - setcurrentCard(cards[cards.length -1]); }; - const hapti = (() => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy) - getValueFor(MY_SECURE_AUTH_STATE_KEY) - .then(key => { (key != null) ? getUserData(key) :console.log("error key is nullll") } ) ; - // Haptics.NotificationFeedbackType.Success - }); + // const hapti = (() => { + // Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy) + // getValueFor(MY_SECURE_AUTH_STATE_KEY) + // .then(key => { (key != null) ? getUserData(key) :console.log("error key is nullll") } ) ; + // // Haptics.NotificationFeedbackType.Success + // }); //////////////////////////////////////////////////////////////// - const [locationData, setLocationData] = useState(); - const [prevLocationData, setPrevLocationData] = useState(); - const [nearbyUsers, setNearbyUsers] = useState([]); - const [currentMusic, setCurrentMusic] = useState(""); - - async function getLocation() { - var { status } = await Location.requestForegroundPermissionsAsync(); - if (status !== 'granted') { - console.log('Permission to access location was denied'); - return; - } - - let currentLocation = await Location.getCurrentPositionAsync({}); - setLocationData({ - latitude: currentLocation.coords.latitude, - longitude: currentLocation.coords.longitude, - timestamp: currentLocation.timestamp - }); - }; - async function sendLocationToServer() { - getLocation(); - if (!locationData) return; - if (prevLocationData && locationData.latitude === prevLocationData.latitude && locationData.longitude === prevLocationData.longitude) { - return; - } - try { - const response = await axios.post( - 'http://localhost/api/users/david/nextToMe', - locationData - ); - - if (response.status !== 200) { - throw new Error('Failed to send location to server'); - } - - setPrevLocationData(locationData); - setNearbyUsers(response.data); - } catch (error) { - console.error(error); - } - }; + // const [locationData, setLocationData] = useState(); + // const [prevLocationData, setPrevLocationData] = useState(); + // const [nearbyUsers, setNearbyUsers] = useState([]); + // const [currentMusic, setCurrentMusic] = useState(""); + + // async function getLocation() { + // var { status } = await Location.requestForegroundPermissionsAsync(); + // if (status !== 'granted') { + // console.log('Permission to access location was denied'); + // return; + // } + + // let currentLocation = await Location.getCurrentPositionAsync({}); + // setLocationData({ + // latitude: currentLocation.coords.latitude, + // longitude: currentLocation.coords.longitude, + // timestamp: currentLocation.timestamp + // }); + // }; + // async function sendLocationToServer() { + // getLocation(); + // if (!locationData) return; + // if (prevLocationData && locationData.latitude === prevLocationData.latitude && locationData.longitude === prevLocationData.longitude) { + // return; + // } + // try { + // const response = await axios.post( + // 'http://localhost/api/users/david/nextToMe', + // locationData + // ); + + // if (response.status !== 200) { + // throw new Error('Failed to send location to server'); + // } + + // setPrevLocationData(locationData); + // setNearbyUsers(response.data); + // } catch (error) { + // console.error(error); + // } + // }; - setInterval(sendLocationToServer, 30000) + // setInterval(sendLocationToServer, 30000) return ( - - - + onSwipe(index, direction)} /> {/*