Some changes in structure + add album

This commit is contained in:
Damien Broqua 2022-02-15 11:03:20 +01:00
parent 3ebdc9c06a
commit f08e70eb7c
36 changed files with 883 additions and 165 deletions

View file

@ -9,10 +9,14 @@ import MongoStore from "connect-mongo";
import config, { env, mongoDbUri, secret } from "./config";
import indexRouter from "./routes/index";
import indexRouter from "./routes";
import addAlbumRouter from "./routes/addAlbum";
import importRouterApiV1 from "./routes/api/v1";
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
// Mongoose schema init
require("./models/users");
require("./models/albums");
require("./libs/passport")(passport);
@ -60,7 +64,7 @@ if (["production"].indexOf(env) !== -1) {
app.use(passport.initialize());
app.use(passport.session());
app.set("views", path.join(__dirname, "views"));
app.set("views", path.join(__dirname, "../views"));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "../public")));
@ -69,45 +73,65 @@ app.use(
express.static(path.join(__dirname, "../node_modules/jquery/dist/"))
);
app.use(
"/libs/mdbootstrap",
express.static(path.join(__dirname, "../node_modules/mdbootstrap"))
"/libs/mdb-ui-kit/css",
express.static(path.join(__dirname, "../node_modules/mdb-ui-kit/css"))
);
app.use(
"/libs/mdb-ui-kit/js",
express.static(path.join(__dirname, "../node_modules/mdb-ui-kit/js"))
);
app.use(
"/libs/vue",
express.static(path.join(__dirname, "../node_modules/vue/dist"))
);
app.use(
"/libs/axios",
express.static(path.join(__dirname, "../node_modules/axios/dist"))
);
app.use("/", indexRouter);
app.use("/ajouter-un-album", addAlbumRouter);
app.use("/api/v1", importRouterApiV1);
app.use("/api/v1/albums", importAlbumRouterApiV1);
// Handle 404
app.use((req, res) => {
if (req.xhr) {
if (req.xhr || req.rawHeaders.indexOf("application/json") !== -1) {
res.status(404).send({ message: "404: Not found" });
} else {
res.status(404).render("error", {
res.status(404).render("index", {
page: { title: `404: Cette page n'existe pas.` },
errorCode: 404,
viewname: "error",
user: req.user || null,
config,
session: req.session || null,
flashInfo: null,
query: null,
params: null,
error: null,
});
}
});
// Handle 500
app.use((error, req, res, next) => {
if (req.xhr) {
res.status(error.errorCode || 500).send({ message: error.message });
if (req.xhr || req.rawHeaders.indexOf("application/json") !== -1) {
const { message, errorCode, date } = error;
res.status(error.errorCode || 500).send({ message, errorCode, date });
} else {
res.status(500);
res.render("error", {
res.status(error.errorCode || 500);
res.render("index", {
page: { title: "500: Oups… le serveur a crashé !", error },
errorCode: error.errorCode || 500,
viewname: "error",
user: req.user || null,
config,
session: req.session || null,
flashInfo: null,
query: null,
params: null,
error: null,
});
next();

View file

@ -3,4 +3,5 @@ module.exports = {
port: parseInt(process.env.PORT || "3001", 10),
mongoDbUri: process.env.MONGODB_URI || "mongodb://nodecdtheque-db/cdtheque",
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
discogsToken: process.env.DISCOGS_TOKEN,
};

View file

@ -1,3 +1,25 @@
/* eslint-disable import/prefer-default-export */
import { Client as Discogs } from "disconnect";
import { discogsToken } from "../config";
export const getBaseUrl = (req) => `${req.protocol}://${req.get("host")}`;
export const searchSong = async (q) => {
const dis = new Discogs({ userToken: discogsToken }).database();
const res = await dis.search({
q,
type: "release",
});
return res;
};
export const getAlbumDetails = async (id) => {
const dis = new Discogs({ userToken: discogsToken }).database();
const res = await dis.getRelease(id);
return res;
};

21
src/libs/error.js Normal file
View file

@ -0,0 +1,21 @@
/**
* Classe permettant la gestion des erreurs personilisées
*/
class ErrorEvent extends Error {
/**
* @param {Number} errorCode
* @param {Mixed} ...params
*/
constructor(errorCode, ...params) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ErrorEvent);
}
this.errorCode = parseInt(errorCode, 10);
this.date = new Date();
}
}
export default ErrorEvent;

View file

@ -1,3 +1,39 @@
/**
* Fonction permettant de formater une réponse basée sur la méthode utilisée
* @param {Object} req
* @param {Object} res
* @param {Object} data
*
* @return {Object}
*/
export const sendResponse = (req, res, data) => {
let status = 200;
const path = req.route.path.split("/");
switch (req.method) {
case "GET":
// INFO: On regarde de quel type de get il s'agit (liste ou item)
if (path[path.length - 1].indexOf(":") === 0) {
// INFO: Cas d'un item
status = !data ? 404 : 200;
} else {
// INFO: Cas d'une liste
status =
data.rows === undefined || data.rows.length > 0 ? 200 : 204;
}
return res.status(status).json(data).end();
case "PATCH":
return res.status(200).json(data).end();
case "DELETE":
return res.status(200).json(data).end();
case "POST":
return res.status(201).json(data).end();
default:
return res.status(500).json({ message: "Not implemented" });
}
};
export default (res, page) => {
res.status(200).render("index", page.render());
};

View file

@ -1,6 +1,7 @@
/* eslint-disable func-names */
const mongoose = require("mongoose");
const LocalStrategy = require("passport-local").Strategy;
const { BasicStrategy } = require("passport-http");
const Users = mongoose.model("Users");
@ -36,4 +37,22 @@ module.exports = function (passport) {
}
)
);
passport.use(
"basic",
new BasicStrategy((email, password, done) => {
Users.findOne({ email })
.then((user) => {
if (!user || !user.validPassword(password)) {
return done(
null,
false,
"Oops! Identifiants incorrects"
);
}
return done(null, user);
})
.catch(done);
})
);
};

85
src/middleware/Albums.js Normal file
View file

@ -0,0 +1,85 @@
import moment from "moment";
import Pages from "./Pages";
import { getAlbumDetails } from "../helpers";
import AlbumsModel from "../models/albums";
/**
* Classe permettant la gestion des albums d'un utilisateur
*/
class Albums extends Pages {
async getFormAddOne() {
const data = await getAlbumDetails(this.req.params.discogsId);
const {
id, // Integer
year, // - Integer
uri, // String
artists, // - Array<Object>
artists_sort, // String
labels, // - Array<Object>
series, // Array
companies, // - Array<Object>
formats, // - Array<Object>
title, // - String
country, // - String
released, // - Date
notes, // - String
identifiers, // - Array<Object>
videos, // - Array<Object>
genres, // - Array<String>
styles, // - Array<String>
tracklist, // - Array<Object>
extraartists, // - Array<Object>
images, // - Array<Object
thumb, // - String
} = data;
this.pageContent.page.values = "test";
this.setPageContent("values", {
id,
year,
uri,
artists,
artists_sort,
labels,
series,
companies,
formats,
title,
country,
released,
notes,
identifiers,
videos,
genres,
styles,
tracklist,
extraartists,
images,
thumb,
});
return true;
}
static async postAddOne(req) {
const { body, user } = req;
const data = {
...body,
discogsId: body.id,
User: user._id,
};
data.released = moment(data.released.replace("-00", "-01"));
delete data.id;
const album = new AlbumsModel(data);
return album.save();
}
}
export default Albums;

View file

@ -34,6 +34,10 @@ class Pages {
}
}
setPageContent(field, value) {
this.pageContent.page[field] = value;
}
/**
* Rendu de la page
* @return {Object}
@ -46,7 +50,7 @@ class Pages {
this.pageContent.params = this.req.params;
this.pageContent.user = this.user;
this.pageContent.config = config;
this.pageContent.getBaseUrl = getBaseUrl();
this.pageContent.getBaseUrl = getBaseUrl(this.req);
if (this.req.session.flash && this.req.session.flash.error) {
// eslint-disable-next-line prefer-destructuring

36
src/models/albums.js Normal file
View file

@ -0,0 +1,36 @@
import mongoose from "mongoose";
const { Schema } = mongoose;
const AlbumSchema = new mongoose.Schema(
{
User: {
type: Schema.Types.ObjectId,
ref: "Users",
},
discogsId: Number,
year: Number,
released: Date,
uri: String,
artists: Array,
artists_sort: String,
labels: Array,
series: Array,
companies: Array,
formats: Array,
title: String,
country: String,
notes: String,
identifiers: Array,
videos: Array,
genres: Array,
styles: Array,
tracklist: Array,
extraartists: Array,
images: Array,
thumb: String,
},
{ timestamps: true }
);
export default mongoose.model("Albums", AlbumSchema);

35
src/routes/addAlbum.js Normal file
View file

@ -0,0 +1,35 @@
import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import Pages from "../middleware/Albums";
import render from "../libs/format";
// eslint-disable-next-line new-cap
const router = express.Router();
router.route("/").get(ensureLoggedIn("/connexion"), (req, res, next) => {
try {
const page = new Pages(req, "ajouter-un-album/search");
render(res, page);
} catch (err) {
next(err);
}
});
router
.route("/:discogsId")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const page = new Pages(req, "ajouter-un-album/form");
await page.getFormAddOne();
render(res, page);
} catch (err) {
next(err);
}
});
export default router;

View file

@ -0,0 +1,20 @@
import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import { sendResponse } from "../../../libs/format";
import Albums from "../../../middleware/Albums";
// eslint-disable-next-line new-cap
const router = express.Router();
router.route("/").post(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const data = await Albums.postAddOne(req);
sendResponse(req, res, data);
} catch (err) {
next(err);
}
});
export default router;

View file

@ -0,0 +1,22 @@
import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import { sendResponse } from "../../../libs/format";
import { searchSong } from "../../../helpers";
// eslint-disable-next-line new-cap
const router = express.Router();
router
.route("/search")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const data = await searchSong(req.query.q);
sendResponse(req, res, data);
} catch (err) {
next(err);
}
});
export default router;

View file

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<%- include('partials/head', {page: page, user: user}); %>
<body class="error">
<%- include('partials/header'); %>
<main class="mt-4">
<div class="container">
<section class="px-md-5 mx-md-5 dark-grey-text mb-4">
<h1><%= page.title %></h1>
<% if ( errorCode && errorCode === 404 ) { %>
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
<% } %>
<p class="lead">
<%= page.error %>
</p>
</section>
</div>
</main>
<%- include('partials/footer', {page: page, user: user, blog: null}); %>
</body>
</html>

View file

@ -1,27 +0,0 @@
<!doctype html>
<html lang="fr">
<%- include('partials/head'); %>
<body>
<%- include('partials/header'); %>
<% if ( page.failureFlash ) {%>
<div class="alert alert-danger" role="alert">
<%= page.failureFlash %>
</div>
<% } %>
<%
if (error && error.length > 0) {
for( let i = 0 ; i < error.length ; i += 1 ) {
%>
<div class="alert alert-danger" role="alert">
<%= error %>
</div>
<%
}
}
%>
<%- include(viewname) %>
<%- include('partials/footer'); %>
</body>
</html>

View file

@ -1,24 +0,0 @@
<div class="container">
<div class="d-flex justify-content-center">
<div class="p-2">
<form class="text-center border border-light p-5" method="POST">
<img class="mb-4" src="/img/logo.png" alt="DarKou">
<p class="h4 mb-4">Connexion</p>
<div class="md-form">
<input type="email" id="email" name="email" class="form-control" required>
<label for="email">Adresse e-mail</label>
</div>
<div class="md-form">
<input type="password" id="password" name="password" class="form-control" required>
<label for="password">Mot de passe</label>
</div>
<button class="btn btn-primary btn-block my-4" type="submit">Connexion</button>
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
</form>
</div>
</div>
</div>

View file

@ -1,29 +0,0 @@
<div class="container">
<div class="d-flex justify-content-center">
<div class="p-2">
<form class="text-center border border-light p-5" method="POST">
<img class="mb-4" src="/img/logo.png" alt="DarKou">
<p class="h4 mb-4">Inscription</p>
<div class="md-form">
<input type="text" id="username" name="username" class="form-control" required>
<label for="username">Nom d'utilisateur</label>
</div>
<div class="md-form">
<input type="email" id="email" name="email" class="form-control" required>
<label for="email">Adresse e-mail</label>
</div>
<div class="md-form">
<input type="password" id="password" name="password" class="form-control" required>
<label for="password">Mot de passe</label>
</div>
<button class="btn btn-primary btn-block my-4" type="submit">Inscription</button>
<p>Déjà inscrit ? <a href="/connexion">Connectez-vous</a></p>
</form>
</div>
</div>
</div>

View file

@ -1,5 +0,0 @@
<script type="text/javascript" src="/libs/mdbootstrap/js/jquery.min.js"></script>
<script type="text/javascript" src="/libs/mdbootstrap/js/popper.min.js"></script>
<script type="text/javascript" src="/libs/mdbootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/libs/mdbootstrap/js/mdb.min.js"></script>
<script type="text/javascript" src="/js/main.js"></script>

View file

@ -1,17 +0,0 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><% if (page.title) { %><%= page.title %> <% } else { %> DarKou - Ma CDThèque <% } %></title>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap">
<link rel="stylesheet" href="/libs/mdbootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/libs/mdbootstrap/css/mdb.min.css">
<link rel="stylesheet" href="/libs/mdbootstrap/css/style.css">
<link rel="stylesheet" href="/css/main.css" />
</head>

View file

@ -1,24 +0,0 @@
<nav class="navbar navbar-expand-md navbar-dark primary-color sticky-top">
<a class="navbar-brand" href="/">CDThèque</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<% if ( user ) { %>
<div class="navbar-collapse collapse w-100 order-1 dual-collapse2">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="/upload">Ajouter une image</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Mon compte</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="/gallery">Mes images</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/se-deconnecter">Déconnexion</a>
</div>
</li>
</ul>
</div>
<% } %>
</nav>