You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
9.5 KiB
286 lines
9.5 KiB
import { Router, Request, Response, NextFunction } from 'express';
|
|
import IController from './interfaces/IController';
|
|
import HttpException from '../exception/HttpException';
|
|
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 {
|
|
this.router.post(
|
|
`${this.authPath}/register`,
|
|
validationMiddleware(validator.register),
|
|
this.register
|
|
);
|
|
this.router.post(
|
|
`${this.authPath}/login`,
|
|
validationMiddleware(validator.login),
|
|
this.login
|
|
);
|
|
this.router.get(`${this.path}`, authenticator, this.getUser);
|
|
this.router.delete(`${this.path}`, authenticator, this.deleteUser);
|
|
this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext);
|
|
this.router.delete(`${this.path}/musics/:id`, authenticator, this.deleteMusic);
|
|
this.router.post(`${this.path}/musics`, authenticator, this.addMusic);
|
|
this.router.get(`${this.path}/musics`, authenticator, this.getMusics);
|
|
this.router.put(`${this.path}/name`, authenticator, this.setName);
|
|
this.router.put(`${this.path}/email`, authenticator, this.setEmail);
|
|
|
|
}
|
|
|
|
private register = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(409, 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 => {
|
|
res.status(200).send({ data: req.user });
|
|
};
|
|
|
|
private deleteUser = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<Response | void> => {
|
|
try {
|
|
const { _id } = req.user;
|
|
await this.userService.delete(_id);
|
|
res.status(204).send();
|
|
} catch (error: any) {
|
|
next(new HttpException(404, error.message));
|
|
}
|
|
};
|
|
|
|
private getUserNext = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(400, 'Cannot create get netUser: ' + error.message));
|
|
}
|
|
}
|
|
|
|
private deleteMusic = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(404, error.message));
|
|
}
|
|
}
|
|
|
|
private addMusic = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<Response | void> => {
|
|
try {
|
|
const { _id } = req.user;
|
|
const { idMusic, idUser } = req.body;
|
|
if (!idMusic || !idUser) {
|
|
return res.status(400).json({ error: 'idMusic and idUser are required fields.' });
|
|
}
|
|
const music: IMusic = {
|
|
idMusic,
|
|
idUser,
|
|
date: new Date(),
|
|
};
|
|
await this.userService.addMusic(_id, music);
|
|
res.status(201).send({ music });
|
|
} catch (error: any) {
|
|
next(new HttpException(400, error.message));
|
|
}
|
|
}
|
|
|
|
private getMusics = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(400, error.message));
|
|
}
|
|
}
|
|
|
|
private setName = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(409, error.message));
|
|
}
|
|
}
|
|
|
|
private setEmail = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): 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) {
|
|
next(new HttpException(409, error.message));
|
|
}
|
|
}
|
|
}
|
|
export default UserController;
|
|
|
|
declare global {
|
|
namespace Express {
|
|
export interface Request {
|
|
user: User;
|
|
}
|
|
}
|
|
}
|