Compare commits
175 Commits
Before Width: | Height: | Size: 366 KiB After Width: | Height: | Size: 368 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 867 KiB |
Before Width: | Height: | Size: 208 KiB |
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 247 KiB |
After Width: | Height: | Size: 26 KiB |
@ -1,15 +0,0 @@
|
||||
> Why do I have a folder named ".expo" in my project?
|
||||
|
||||
The ".expo" folder is created when an Expo project is started using "expo start" command.
|
||||
|
||||
> What do the files contain?
|
||||
|
||||
- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
|
||||
- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator.
|
||||
- "settings.json": contains the server configuration that is used to serve the application manifest.
|
||||
|
||||
> Should I commit the ".expo" folder?
|
||||
|
||||
No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
|
||||
|
||||
Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"hostType": "lan",
|
||||
"lanType": "ip",
|
||||
"dev": true,
|
||||
"minify": false,
|
||||
"urlRandomness": null,
|
||||
"https": false
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
# Xcode
|
||||
!**/*.xcodeproj
|
||||
!**/*.pbxproj
|
||||
!**/*.xcworkspacedata
|
||||
!**/*.xcsettings
|
||||
!**/*.xcscheme
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
**/.xcode.env.local
|
||||
**/src/config.ts
|
||||
|
||||
# Gradle
|
||||
/build/
|
||||
/packages/react-native-gradle-plugin/build/
|
||||
/packages/rn-tester/build
|
||||
/packages/rn-tester/android/app/.cxx/
|
||||
/packages/rn-tester/android/app/build/
|
||||
/packages/rn-tester/android/app/gradle/
|
||||
/packages/rn-tester/android/app/gradlew
|
||||
/packages/rn-tester/android/app/gradlew.bat
|
||||
/ReactAndroid/build/
|
||||
/ReactAndroid/.cxx/
|
||||
/ReactAndroid/gradle/
|
||||
/ReactAndroid/gradlew
|
||||
/ReactAndroid/gradlew.bat
|
||||
/ReactAndroid/external-artifacts/build/
|
||||
/ReactAndroid/external-artifacts/artifacts/
|
||||
/ReactAndroid/hermes-engine/build/
|
||||
/ReactAndroid/hermes-engine/.cxx/
|
||||
/template/android/app/build/
|
||||
/template/android/build/
|
||||
|
||||
# Buck
|
||||
.buckd
|
||||
buck-out
|
||||
/.lsp.buckd
|
||||
/.lsp-buck-out
|
||||
/ReactAndroid/src/main/jni/prebuilt/lib/
|
||||
/ReactAndroid/src/main/gen
|
||||
|
||||
# Android Studio
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
|
||||
# Watchman
|
||||
.watchmanconfig
|
||||
|
||||
# Android
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
/android/*
|
||||
!/android/README.md
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
*.log
|
||||
.nvm
|
||||
package-lock.json
|
||||
dist
|
||||
**/.env
|
||||
|
||||
# OS X
|
||||
.DS_Store
|
||||
|
||||
# Test generated files
|
||||
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
|
||||
*.js.meta
|
||||
|
||||
/coverage
|
||||
/third-party
|
||||
|
||||
# Test Reports
|
||||
/reports
|
||||
|
||||
# Stack Dumps generated when programs crash (Ex. bash.exe.stackdump on Win)
|
||||
*.stackdump
|
||||
|
||||
# Root dir shouldn't have Xcode project
|
||||
/*.xcodeproj
|
||||
|
||||
# ReactCommon subdir shouldn't have Xcode project
|
||||
/ReactCommon/**/*.xcodeproj
|
||||
|
||||
# Libs that shouldn't have Xcode project
|
||||
/Libraries/FBLazyVector/**/*.xcodeproj
|
||||
/Libraries/RCTRequired/**/*.xcodeproj
|
||||
/React/CoreModules/**/*.xcodeproj
|
||||
/React/FBReactNativeSpec/**/*.xcodeproj
|
||||
/packages/react-native-codegen/**/*.xcodeproj
|
||||
|
||||
# Ruby Gems (Bundler)
|
||||
/vendor
|
||||
/template/vendor
|
||||
|
||||
# iOS / CocoaPods
|
||||
/template/ios/build/
|
||||
/template/ios/Pods/
|
||||
/template/ios/Podfile.lock
|
||||
/packages/rn-tester/Gemfile.lock
|
||||
|
||||
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
|
||||
/packages/rn-tester/Pods/*
|
||||
!/packages/rn-tester/Pods/__offline_mirrors_hermes__
|
||||
!/packages/rn-tester/Pods/__offline_mirrors_jsc__
|
||||
|
||||
# @react-native/codegen
|
||||
/React/FBReactNativeSpec/FBReactNativeSpec
|
||||
/packages/react-native-codegen/lib
|
||||
/packages/react-native-codegen/tmp/
|
||||
/ReactCommon/react/renderer/components/rncore/
|
||||
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
|
||||
|
||||
|
||||
# Additional SDKs
|
||||
/sdks/download
|
||||
/sdks/hermes
|
||||
/sdks/hermesc
|
||||
|
||||
# Visual studio
|
||||
.vscode
|
||||
.vs
|
||||
|
||||
# Android memory profiler files
|
||||
*.hprof
|
||||
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
@ -0,0 +1,8 @@
|
||||
FROM node:latest
|
||||
WORKDIR /Api
|
||||
COPY package.json /Api
|
||||
COPY tsconfig.json /Api
|
||||
COPY . /Api
|
||||
RUN npm install && npm run build
|
||||
EXPOSE 80
|
||||
CMD ["node", "."]
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"watch": ["src"],
|
||||
"ext": ".ts.js",
|
||||
"exec": "ts-node ./src/index.ts"
|
||||
}
|
@ -1,79 +1,60 @@
|
||||
import express, { Application } from 'express';
|
||||
// import compression from 'compression';
|
||||
import cors from 'cors';
|
||||
// import db from './database';
|
||||
// import morgan from 'morgan';
|
||||
import Controller from './controller/Icontroller';
|
||||
// import ErrorMiddleware from './middleware/error.middleware';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import bodyParser from 'body-parser';
|
||||
import IController from './controllers/interfaces/IController';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
// to secure
|
||||
// import helmet from 'helmet';
|
||||
|
||||
import http from 'http';
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
import { specs } from './utils/swagger';
|
||||
|
||||
class App {
|
||||
public express: Application;
|
||||
public port: number;
|
||||
public dataBase: null;
|
||||
|
||||
public server : any;
|
||||
|
||||
constructor(controllers: Controller[], port: number) {
|
||||
constructor(controllers: IController[], port: number) {
|
||||
this.express = express();
|
||||
this.port = port;
|
||||
this.dataBase = null;
|
||||
|
||||
this.initialiseDatabase();
|
||||
this.initialiseMiddleware();
|
||||
this.initialiseControllers(controllers);
|
||||
|
||||
// this.initialiseErrorHandling();
|
||||
|
||||
this.initDatabase();
|
||||
this.initMiddleware();
|
||||
this.initControllers(controllers);
|
||||
this.initSwagger();
|
||||
}
|
||||
|
||||
private initialiseMiddleware(): void {
|
||||
// this.express.use(helmet());
|
||||
private initMiddleware(): void {
|
||||
this.express.use(cors());
|
||||
// this.express.use(morgan('dev'));
|
||||
this.express.use(cookieParser());
|
||||
this.express.use(express.json());
|
||||
this.express.use(express.urlencoded({ extended: false }));
|
||||
// this.express.use(compression());
|
||||
// mine
|
||||
this.express.use(bodyParser.json());
|
||||
this.express.use(bodyParser.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
private initialiseControllers(controllers: Controller[]): void {
|
||||
controllers.forEach((controller: Controller) => {
|
||||
private initControllers(controllers: IController[]): void {
|
||||
controllers.forEach((controller: IController) => {
|
||||
this.express.use('/api', controller.router);
|
||||
this.express.get('/toto', (req, res) => {
|
||||
res.send('Hello World!');
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// private initialiseErrorHandling(): void {
|
||||
// this.express.use(ErrorMiddleware);
|
||||
// }
|
||||
|
||||
public listen(): void {
|
||||
const server = this.express.listen(this.port, () => {
|
||||
this.express.listen(this.port, () => {
|
||||
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 ));
|
||||
|
||||
private initDatabase(): void {
|
||||
const MONGO_URL = `mongodb+srv://FladDev:${process.env.MONGO_PASSWORD}@flad.mliekr2.mongodb.net/?retryWrites=true&w=majority`;
|
||||
mongoose.connect(MONGO_URL)
|
||||
.then(() => console.log("Connect to MongoDB database successfully"))
|
||||
.catch(error => console.log("Error connecting : " + error));
|
||||
}
|
||||
|
||||
public initSwagger(): void {
|
||||
this.express.use("/swagger", swaggerUi.serve, swaggerUi.setup(specs),);
|
||||
console.log(`Docs available at /${this.port}/swagger`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default App;
|
After Width: | Height: | Size: 8.6 KiB |
@ -1,15 +0,0 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
interface Controller {
|
||||
path: string;
|
||||
router: Router;
|
||||
// constructor() {
|
||||
// this.initialiseRoutes();
|
||||
// }
|
||||
|
||||
// initialiseRoutes(): void ;
|
||||
|
||||
}
|
||||
// il y a un truc inject
|
||||
|
||||
export default Controller;
|
@ -1,27 +0,0 @@
|
||||
import { Router } from "express";
|
||||
import Controller from "./Icontroller";
|
||||
|
||||
type PingResponse = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default class PingController implements Controller {
|
||||
public path = '/ping';
|
||||
public router = Router();
|
||||
|
||||
constructor() {
|
||||
this.initialiseRoutes();
|
||||
}
|
||||
|
||||
private initialiseRoutes(): void {
|
||||
this.router.get("/ping", async (_req, res) => {
|
||||
const response = await this.getMessage();
|
||||
return res.send(response);
|
||||
});
|
||||
}
|
||||
async getMessage(): Promise<PingResponse> {
|
||||
return {
|
||||
message: "pong",
|
||||
};
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
export default class CryptString{
|
||||
|
||||
stringCrypt: string;
|
||||
|
||||
constructor(length : number){
|
||||
this.stringCrypt = this.generateRandomString(length);
|
||||
}
|
||||
generateRandomString (length : number){
|
||||
var text = '';
|
||||
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
export type AuthReqBody = {
|
||||
grant_type: string,
|
||||
redirect_uri?: string,
|
||||
code?: string,
|
||||
refresh_token?: string,
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
import Controller from '../Icontroller';
|
||||
import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
|
||||
import { AuthReqBody } from './request/authReqBody';
|
||||
import HttpException from '../../middleware/exeption/httpExeption';
|
||||
import axios from 'axios';
|
||||
import CryptString from './crypt';
|
||||
import AES from 'crypto-js'
|
||||
import querystring from 'querystring';
|
||||
import qs from 'qs';
|
||||
|
||||
class SpotifyController implements Controller {
|
||||
public path = '/spotify';
|
||||
public router = Router();
|
||||
|
||||
constructor() {
|
||||
console.log("useeeee");
|
||||
|
||||
this.initialiseRoutes();
|
||||
}
|
||||
initialiseRoutes() {
|
||||
// this.router.post(`${this.path}`,this.createTask);
|
||||
this.router.get(`${this.path}/exchange`,this.login);
|
||||
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
|
||||
// private readonly CLIENT_CALLBACK_URL = "http://localhost:8080/callback";
|
||||
private readonly API_URL = "https://accounts.spotify.com/api/token";
|
||||
private readonly CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10";
|
||||
private readonly CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82";
|
||||
private readonly CLIENT_CALLBACK_URL = "https://auth.expo.io/@thed47/FLAD//callback";
|
||||
private readonly CALLBACK_URL = "http://localhost:8080/api/spotify/callback";
|
||||
private readonly SCOPES ='user-read-private user-read-email user-read-playback-state user-read-currently-playing user-read-recently-played playlist-modify-public ugc-image-upload user-modify-playback-state';
|
||||
private readonly ENCRYPTION_SECRET = new CryptString(16);
|
||||
|
||||
private login = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
|
||||
console.log("useeeee");
|
||||
try {
|
||||
// const params = req.body;
|
||||
// if (!params.refresh_token) {
|
||||
// return res.json({
|
||||
// "error": "Parameter missing"
|
||||
// });
|
||||
// }
|
||||
|
||||
// this.spotifyRequest({
|
||||
// grant_type: "authorization_code",
|
||||
// redirect_uri: this.CLIENT_CALLBACK_URL,
|
||||
// // code: params.code
|
||||
// })
|
||||
res.redirect('https://accounts.spotify.com/authorize?' +
|
||||
querystring.stringify({
|
||||
response_type: 'code',
|
||||
client_id: this.CLIENT_ID,
|
||||
scope: this.SCOPES,
|
||||
redirect_uri: this.CALLBACK_URL,
|
||||
state: this.ENCRYPTION_SECRET.stringCrypt
|
||||
}));
|
||||
// .then(session => {
|
||||
// let result = {
|
||||
// "access_token": session.access_token,
|
||||
// "expires_in": session.expires_in,
|
||||
// "refresh_token": this.encrypt(session.refresh_token)
|
||||
// };
|
||||
// return res.send(result);
|
||||
// })
|
||||
// .catch(response => {
|
||||
// return res.json(response);
|
||||
// });
|
||||
} catch (error) {
|
||||
next(new HttpException(400, 'Cannot create spot'));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
private getRefreshToken = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
|
||||
console.log('UUse2');
|
||||
|
||||
try {
|
||||
const params = req.query.refresh_token;
|
||||
if (!req.query.refresh_token) {
|
||||
return res.json({
|
||||
"error": "Parameter refresh_token missing"
|
||||
});
|
||||
}
|
||||
var authOptions = {
|
||||
method: 'POST',
|
||||
url: 'https://accounts.spotify.com/api/token',
|
||||
data: qs.stringify({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: params
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
|
||||
'Content-Type' : 'application/x-www-form-urlencoded'
|
||||
},
|
||||
json: true
|
||||
};
|
||||
|
||||
// request.post(authOptions, function(error, response, body) {
|
||||
// if (!error && response.statusCode === 200) {
|
||||
// var access_token = body.access_token;
|
||||
// res.send({
|
||||
// 'access_token': access_token
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
axios(authOptions)
|
||||
.then(session => {
|
||||
if(session.status === 200){
|
||||
|
||||
res.send({
|
||||
"access_token": session.data.access_token,
|
||||
"expires_in": session.data.expires_in
|
||||
});
|
||||
}});
|
||||
console.log("goood");
|
||||
} catch (error) {
|
||||
console.log("errur");
|
||||
next(new HttpException(400, 'Cannot create post'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public getSpot = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
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'));
|
||||
} }
|
||||
|
||||
|
||||
private getAccessToken = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
|
||||
var code = req.query.code;
|
||||
var state = req.query.state || null;
|
||||
// var storedState = req.cookies ? req.cookies[stateKey] : null;
|
||||
var authOptions = {
|
||||
method: 'POST',
|
||||
url: 'https://accounts.spotify.com/api/token',
|
||||
data: qs.stringify({
|
||||
code: code,
|
||||
redirect_uri: this.CALLBACK_URL,
|
||||
grant_type: 'authorization_code'
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
|
||||
'Content-Type' : 'application/x-www-form-urlencoded'
|
||||
},
|
||||
json: true
|
||||
};
|
||||
try {
|
||||
console.log('presssquuueee');
|
||||
var resp = await axios(authOptions);
|
||||
if (resp.status === 200) {
|
||||
console.log(resp);
|
||||
console.log('oon esttt laaa');
|
||||
var access_token = resp.data.access_token;
|
||||
console.log(access_token);
|
||||
// should redirect res.redirect('/')
|
||||
res.json("ok");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee');
|
||||
console.log(error);
|
||||
next(new HttpException(400, 'On peut pas te connecter mec'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
export default SpotifyController;
|
@ -1,248 +0,0 @@
|
||||
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 UserController implements Controller {
|
||||
public path = '/users';
|
||||
public router = Router();
|
||||
private userService = new UserService();
|
||||
private locationService = new LocationService();
|
||||
|
||||
constructor() {
|
||||
this.initialiseRoutes();
|
||||
}
|
||||
|
||||
private initialiseRoutes(): void {
|
||||
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);
|
||||
|
||||
|
||||
// //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<Response | void> => {
|
||||
// try {
|
||||
|
||||
// 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" });
|
||||
|
||||
|
||||
// } catch (error) {
|
||||
// next(new HttpException(400, 'Cannot create post'));
|
||||
// }
|
||||
// };
|
||||
// private readonly getUserById: RequestHandler = async (
|
||||
// req: Request,
|
||||
// res: Response,
|
||||
// next: NextFunction
|
||||
// ): Promise<Response | void> => {
|
||||
// try {
|
||||
// const id = req.params.taskId;
|
||||
// const userId = req.params.userId;
|
||||
|
||||
// const data = await this.userService.getUserById(id, userId);
|
||||
// res.status(201).send(data);
|
||||
|
||||
// }
|
||||
// catch(error){
|
||||
// next(new HttpException(400, 'Cannot create post'));
|
||||
// }
|
||||
|
||||
// }
|
||||
// private readonly getAllUsers: RequestHandler = async (
|
||||
// req: Request,
|
||||
// res: Response,
|
||||
// next: NextFunction
|
||||
// ): Promise<Response | void> => {
|
||||
// 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<Response | void> => {
|
||||
// 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<Response | void> => {
|
||||
// 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<Response | void> => {
|
||||
try {
|
||||
// the FladId should be created by the Userservice
|
||||
const { name, email, password , idFlad, idSpotify } = req.body;
|
||||
console.log(name, email, password, idFlad, idSpotify);
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
private login = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
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 getUser = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Response | void => {
|
||||
if (!req.user) {
|
||||
return next(new HttpException(404, 'No logged in user'));
|
||||
}
|
||||
|
||||
res.status(200).send({ data: req.user });
|
||||
};
|
||||
|
||||
private getUserNext = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const longitude = Number(req.body.longitude);
|
||||
const latitude = Number(req.body.latitude);
|
||||
//verify::val_int(){
|
||||
if (isNaN(longitude) || isNaN(latitude)) {
|
||||
console.log('Impossible de convertir la chaîne en nombre');
|
||||
}
|
||||
//}
|
||||
const currentMusicId = req.body.currentMusicId;
|
||||
const userId = req.user.idFlad;
|
||||
const data = await this.locationService.getNearUser(userId,latitude,longitude, currentMusicId);
|
||||
console.log(data);
|
||||
res.status(201).send(data);
|
||||
|
||||
}
|
||||
catch(error : any){
|
||||
next(new HttpException(400, 'Cannot create get netUser'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default UserController;
|
||||
|
||||
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
export interface Request {
|
||||
user: IUser;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
interface IController {
|
||||
path: string;
|
||||
router: Router;
|
||||
}
|
||||
|
||||
export default IController;
|
@ -0,0 +1,196 @@
|
||||
import IController from './interfaces/IController';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import axios from 'axios';
|
||||
import qs from 'qs';
|
||||
|
||||
class SpotifyController implements IController {
|
||||
public path = '/spotify';
|
||||
public router = Router();
|
||||
public readonly CLIENT_ID_SPOTIFY = process.env.CLIENT_ID_SPOTIFY;
|
||||
public readonly CLIENT_SECRET_SPOTIFY = process.env.CLIENT_SECRET_SPOTIFY;
|
||||
private readonly API_URL = "https://accounts.spotify.com/api/token";
|
||||
private readonly CALLBACK = 'http://localhost:8080/api/spotify/callback';
|
||||
private readonly SCOPES = 'user-read-private user-read-email user-read-playback-state user-read-currently-playing user-read-recently-played playlist-modify-public ugc-image-upload user-modify-playback-state';
|
||||
|
||||
constructor() {
|
||||
this.initRoutes();
|
||||
}
|
||||
|
||||
initRoutes() {
|
||||
/**
|
||||
* @swagger
|
||||
* /api/spotify/exchange:
|
||||
* get:
|
||||
* summary: Initiate the Spotify login flow
|
||||
* description: Redirect the user to the Spotify login page for authorization
|
||||
* tags:
|
||||
* - Spotify
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: redirectUrl
|
||||
* schema:
|
||||
* type: string
|
||||
* description: The URL to redirect the user after Spotify authorization (optional)
|
||||
* responses:
|
||||
* 302:
|
||||
* description: Redirecting to Spotify login page
|
||||
* 400:
|
||||
* description: Bad request - Cannot connect to Spotify
|
||||
*/
|
||||
this.router.get(`${this.path}/exchange`, this.login);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/spotify/callback:
|
||||
* get:
|
||||
* summary: Handle Spotify callback and exchange code for access token
|
||||
* description: Handle Spotify callback and exchange the received code for an access token
|
||||
* tags:
|
||||
* - Spotify
|
||||
* responses:
|
||||
* 302:
|
||||
* description: Redirecting with access token information
|
||||
* 400:
|
||||
* description: Bad request - Error connecting to Spotify
|
||||
*/
|
||||
this.router.get(`${this.path}/callback`, this.getAccessToken);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/spotify/refresh:
|
||||
* get:
|
||||
* summary: Refresh the Spotify access token using a refresh token
|
||||
* description: Refresh the Spotify access token using a refresh token
|
||||
* tags:
|
||||
* - Spotify
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: refresh_token
|
||||
* schema:
|
||||
* type: string
|
||||
* required: true
|
||||
* description: The refresh token obtained during the initial authorization
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Successfully refreshed access token
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* access_token:
|
||||
* type: string
|
||||
* description: The new access token
|
||||
* refresh_token:
|
||||
* type: string
|
||||
* description: The new refresh token
|
||||
* expires_in:
|
||||
* type: number
|
||||
* description: The time until the access token expires (in seconds)
|
||||
* 400:
|
||||
* description: Bad request - Cannot refresh the access token
|
||||
*/
|
||||
this.router.get(`${this.path}/refresh`, this.getRefreshToken);
|
||||
}
|
||||
|
||||
private readonly clientRedirect = 'spotify_final_redirect-uri-key';
|
||||
|
||||
private login = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const redirectResponse = req.query.redirectUrl ? req.query.redirectUrl : req.headers.referer;
|
||||
res.cookie(this.clientRedirect, redirectResponse);
|
||||
res.redirect('https://accounts.spotify.com/authorize?' +
|
||||
qs.stringify({
|
||||
response_type: 'code',
|
||||
client_id: this.CLIENT_ID_SPOTIFY,
|
||||
scope: this.SCOPES,
|
||||
redirect_uri: this.CALLBACK,
|
||||
}));
|
||||
} catch (error) {
|
||||
res.status(400).send('Cannot connect: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
private getRefreshToken = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
const params = req.query.refresh_token;
|
||||
if (!req.query.refresh_token) {
|
||||
return res.json({
|
||||
"error": "Parameter refresh_token missing"
|
||||
});
|
||||
}
|
||||
const authOptions = {
|
||||
method: 'POST',
|
||||
url: 'https://accounts.spotify.com/api/token',
|
||||
data: qs.stringify({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: params
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + (Buffer.from(this.CLIENT_ID_SPOTIFY + ':' + this.CLIENT_SECRET_SPOTIFY).toString('base64')),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
json: true
|
||||
};
|
||||
|
||||
axios(authOptions)
|
||||
.then(session => {
|
||||
if (session.status === 200) {
|
||||
res.send({
|
||||
"access_token": session.data.access_token,
|
||||
"refresh_token": session.data.refresh_token,
|
||||
"expires_in": session.data.expires_in
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
res.status(400).send("Cannot get a new refresh token");
|
||||
});
|
||||
}
|
||||
|
||||
private getAccessToken = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
let code = req.query.code;
|
||||
let storedRedirectUri = req.cookies ? req.cookies[this.clientRedirect] : null;
|
||||
let authOptions = {
|
||||
method: 'POST',
|
||||
url: this.API_URL,
|
||||
data: qs.stringify({
|
||||
code: code,
|
||||
redirect_uri: this.CALLBACK,
|
||||
grant_type: 'authorization_code'
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + (Buffer.from(this.CLIENT_ID_SPOTIFY + ':' + this.CLIENT_SECRET_SPOTIFY).toString('base64')),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
json: true
|
||||
};
|
||||
try {
|
||||
const resp = await axios(authOptions);
|
||||
if (resp.status === 200) {
|
||||
let access_token = resp.data.access_token;
|
||||
let expiration = resp.data.expires_in;
|
||||
let refresh = resp.data.refresh_token
|
||||
res.clearCookie(this.clientRedirect);
|
||||
res.redirect(`${storedRedirectUri}?` +
|
||||
qs.stringify({
|
||||
"access_token": access_token,
|
||||
"expires_in": expiration,
|
||||
"refresh_token": refresh
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(400).send('Error connection: ' + error.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default SpotifyController;
|
@ -0,0 +1,803 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import IController from './interfaces/IController';
|
||||
import User from '../models/User';
|
||||
import UserService from '../services/UserService';
|
||||
import validator from '../middlewares/UserValidation'
|
||||
import validationMiddleware from '../middlewares/validationMiddleware';
|
||||
import authenticator from '../middlewares/authMiddleware'
|
||||
import LocationService from '../services/LocationService';
|
||||
import axios from 'axios';
|
||||
import { IMusic } from '../models/Music';
|
||||
import * as fs from 'fs';
|
||||
import * as base64js from 'base64-js';
|
||||
|
||||
class UserController implements IController {
|
||||
public path = '/user';
|
||||
public authPath = '/auth';
|
||||
public router = Router();
|
||||
private userService = new UserService();
|
||||
private locationService = new LocationService();
|
||||
|
||||
constructor() {
|
||||
this.initRoutes();
|
||||
}
|
||||
|
||||
private initRoutes(): void {
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/register:
|
||||
* post:
|
||||
* summary: Register a new user
|
||||
* description: Register a new user with the provided details
|
||||
* tags:
|
||||
* - Authentication
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* default: john.doe@example.com
|
||||
* password:
|
||||
* type: string
|
||||
* default: stringPassword123
|
||||
* name:
|
||||
* type: string
|
||||
* default: john_doe
|
||||
* tokenSpotify:
|
||||
* type: string
|
||||
* responses:
|
||||
* 201:
|
||||
* description: User registered successfully
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
* 401:
|
||||
* description: Unauthorized - Spotify token is invalid
|
||||
* 409:
|
||||
* description: Conflict - Email or username is already in use
|
||||
* 500:
|
||||
* description: Internal Server Error - Spotify account not authorized or not found
|
||||
*/
|
||||
this.router.post(
|
||||
`${this.authPath}/register`,
|
||||
validationMiddleware(validator.register),
|
||||
this.register
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/login:
|
||||
* post:
|
||||
* summary: Login a user
|
||||
* description: Login with the provided email and password
|
||||
* tags:
|
||||
* - Authentication
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* default: john.doe@example.com
|
||||
* password:
|
||||
* type: string
|
||||
* default: stringPassword123
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User logged in successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* token:
|
||||
* type: string
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
*/
|
||||
this.router.post(
|
||||
`${this.authPath}/login`,
|
||||
validationMiddleware(validator.login),
|
||||
this.login
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user:
|
||||
* get:
|
||||
* summary: Get user information
|
||||
* description: Get information about the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User logged in successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
*/
|
||||
this.router.get(`${this.path}`, authenticator, this.getUser);
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users:
|
||||
* get:
|
||||
* summary: Get information about multiple users
|
||||
* description: Get information about multiple users based on provided user ids
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: ids
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Comma-separated list of user ids
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Users information retrieved successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
*/
|
||||
this.router.get(`${this.path}s`, authenticator, this.getUsers);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user:
|
||||
* delete:
|
||||
* summary: Delete the authenticated user
|
||||
* description: Delete the authenticated user and associated data
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 204:
|
||||
* description: User deleted successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 404:
|
||||
* description: User not found
|
||||
*/
|
||||
this.router.delete(`${this.path}`, authenticator, this.deleteUser);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/nextTo:
|
||||
* get:
|
||||
* summary: Get users near the authenticated user
|
||||
* description: Get information about users near the authenticated user based on location
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: longitude
|
||||
* schema:
|
||||
* type: number
|
||||
* description: Longitude of the user's current location
|
||||
* - in: query
|
||||
* name: latitude
|
||||
* schema:
|
||||
* type: number
|
||||
* description: Latitude of the user's current location
|
||||
* - in: query
|
||||
* name: currentMusic
|
||||
* schema:
|
||||
* type: string
|
||||
* description: The ID of the currently playing music
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Users near the authenticated user retrieved successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* type: array
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
*/
|
||||
this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/musics/{id}:
|
||||
* delete:
|
||||
* summary: Delete a music from the authenticated user's liked list
|
||||
* description: Delete a music from the authenticated user's liked list by music id
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: string
|
||||
* description: The ID of the music to delete
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Music deleted successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 404:
|
||||
* description: Music not found
|
||||
*/
|
||||
this.router.delete(`${this.path}/musics/:id`, authenticator, this.deleteMusic);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/musics:
|
||||
* post:
|
||||
* summary: Add a music to the authenticated user's liked list
|
||||
* description: Add a music to the authenticated user's liked list
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* musicId:
|
||||
* type: string
|
||||
* description: The ID of the music to add
|
||||
* userId:
|
||||
* type: string
|
||||
* description: The ID of the user who liked the music
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Music added to liked list successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
*/
|
||||
this.router.post(`${this.path}/musics`, authenticator, this.addMusic);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/musics:
|
||||
* get:
|
||||
* summary: Get the list of musics liked by the authenticated user
|
||||
* description: Get the list of musics liked by the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of musics retrieved successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* musics:
|
||||
* type: array
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
*/
|
||||
this.router.get(`${this.path}/musics`, authenticator, this.getMusics);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/name:
|
||||
* put:
|
||||
* summary: Update the name of the authenticated user
|
||||
* description: Update the name of the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* description: The new name for the user
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User name updated successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
* 409:
|
||||
* description: Conflict - The provided name is already in use by another user
|
||||
*/
|
||||
this.router.put(`${this.path}/name`, authenticator, this.setName);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/email:
|
||||
* put:
|
||||
* summary: Update the email of the authenticated user
|
||||
* description: Update the email of the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: The new email for the user
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User email updated successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 400:
|
||||
* description: Bad request - Invalid input data
|
||||
* 409:
|
||||
* description: Conflict - The provided email is already in use by another user
|
||||
*/
|
||||
this.router.put(`${this.path}/email`, authenticator, this.setEmail);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/spotify:
|
||||
* put:
|
||||
* summary: Update the spotify account
|
||||
* description: Update the spotify account of the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* tokenSpotify:
|
||||
* type: string
|
||||
* description: Spotify token
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Spotify account updated successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 409:
|
||||
* description: Conflict - The provided token is already in use by another user
|
||||
* 500:
|
||||
* description: Internal Server Error - Spotify account not authorized or not found
|
||||
*/
|
||||
this.router.put(`${this.path}/spotify`, authenticator, this.setSpotify);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/image:
|
||||
* put:
|
||||
* summary: Update the profile image of the authenticated user
|
||||
* description: Update the profile image of the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* image:
|
||||
* type: string
|
||||
* format: base64
|
||||
* description: The new profile image for the user (base64 encoded)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User profile image updated successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 500:
|
||||
* description: Internal Server Error - Unable to update the profile image
|
||||
*/
|
||||
this.router.put(`${this.path}/image`, authenticator, this.setImage);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/password:
|
||||
* put:
|
||||
* summary: Update the password of the authenticated user
|
||||
* description: Update the password of the authenticated user
|
||||
* tags:
|
||||
* - User
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* oldPassword:
|
||||
* type: string
|
||||
* description: The current password of the user
|
||||
* newPassword:
|
||||
* type: string
|
||||
* description: The new password for the user
|
||||
* responses:
|
||||
* 200:
|
||||
* description: User password updated successfully
|
||||
* 401:
|
||||
* description: Unauthorized - Invalid or missing authentication token
|
||||
* 500:
|
||||
* description: Internal Server Error - Unable to update the password
|
||||
*/
|
||||
this.router.put(`${this.path}/password`, authenticator, this.setPassword);
|
||||
}
|
||||
|
||||
private register = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
|
||||
let access_token;
|
||||
let idSpotify: string;
|
||||
let image: string;
|
||||
const { name, email, password, tokenSpotify } = req.body;
|
||||
const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:8080/api';
|
||||
const refreshUrl = `${apiBaseUrl}/spotify/refresh?refresh_token=${tokenSpotify}`;
|
||||
try {
|
||||
const authOptions = {
|
||||
method: 'GET',
|
||||
url: refreshUrl,
|
||||
json: true
|
||||
};
|
||||
const authResponse = await axios(authOptions);
|
||||
if (authResponse.status === 200) {
|
||||
access_token = authResponse.data.access_token;
|
||||
const headers = {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
};
|
||||
const resp = await axios.get('https://api.spotify.com/v1/me', { headers });
|
||||
if (resp.status == 200) {
|
||||
const images = resp.data.images;
|
||||
idSpotify = resp.data.id;
|
||||
if (images && images.length > 0) {
|
||||
images.sort((a: any, b: any) => b.height - a.height);
|
||||
image = images[0].url;
|
||||
}
|
||||
else {
|
||||
const imagePath = './src/assets/images/default_user.png';
|
||||
const imageBuffer = fs.readFileSync(imagePath);
|
||||
const base64Image = 'data:image/png;base64,' + base64js.fromByteArray(imageBuffer);
|
||||
image = base64Image
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
if (error.response.status === 400) {
|
||||
res.status(401).send("Unauthorized: Spotify token is invalid");
|
||||
return;
|
||||
}
|
||||
res.status(500).send("Internal Server Error: Unable to authenticate with Spotify");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await this.userService.register(
|
||||
name.toLowerCase(),
|
||||
email.toLowerCase(),
|
||||
password,
|
||||
idSpotify,
|
||||
tokenSpotify,
|
||||
image
|
||||
);
|
||||
res.status(201).json({ token });
|
||||
} catch (error: any) {
|
||||
res.status(409).json(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
private login = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
const token = await this.userService.login(email, password);
|
||||
res.status(200).json({ token });
|
||||
} catch (error: any) {
|
||||
res.status(400).json(error.message)
|
||||
}
|
||||
};
|
||||
|
||||
private getUser = (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Response | void => {
|
||||
res.status(200).send({ data: req.user });
|
||||
};
|
||||
|
||||
private getUsers = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
const userIds = req.query.ids as string;
|
||||
|
||||
if (!userIds) {
|
||||
return res.status(200).json([]);
|
||||
}
|
||||
|
||||
const userIdArray = userIds.split('&');
|
||||
|
||||
try {
|
||||
const users = await this.userService.getUsers(userIdArray);
|
||||
res.json(users);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
private deleteUser = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
await this.userService.delete(_id);
|
||||
await this.locationService.delete(_id);
|
||||
res.status(204).send();
|
||||
} catch (error: any) {
|
||||
res.status(404).json(error.message)
|
||||
}
|
||||
};
|
||||
|
||||
private getUserNext = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const longitude = Number(req.query.longitude);
|
||||
const latitude = Number(req.query.latitude);
|
||||
if (isNaN(longitude) || isNaN(latitude)) {
|
||||
console.log('Unable to convert string to number');
|
||||
throw new Error('Unable to convert string to number');
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const musicId = String(req.query.currentMusic);
|
||||
const data = await this.locationService.getNearUser(userId, musicId, latitude, longitude);
|
||||
res.status(201).send(data);
|
||||
}
|
||||
catch (error: any) {
|
||||
res.status(400).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private deleteMusic = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const musicId: string = req.params.id;
|
||||
if (!musicId) {
|
||||
return res.status(400).json({ error: 'musicId are required fields.' });
|
||||
}
|
||||
|
||||
const deleted = await this.userService.deleteMusic(_id, musicId);
|
||||
|
||||
if (deleted) {
|
||||
res.status(200).send({ message: 'Music deleted successfully.' });
|
||||
} else {
|
||||
res.status(404).json({ error: 'Music not found.' });
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
res.status(404).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private addMusic = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const { musicId, userId } = req.body;
|
||||
if (!musicId || !userId) {
|
||||
return res.status(400).json({ error: 'musicId and userId are required fields.' });
|
||||
}
|
||||
const music: IMusic = {
|
||||
musicId,
|
||||
userId,
|
||||
date: new Date(),
|
||||
};
|
||||
await this.userService.addMusic(_id, music);
|
||||
res.status(201).send({ music });
|
||||
} catch (error: any) {
|
||||
res.status(400).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private getMusics = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const userId: string = req.user.id;
|
||||
const musics = await this.userService.getMusics(userId);
|
||||
return res.status(200).json({ musics });
|
||||
} catch (error: any) {
|
||||
res.status(400).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private setName = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const { name } = req.body;
|
||||
|
||||
const regex = /^\w+$/;
|
||||
if (!regex.test(name) || !name) {
|
||||
return res.status(400).json({ error: "Name should only contain alphanumeric characters (letters, numbers, and underscores)" });
|
||||
}
|
||||
|
||||
await this.userService.setName(_id, name.toLowerCase());
|
||||
|
||||
res.status(200).json({ message: 'Name updated successfully' });
|
||||
} catch (error: any) {
|
||||
res.status(409).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private setEmail = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const { email } = req.body;
|
||||
|
||||
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (!regex.test(email) || !email) {
|
||||
return res.status(400).json({ error: "Invalid email" });
|
||||
}
|
||||
|
||||
await this.userService.setEmail(_id, email.toLowerCase());
|
||||
|
||||
res.status(200).json({ message: 'Email updated successfully' });
|
||||
} catch (error: any) {
|
||||
res.status(409).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private setSpotify = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
let access_token;
|
||||
let idAccount: string;
|
||||
let image: string;
|
||||
const { _id, idSpotify } = req.user;
|
||||
const { tokenSpotify } = req.body;
|
||||
|
||||
if (!tokenSpotify) {
|
||||
return res.status(400).json({ error: 'TokenSpotify is missing in the request.' });
|
||||
}
|
||||
|
||||
const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:8080/api';
|
||||
const refreshUrl = `${apiBaseUrl}/spotify/refresh?refresh_token=${tokenSpotify}`;
|
||||
try {
|
||||
const authOptions = {
|
||||
method: 'GET',
|
||||
url: refreshUrl,
|
||||
json: true
|
||||
};
|
||||
const authResponse = await axios(authOptions);
|
||||
if (authResponse.status === 200) {
|
||||
access_token = authResponse.data.access_token;
|
||||
const headers = {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
};
|
||||
const resp = await axios.get('https://api.spotify.com/v1/me', { headers });
|
||||
if (resp.status == 200) {
|
||||
const images = resp.data.images;
|
||||
idAccount = resp.data.id;
|
||||
if (idSpotify === idAccount) {
|
||||
return res.status(400).json({ error: 'idSpotify cannot be the same as idAccount.' });
|
||||
}
|
||||
if (images && images.length > 0) {
|
||||
images.sort((a: any, b: any) => b.height - a.height);
|
||||
image = images[0].url;
|
||||
}
|
||||
else {
|
||||
const imagePath = './src/assets/images/default_user.png';
|
||||
const imageBuffer = fs.readFileSync(imagePath);
|
||||
const base64Image = 'data:image/png;base64,' + base64js.fromByteArray(imageBuffer);
|
||||
image = base64Image
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
res.status(500).send("Internal Server Error: Unable to authenticate with Spotify");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.userService.setSpotify(_id, tokenSpotify, idAccount, image);
|
||||
|
||||
res.status(200).json({ message: 'Spotify token updated successfully' });
|
||||
} catch (error: any) {
|
||||
res.status(409).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private setImage = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const { image } = req.body;
|
||||
|
||||
await this.userService.setImage(_id, image);
|
||||
|
||||
res.status(200).json({ message: 'Image updated successfully' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
private setPassword = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { _id } = req.user;
|
||||
const { oldPassword, newPassword } = req.body;
|
||||
|
||||
await this.userService.setPassword(_id, oldPassword, newPassword);
|
||||
|
||||
res.status(200).json({ message: 'Password updated successfully' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
export default UserController;
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
export interface Request {
|
||||
user: User;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { Schema, model } from 'mongoose';
|
||||
import { Location } from '../models/Location';
|
||||
|
||||
const locationSchema = new Schema({
|
||||
userId: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
musicId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
latitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
longitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
export default model<Location>('Location', locationSchema);
|
@ -1,4 +0,0 @@
|
||||
|
||||
// export default db = new MongoClient(uri);
|
||||
|
||||
|
@ -0,0 +1,62 @@
|
||||
import User from "../models/User";
|
||||
import { Schema, model } from 'mongoose';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
const userSchema = new Schema({
|
||||
idSpotify: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
tokenSpotify: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
trim: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
,
|
||||
image: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
musics_likes: {
|
||||
type: [{
|
||||
musicId: String,
|
||||
userId: String,
|
||||
date: Date
|
||||
}],
|
||||
default: []
|
||||
}
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
userSchema.pre<User>('save', async function (next) {
|
||||
if (!this.isModified('password')) {
|
||||
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);
|
||||
};
|
||||
|
||||
export default model<User>('User', userSchema);
|
@ -1,39 +0,0 @@
|
||||
import { Schema, model,Document } from 'mongoose';
|
||||
|
||||
|
||||
const locationSchema = new Schema(
|
||||
{
|
||||
|
||||
idFlad: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
|
||||
latitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
longitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
currentMusicId : {
|
||||
type: String,
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
// fladDevDb
|
||||
// ZslYlNRWIOUU7i6o
|
||||
export default model<ILocation>('Location', locationSchema);
|
||||
|
||||
export interface ILocation extends Document {
|
||||
idFlad: string;
|
||||
latitude : number;
|
||||
longitude: number;
|
||||
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { Schema, model } from 'mongoose';
|
||||
|
||||
const notificationSchema = new Schema({
|
||||
type: {type: String, required: true},
|
||||
content: {type: String, required: true}
|
||||
});
|
||||
|
||||
export default {Notification: model("nofitication", notificationSchema)}
|
@ -1,11 +0,0 @@
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export default interface IUser extends Document {
|
||||
email: string;
|
||||
name: string;
|
||||
password: string;
|
||||
idFlad : string;
|
||||
idSpotify : string;
|
||||
|
||||
isValidPassword(password: string): Promise<Error | boolean>;
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
// 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<IUser>({
|
||||
// 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<IUser>('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<IUser>('User', userSchema);
|
||||
// export const User: Model<IUser> = model('User', userSchema);
|
@ -0,0 +1,12 @@
|
||||
import App from "./app";
|
||||
import SpotifyController from "./controllers/spotifyController";
|
||||
import UserController from "./controllers/userController";
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config();
|
||||
const app = new App(
|
||||
[new SpotifyController(), new UserController()],
|
||||
Number(process.env.PORT)
|
||||
);
|
||||
|
||||
app.listen();
|
@ -1,45 +0,0 @@
|
||||
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<Response | void> {
|
||||
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;
|
@ -1,8 +0,0 @@
|
||||
// export const loggerOptions: expressWinston.LoggerOptions = {
|
||||
// transports: [new winston.transports.Console()],
|
||||
// format: winston.format.combine(
|
||||
// winston.format.json(),
|
||||
// winston.format.prettyPrint(),
|
||||
// winston.format.colorize({ all: true })
|
||||
// ),
|
||||
// };
|
@ -1,14 +1,11 @@
|
||||
import Joi from 'joi';
|
||||
|
||||
const register = Joi.object({
|
||||
name: Joi.string().max(30).required(),
|
||||
|
||||
name: Joi.string().max(30).required().regex(/^\w+$/)
|
||||
.message("Name should only contain alphanumeric characters (letters, numbers, and underscores)"),
|
||||
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
|
||||
idSpotify: Joi.string(),
|
||||
idFlad : Joi.string(),
|
||||
tokenSpotify: Joi.string().required()
|
||||
});
|
||||
|
||||
const login = Joi.object({
|
@ -0,0 +1,45 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import Token from '../models/Token';
|
||||
import UserSchema from '../database/UserSchema';
|
||||
import token from '../services/TokenService';
|
||||
import HttpException from '../exception/HttpException';
|
||||
|
||||
async function authMiddleware(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<Response | void> {
|
||||
const bearer = req.headers.authorization;
|
||||
|
||||
if (!bearer || !bearer.startsWith('Bearer ')) {
|
||||
return next(new HttpException(401, 'Unauthorized'));
|
||||
}
|
||||
|
||||
const accessToken = bearer.split('Bearer')[1].trim();
|
||||
try {
|
||||
const payload: Token | jwt.JsonWebTokenError = await token.verifyToken(
|
||||
accessToken
|
||||
);
|
||||
|
||||
if (payload instanceof jwt.JsonWebTokenError) {
|
||||
return next(new HttpException(401, 'Unauthorized'));
|
||||
}
|
||||
|
||||
const user = await UserSchema.findById(payload.id)
|
||||
.select('-password')
|
||||
.exec();
|
||||
|
||||
if (!user) {
|
||||
return next(new HttpException(401, 'Unauthorized'));
|
||||
}
|
||||
|
||||
req.user = user;
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
return next(new HttpException(401, 'Unauthorized'));
|
||||
}
|
||||
}
|
||||
|
||||
export default authMiddleware;
|
@ -1,5 +0,0 @@
|
||||
export default interface IUser{
|
||||
name: string;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
export interface Position {
|
||||
/**
|
||||
* Creation timestamp for coords
|
||||
*/
|
||||
timestamp: number;
|
||||
/**
|
||||
* The GPS coordinates along with the accuracy of the data
|
||||
*/
|
||||
coords: {
|
||||
/**
|
||||
* Latitude in decimal degrees
|
||||
*/
|
||||
latitude: number;
|
||||
/**
|
||||
* longitude in decimal degrees
|
||||
*/
|
||||
longitude: number;
|
||||
};
|
||||
}
|
||||
export class PlacePosition implements Position {
|
||||
timestamp: number;
|
||||
coords: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
constructor(timestamp: number,latitude : number ,longitude: number){
|
||||
this.timestamp = timestamp;
|
||||
this.coords = {latitude, longitude};
|
||||
}
|
||||
}
|
||||
export class UserLocation {
|
||||
uuid: string;
|
||||
latitude : number;
|
||||
longitude: number;
|
||||
currentMusicId : string;
|
||||
constructor(uuid: string, latitude: number, longitude: number, currentMusicId : string) {
|
||||
this.uuid = uuid;
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.currentMusicId = currentMusicId;
|
||||
}
|
||||
}
|
||||
|
||||
export class Place{
|
||||
position: Position;
|
||||
address: Address;
|
||||
constructor(address: Address,position: Position){
|
||||
this.position = position;
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type Address = {
|
||||
street : string;
|
||||
city : string;
|
||||
state : string;
|
||||
zip : string;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
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<jwt.VerifyErrors | IToken> => {
|
||||
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 };
|
@ -0,0 +1,24 @@
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export class UserLocation {
|
||||
_id: string;
|
||||
userId: string;
|
||||
musicId: string;
|
||||
distance: number;
|
||||
date: Date;
|
||||
constructor(id: string, userId: string, musicId: string, distance: number, date: Date) {
|
||||
this._id = id;
|
||||
this.userId = userId;
|
||||
this.musicId = musicId;
|
||||
this.distance = distance;
|
||||
this.date = date;
|
||||
}
|
||||
}
|
||||
|
||||
export class Location extends Document {
|
||||
userId: string;
|
||||
musicId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
updatedAt: Date;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface IMusic {
|
||||
musicId: string;
|
||||
userId: string;
|
||||
date: Date;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface IPerson {
|
||||
_id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
interface IToken extends Object {
|
||||
export default interface Token extends Object {
|
||||
id: Schema.Types.ObjectId;
|
||||
expiresIn: number;
|
||||
}
|
||||
|
||||
export default IToken;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { Document } from 'mongoose';
|
||||
import { IMusic } from './Music';
|
||||
|
||||
export default interface User extends Document {
|
||||
idSpotify: string;
|
||||
tokenSpotify: string;
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
isValidPassword(password: string): Promise<Error | boolean>;
|
||||
image: string;
|
||||
musics_likes: IMusic[];
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
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 UserController()],
|
||||
Number(8080)
|
||||
// Number(process.env.PORT)
|
||||
|
||||
);
|
||||
|
||||
app.listen();
|
||||
|
||||
|
||||
|
@ -1,139 +0,0 @@
|
||||
// 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 locationCollection = LocationSchema;
|
||||
// private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A";
|
||||
public async getNearUser(idFlad : string, latitude : number, longitude : number, currentMusicId: string)
|
||||
{
|
||||
await this.locationCollection.findOneAndUpdate(
|
||||
{ idFlad },
|
||||
{ idFlad, latitude, longitude, currentMusicId },
|
||||
{ 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.idFlad,doc.latitude,doc.longitude, doc.currentMusicId));
|
||||
console.log(doc.idFlad, '=>', doc);
|
||||
});
|
||||
// missing the curent music
|
||||
let listUser: {userid: string, music : string}[] = [];
|
||||
dbUsersList.forEach(user => {
|
||||
console.log(user);
|
||||
const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude);
|
||||
console.log(user.uuid,dist);
|
||||
if (dist <= 100) {
|
||||
|
||||
listUser.push({userid : user.uuid, music : user.currentMusicId});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
return listUser;
|
||||
// $listUser[] = {userID,idMusic};
|
||||
|
||||
}
|
||||
|
||||
public getCenter (points: Position[]) {
|
||||
if (Array.isArray(points) === false || points.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const numberOfPoints = points.length;
|
||||
|
||||
const sum = points.reduce(
|
||||
(acc, point) => {
|
||||
const pointLat = this.toRad(point.coords.latitude);
|
||||
const pointLon = this.toRad(point.coords.longitude);
|
||||
return {
|
||||
X: acc.X + Math.cos(pointLat) * Math.cos(pointLon),
|
||||
Y: acc.Y + Math.cos(pointLat) * Math.sin(pointLon),
|
||||
Z: acc.Z + Math.sin(pointLat),
|
||||
};
|
||||
},
|
||||
{ X: 0, Y: 0, Z: 0 }
|
||||
);
|
||||
|
||||
const X = sum.X / numberOfPoints;
|
||||
const Y = sum.Y / numberOfPoints;
|
||||
const Z = sum.Z / numberOfPoints;
|
||||
|
||||
return {
|
||||
longitude: this.toDeg(Math.atan2(Y, X)),
|
||||
latitude: this.toDeg(Math.atan2(Z, Math.sqrt(X * X + Y * Y))),
|
||||
};
|
||||
};
|
||||
|
||||
public toRad = (value: number) => (value * Math.PI) / 180;
|
||||
public toDeg = (value: number) => (value * 180) / Math.PI;
|
||||
|
||||
// sa c'est un utils du coup mettre dans une calss utils
|
||||
// resulta en km
|
||||
private distanceBetween (lat1 : number, lon1 : number, lat2: number, lon2 : number) : number {
|
||||
if ((lat1 == lat2) && (lon1 == lon2)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
var radlat1 = Math.PI * lat1/180;
|
||||
var radlat2 = Math.PI * lat2/180;
|
||||
var theta = lon1-lon2;
|
||||
var radtheta = Math.PI * theta/180;
|
||||
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
|
||||
|
||||
if (dist > 1) {
|
||||
dist = 1;
|
||||
}
|
||||
|
||||
dist = Math.acos(dist);
|
||||
dist = dist * 180/Math.PI;
|
||||
dist = dist * 60 * 1.1515;
|
||||
dist = dist * 1.609344;
|
||||
|
||||
return dist;
|
||||
}
|
||||
}
|
||||
private distanceBetweenPosition(first : Position, second : Position) : number {
|
||||
return this.distanceBetween (first.coords.latitude, first.coords.longitude, second.coords.latitude, second.coords.longitude)
|
||||
}
|
||||
|
||||
// give a array of position sorted by distance and return the first
|
||||
private findNearest(main : Position, list : Position[]){
|
||||
this.orderByDistance(main, list)[0]
|
||||
}
|
||||
|
||||
//distanceFn: DistanceFn = getDistance est param sa serrait cool de lui passer un fonction
|
||||
private orderByDistance (mainPos: Position,coords: Position[]){
|
||||
return coords
|
||||
.slice()
|
||||
.sort((a, b) => this.distanceBetweenPosition(mainPos, a) - this.distanceBetweenPosition(mainPos, b));
|
||||
};
|
||||
|
||||
// getCenter(coords)
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default LocationService;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,62 +0,0 @@
|
||||
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<string | Error> {
|
||||
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<string | Error> {
|
||||
// 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 });
|
||||
console.log(user?._id);
|
||||
// const user = await this.user.findById(idFlad);
|
||||
|
||||
if (user === undefined || user === null) {
|
||||
console.log("Could")
|
||||
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');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default UserService;
|
@ -0,0 +1,66 @@
|
||||
import { UserLocation } from '../models/Location';
|
||||
import LocationSchema from "../database/LocationSchema";
|
||||
|
||||
class LocationService {
|
||||
private location = LocationSchema;
|
||||
public async getNearUser(userId: string, musicId: string, latitude: number, longitude: number) {
|
||||
await this.location.findOneAndUpdate(
|
||||
{ userId },
|
||||
{ userId, musicId, latitude, longitude },
|
||||
{ upsert: true }
|
||||
);
|
||||
|
||||
const snapshot = await this.location.find({ userId: { $ne: userId } });
|
||||
if (!snapshot.length) {
|
||||
console.log('No matching documents.');
|
||||
return;
|
||||
}
|
||||
|
||||
let usersLocation: UserLocation[] = [];
|
||||
|
||||
snapshot.forEach(location => {
|
||||
const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude);
|
||||
if (distance <= 1000) {
|
||||
usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance + 0.1 / 200) * 200, location.updatedAt));
|
||||
}
|
||||
});
|
||||
return { data: usersLocation };
|
||||
}
|
||||
|
||||
private distanceBetween(lat1: number, lon1: number, lat2: number, lon2: number): number {
|
||||
if ((lat1 == lat2) && (lon1 == lon2)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
const radlat1 = Math.PI * lat1 / 180;
|
||||
const radlat2 = Math.PI * lat2 / 180;
|
||||
const theta = lon1 - lon2;
|
||||
const radtheta = Math.PI * theta / 180;
|
||||
let dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
|
||||
|
||||
if (dist > 1) {
|
||||
dist = 1;
|
||||
}
|
||||
|
||||
dist = Math.acos(dist);
|
||||
dist = dist * 180 / Math.PI;
|
||||
dist = dist * 60 * 1.1515;
|
||||
dist = dist * 1.609344 * 1000;
|
||||
|
||||
return dist;
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(
|
||||
id: string
|
||||
): Promise<void | Error> {
|
||||
try {
|
||||
await this.location.findByIdAndRemove(id);
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default LocationService;
|
@ -0,0 +1,26 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import User from '../models/User';
|
||||
import Token from '../models/Token';
|
||||
|
||||
export const createToken = (user: User): string => {
|
||||
return jwt.sign({ id: user._id }, process.env.SECRET_JWT as jwt.Secret, {
|
||||
expiresIn: '15d',
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyToken = async (
|
||||
token: string
|
||||
): Promise<jwt.VerifyErrors | Token> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.SECRET_JWT as jwt.Secret,
|
||||
(err, payload) => {
|
||||
if (err) return reject(err);
|
||||
resolve(payload as Token);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default { createToken, verifyToken };
|
@ -0,0 +1,175 @@
|
||||
import { IMusic } from "../models/Music";
|
||||
import UserSchema from "../database/UserSchema";
|
||||
import token from "./TokenService";
|
||||
import { IPerson } from "../models/Person";
|
||||
import mongoose from "mongoose";
|
||||
|
||||
class UserService {
|
||||
private user = UserSchema;
|
||||
|
||||
public async register(
|
||||
name: string,
|
||||
email: string,
|
||||
password: string,
|
||||
idSpotify: string,
|
||||
tokenSpotify: string,
|
||||
image: string
|
||||
): Promise<string | Error> {
|
||||
try {
|
||||
const user = await this.user.create({
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
tokenSpotify,
|
||||
idSpotify,
|
||||
image
|
||||
});
|
||||
return token.createToken(user);
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async login(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<string | Error> {
|
||||
const user = await this.user.findOne({ email });
|
||||
if (!user) {
|
||||
throw new Error('Wrong credentials given');
|
||||
}
|
||||
if (await user.isValidPassword(password)) {
|
||||
return token.createToken(user);
|
||||
} else {
|
||||
throw new Error('Wrong credentials given');
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(
|
||||
id: string
|
||||
): Promise<void | Error> {
|
||||
try {
|
||||
await this.user.findByIdAndRemove(id);
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async getUsers(
|
||||
ids: string[]
|
||||
): Promise<IPerson[] | Error> {
|
||||
try {
|
||||
const validIds = ids.filter(id => mongoose.Types.ObjectId.isValid(id));
|
||||
|
||||
if (validIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return await this.user.find({ _id: { $in: validIds } })
|
||||
.select('_id name image')
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async addMusic(userId: string, music: IMusic): Promise<string | Error> {
|
||||
try {
|
||||
return await this.user.findByIdAndUpdate(userId, {
|
||||
$push: { musics_likes: music },
|
||||
});
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteMusic(userId: string, musicId: string): Promise<boolean | Error> {
|
||||
try {
|
||||
const userOld = await this.user.findById(userId);
|
||||
const userNew = await this.user.findByIdAndUpdate(userId, {
|
||||
$pull: { musics_likes: { _id: musicId } },
|
||||
}, { new: true });
|
||||
|
||||
if (userOld.musics_likes.length === userNew.musics_likes.length) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async getMusics(userId: string): Promise<IMusic[] | Error> {
|
||||
try {
|
||||
const user = await this.user.findById(userId);
|
||||
return user?.musics_likes || [];
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async setName(userId: string, newName: string): Promise<void | Error> {
|
||||
try {
|
||||
await this.user.findByIdAndUpdate(
|
||||
userId,
|
||||
{ name: newName }
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async setEmail(userId: string, newEmail: string): Promise<void | Error> {
|
||||
try {
|
||||
await this.user.findByIdAndUpdate(
|
||||
userId,
|
||||
{ email: newEmail }
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async setSpotify(userId: string, tokenSpotify: string, idSpotify: string, image: string): Promise<void | Error> {
|
||||
try {
|
||||
await this.user.findByIdAndUpdate(
|
||||
userId,
|
||||
{
|
||||
tokenSpotify: tokenSpotify,
|
||||
idSpotify: idSpotify,
|
||||
image: image
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async setImage(userId: string, newImage: string): Promise<void | Error> {
|
||||
try {
|
||||
await this.user.findByIdAndUpdate(
|
||||
userId,
|
||||
{ image: newImage }
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async setPassword(userId: string, oldPassword: string, newPassword: string): Promise<void | Error> {
|
||||
try {
|
||||
|
||||
const user = await this.user.findById(userId);
|
||||
|
||||
if (await user.isValidPassword(oldPassword)) {
|
||||
user.password = newPassword;
|
||||
await user.save();
|
||||
} else {
|
||||
throw new Error('Old password does not match.');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UserService;
|
@ -0,0 +1,34 @@
|
||||
import swaggerJsdoc from "swagger-jsdoc";
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: "3.0.1",
|
||||
info: {
|
||||
title: "FLAD API",
|
||||
version: "1.0.0",
|
||||
description:
|
||||
"This is the Express API for the Flad project.",
|
||||
contact: {
|
||||
name: "Flad Dev",
|
||||
url: "code",
|
||||
email: "fladdevpro@gmail.com",
|
||||
},
|
||||
},
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
}
|
||||
}
|
||||
},
|
||||
security: [{
|
||||
bearerAuth: ["read"]
|
||||
}]
|
||||
},
|
||||
apis: ["./dist/**/*.js"],
|
||||
};
|
||||
|
||||
|
||||
export const specs = swaggerJsdoc(options);
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es6",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["node_modules/*"]
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
@ -1,25 +1,13 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
import store from './redux/store';
|
||||
import AuthNavigation from './navigation/AuthNavigation';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import SpotifyService from './services/spotify/spotify.service';
|
||||
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
|
||||
export default function App() {
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<AuthNavigation/>
|
||||
<AuthNavigation />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
mainSafeArea: {
|
||||
flex: 1,
|
||||
backgroundColor: "#141414",
|
||||
}
|
||||
});
|
||||
export const theService = new SpotifyService('BQC0rAGJvxTt4-24P-nda6qP9iXYCql2eApnUAoEbZZkKemJ11cU3Nx-I_tKVX0FwEgFbIbSIuaVvxOapRVJq2z1Htyy3XQ5jIYNsrhrnp3KTCfppamAjxgDTf6khBrNGTxe6CNKBsMhc5IRnphey5Td2zJPvGMwnFFfMQdCtVAVsCNK7kPKlCAaf_kRMAoPn30Qk4RD45XmwtZIwQg7X0J4beGuHSiBf0MRjhsnFEW89GxVm8YuIVwgrDbF3izfPR0AlqS4IMJT5m4pEA72lYEwp1JnSDVsafILzmksaqG-11H3WAsWIENrOIu_j7qNgbvYwmUWXOrYmeWBkQ');
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
export default class Artist {
|
||||
private id: string;
|
||||
private name: string;
|
||||
private url: string; // Image.source
|
||||
|
||||
constructor(id: string, name: string, url: string) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import SpotifyService from "../services/spotify/spotify.service";
|
||||
|
||||
class Manager {
|
||||
|
||||
// injection de dépences
|
||||
spotifyService = new SpotifyService();
|
||||
userService = new userService();
|
||||
|
||||
// spotify methods
|
||||
apiAuthorization(url: string) {
|
||||
this.spotifyService.apiAuthorization(url);
|
||||
}
|
||||
|
||||
getCompleteMusic = async (id: string): Promise<Music> => {
|
||||
// Map info = await spotifyService.getTrackInfo(id);
|
||||
// return Music(id, info['name'], info['artist'], info['cover']);
|
||||
}
|
||||
|
||||
removeFromPlaylist(id: string) {
|
||||
this.spotifyService.removeFromPlaylist(id);
|
||||
}
|
||||
|
||||
|
||||
addToPlaylist(id: string) {
|
||||
this.spotifyService.addToPlaylist(id);
|
||||
}
|
||||
|
||||
playTrack(id: string) {
|
||||
this.spotifyService.playTrack(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Manager;
|
@ -1,14 +0,0 @@
|
||||
// export default class Music {
|
||||
// private id : string;
|
||||
// private name : string;
|
||||
// private artist : string;
|
||||
// private linkCover : string; // Image.source
|
||||
|
||||
// constructor(id : string, name : string, artist : string, linkCover : string){
|
||||
// this.id = id;
|
||||
// this.name = name;
|
||||
// this.artist = artist;
|
||||
// this.linkCover = linkCover;
|
||||
// }
|
||||
|
||||
// }
|
@ -1,22 +0,0 @@
|
||||
import Music from "./Music";
|
||||
|
||||
export class Spot {
|
||||
private _userId: string;
|
||||
private _music: Music;
|
||||
constructor(userId: string, music: Music) {
|
||||
this._userId = userId;
|
||||
this._music = music;
|
||||
}
|
||||
get userSpotifyId(): string {
|
||||
return this._userId;
|
||||
}
|
||||
set userSpotifyId(value: string) {
|
||||
this._userId = value;
|
||||
}
|
||||
get music(): Music {
|
||||
return this._music;
|
||||
}
|
||||
set music(value: Music) {
|
||||
this._music = value;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
class TokenSpotify {
|
||||
_accessToken: string;
|
||||
_refreshToken: string;
|
||||
late DateTime _tokenEnd;
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
// import { useState } from "react";
|
||||
// import SpotifyService from "../services/spotify/spotify.service";
|
||||
// import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
|
||||
|
||||
// class StubManager{
|
||||
|
||||
// // injection de dépences
|
||||
// spotifyService = new SpotifyService();
|
||||
// // userService = new userService();
|
||||
|
||||
// private currentUser = useState(null);
|
||||
// private discovery = {
|
||||
// authorizationEndpoint: 'https://accounts.spotify.com/authorize',
|
||||
// tokenEndpoint: 'https://accounts.spotify.com/api/token',
|
||||
// };
|
||||
// constructor() {
|
||||
// }
|
||||
|
||||
// // spotify methods
|
||||
// apiAuthorization(url : string){
|
||||
// const [, response, promptAsync] = useAuthRequest(
|
||||
// {
|
||||
// clientId: '1f1e34e4b6ba48b388469dba80202b10',
|
||||
// scopes: ['user-read-email', 'playlist-modify-public'],
|
||||
// // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
|
||||
// // this must be set to false
|
||||
// usePKCE: false,
|
||||
// redirectUri: makeRedirectUri({
|
||||
// scheme: 'flad'
|
||||
// }),
|
||||
// },
|
||||
// this.discovery
|
||||
// );
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// getCompleteMusic = async (id : string) :Promise<Music> =>{
|
||||
// // Map info = await spotifyService.getTrackInfo(id);
|
||||
// // return Music(id, info['name'], info['artist'], info['cover']);
|
||||
// }
|
||||
|
||||
// removeFromPlaylist(id : string) {
|
||||
// this.spotifyService.removeFromPlaylist(id);
|
||||
// }
|
||||
|
||||
|
||||
// addToPlaylist(id : string) {
|
||||
// this.spotifyService.addToPlaylist(id);
|
||||
// }
|
||||
|
||||
// playTrack(id : string) {
|
||||
// this.spotifyService.playTrack(id);
|
||||
// }
|
||||
// ////////////////////////////////////////////
|
||||
|
||||
// // private readonly getTaskById: RequestHandler = async (
|
||||
// // req: Request,
|
||||
// // res: Response,
|
||||
// // next: NextFunction
|
||||
// // ): Promise<Response | void> => {
|
||||
// // try {
|
||||
// // const id = req.params.taskId;
|
||||
// // const userId = req.params.userId;
|
||||
|
||||
// // const data = await this.Taskservice.getTaskById(id, userId);
|
||||
// // res.status(201).send(data);
|
||||
|
||||
// // }
|
||||
// // catch(error){
|
||||
// // next(new HttpException(400, 'Cannot create post'));
|
||||
// // }
|
||||
|
||||
// // }
|
||||
|
||||
|
||||
// // private delete = async (
|
||||
// // req: Request,
|
||||
// // res: Response,
|
||||
// // next: NextFunction
|
||||
// // ): Promise<Response | void> => {
|
||||
// // try {
|
||||
// // const id = req.params.taskId;
|
||||
// // const userId = req.params.userId;
|
||||
// // await this.Taskservice.DeleteTask(id, userId);
|
||||
// // return res.status(200).send({ status: "Success", msg: "Data Removed" });
|
||||
// // } catch (error) {
|
||||
// // next(new HttpException(400, 'Cannot create post'));
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// // private updateTask = async (
|
||||
// // req: Request,
|
||||
// // res: Response,
|
||||
// // next: NextFunction
|
||||
// // ): Promise<Response | void> => {
|
||||
// // try {
|
||||
|
||||
// // const taskId = req.params.taskId;
|
||||
// // const userId = req.params.userId;
|
||||
// // const reqBody:CreateTaskReqBody = Object.assign({}, req.body);
|
||||
|
||||
// // const updatedTask = await this.Taskservice.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'));
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// }
|
||||
|
||||
// export default StubManager;
|
@ -1,43 +0,0 @@
|
||||
export class User {
|
||||
//attributes from DAFL
|
||||
private _idFlad: string;
|
||||
private _idSpotify: string;
|
||||
private _email: string;
|
||||
private _createdAt: Date;
|
||||
private _name: string;
|
||||
public image: string = require('../assets/images/jul.png');
|
||||
|
||||
//constructors
|
||||
constructor(idFlad: string, idSpotify: string, email: string, createdAt: Date, name: string, image: string) {
|
||||
this._name = name;
|
||||
this._idFlad = idFlad;
|
||||
this._idSpotify = idSpotify;
|
||||
this._createdAt = createdAt;
|
||||
this._email = email;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
get idFlad(): string {
|
||||
return this._idFlad;
|
||||
}
|
||||
get idSpotify(): string {
|
||||
return this._idSpotify;
|
||||
}
|
||||
get email(): string {
|
||||
return this._email;
|
||||
}
|
||||
get createAt(): Date {
|
||||
return this._createdAt;
|
||||
}
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return new User('', '', '', new Date(), '', require('../assets/images/jul.png'));
|
||||
}
|
||||
|
||||
toString() {
|
||||
return 'User : ' + this.idFlad + ', ' + this.name + ', ' + this.idSpotify;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import Music from "../Music";
|
||||
|
||||
export default class MusicFactory {
|
||||
static mapFromSpotifyTrack(jsonMusic :any ): Music {
|
||||
const music = new Music(
|
||||
jsonMusic.id,
|
||||
jsonMusic.name,
|
||||
"",
|
||||
jsonMusic.album.images[0].url,
|
||||
jsonMusic.preview_url
|
||||
);
|
||||
return music;
|
||||
}
|
||||
static mapFromSpotifyTrackSmpified(jsonMusic :any ): Music {
|
||||
const music = new Music(
|
||||
jsonMusic.id,
|
||||
jsonMusic.name,
|
||||
"",
|
||||
jsonMusic.album.images[0].url,
|
||||
jsonMusic.preview_url
|
||||
);
|
||||
return music;
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { User } from "../User";
|
||||
|
||||
export class UserFactory {
|
||||
|
||||
public static JsonToModel( jsonUser :any ) : User{
|
||||
return new User(jsonUser.idFlad, jsonUser.idSpotify, jsonUser.email, jsonUser.createdAt, jsonUser.name, jsonUser.imageUrl);
|
||||
}
|
||||
public static uptade( jsonUser :any ) : User{
|
||||
return new User(jsonUser.idFlad, jsonUser.idSpotify, jsonUser.email, jsonUser.createdAt, jsonUser.name, jsonUser.imageUrl);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export const GraphicalCharterDark = {
|
||||
"body": "#141414",
|
||||
"Text": "white",
|
||||
"Card": "#232123",
|
||||
"Line": "#403F3F"
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export const GraphicalCharterLight = {
|
||||
"body": "#f2f2f6",
|
||||
"Text": "black",
|
||||
"Card": "#fff",
|
||||
"Line": "#e2e2e3"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
const Icons = {
|
||||
discovery: require('./images/icon_discovery.png'),
|
||||
like: require('./images/icon_like.png'),
|
||||
dislike: require('./images/icon_dislike.png'),
|
||||
bookmark : require('./images/icon_bookmark.svg'),
|
||||
share : require('./images/Vector.png'),
|
||||
}
|
||||
|
||||
export default Icons;
|
Before Width: | Height: | Size: 1.9 MiB |
@ -1,5 +0,0 @@
|
||||
export const spotifyCredentials = {
|
||||
clientId: 'a5cb39302b6e4c64957de1df50742d71',
|
||||
clientSecret: 'e23db1cd77ee4b589ee99525e282b2e8',
|
||||
redirectUri: 'Your Redirect URI'
|
||||
}
|
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 757 B |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 835 B |
Before Width: | Height: | Size: 674 B |
Before Width: | Height: | Size: 677 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.5 KiB |
@ -1,12 +0,0 @@
|
||||
|
||||
const Icons = {
|
||||
discovery: require('./icon_discovery.png'),
|
||||
like: require('./icon_like.png'),
|
||||
dislike: require('./icon_dislike.png'),
|
||||
bookmark : require('./icon_bookmark.svg'),
|
||||
share : require('./Vector.png'),
|
||||
|
||||
// riveLike : require('./light_like.riv'),
|
||||
}
|
||||
|
||||
export default Icons;
|
Before Width: | Height: | Size: 329 KiB |
Before Width: | Height: | Size: 760 KiB |
Before Width: | Height: | Size: 766 KiB |
Before Width: | Height: | Size: 756 KiB |
Before Width: | Height: | Size: 778 KiB |
Before Width: | Height: | Size: 4.0 MiB |