Some changes in structure + add album
This commit is contained in:
parent
3ebdc9c06a
commit
f08e70eb7c
36 changed files with 883 additions and 165 deletions
|
@ -16,7 +16,13 @@ module.exports = {
|
||||||
'no-underscore-dangle': [
|
'no-underscore-dangle': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
allow: ['_id'],
|
allow: ['_id', 'artists_sort'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'camelcase': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allow: ['artists_sort',]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -120,3 +120,4 @@ dist
|
||||||
|
|
||||||
dist
|
dist
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
public/css
|
||||||
|
|
31
README.md
31
README.md
|
@ -1,2 +1,33 @@
|
||||||
# nodecdtheque
|
# nodecdtheque
|
||||||
|
|
||||||
|
NodeCDThèque (non temporaire faute de mieux haha) est une application Web permettant de lister votre collection de CD ou vinyles.
|
||||||
|
|
||||||
|
## Prérequis
|
||||||
|
|
||||||
|
Pour fonctionner vous devez avoir `docker` d'installer sur votre serveur.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Préparer la terre
|
||||||
|
|
||||||
|
Après avoir cloné le projet il faudra créer un fichier d'environnement nommé `.env` et situé à la racine de projet.
|
||||||
|
|
||||||
|
Contenu du fichier :
|
||||||
|
```
|
||||||
|
NODE_ENV=development
|
||||||
|
DISCOGS_TOKEN=***
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour obtenir un token Discogs vous devez simplement vous inscrire sur [Discogs](https://www.discogs.com). Une fois connecté à votre espace Discogs vous pourrez obtenir un token en allant dans le sous-menu [Développeur](https://www.discogs.com/settings/developers).
|
||||||
|
|
||||||
|
### Semer
|
||||||
|
|
||||||
|
Vous pouvez maintenant simplement lancer la commande suivante afin de démarrer le projet en tant que service :
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pailler
|
||||||
|
|
||||||
|
Une fois le serveur démarré vous pourrez accéder à l'interface web via [http://127.0.0.1:3001](http://127.0.0.1:3001). (ou tout autre port si vous avez défini `PORT` dans le fichier `.env`).
|
||||||
|
|
|
@ -19,7 +19,8 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- nodecdtheque-db
|
- nodecdtheque-db
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: "development"
|
NODE_ENV: ${NODE_ENV}
|
||||||
|
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
|
||||||
networks:
|
networks:
|
||||||
- nodecdtheque
|
- nodecdtheque
|
||||||
nodecdtheque-db:
|
nodecdtheque-db:
|
||||||
|
|
15
package.json
15
package.json
|
@ -4,8 +4,9 @@
|
||||||
"description": "Simple application to manage your CD/Vinyl collection",
|
"description": "Simple application to manage your CD/Vinyl collection",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./dist/bin/www",
|
"start": "node ./dist/bin/www",
|
||||||
"dev": "npm-run-all build start",
|
"dev": "npm-run-all build sass start",
|
||||||
"watch": "nodemon -e js,ejs",
|
"watch": "nodemon",
|
||||||
|
"sass": "npx sass sass/*.scss public/css/main.css -s compressed",
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "babel ./src --out-dir dist --copy-files",
|
"build": "babel ./src --out-dir dist --copy-files",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
@ -42,20 +43,26 @@
|
||||||
"rimraf": "^3.0.2"
|
"rimraf": "^3.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.26.0",
|
||||||
"connect-ensure-login": "^0.1.1",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"debug": "^4.3.3",
|
"debug": "^4.3.3",
|
||||||
|
"disconnect": "^1.2.2",
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"mdbootstrap": "^4.20.0",
|
"mdb-ui-kit": "^3.10.2",
|
||||||
|
"moment": "^2.29.1",
|
||||||
"mongoose": "^6.2.1",
|
"mongoose": "^6.2.1",
|
||||||
"mongoose-unique-validator": "^3.0.0",
|
"mongoose-unique-validator": "^3.0.0",
|
||||||
"passport": "^0.5.2",
|
"passport": "^0.5.2",
|
||||||
"passport-local": "^1.0.0"
|
"passport-http": "^0.3.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"sass": "^1.49.7",
|
||||||
|
"vue": "^3.2.31"
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
"exec": "npm run dev",
|
"exec": "npm run dev",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
#toastr{visibility:hidden;min-width:250px;margin-left:-125px;background-color:#9c3030;color:#fff;text-align:left;border-radius:2px;padding:16px;position:fixed;z-index:1;right:30px;top:30px;font-size:17px}#toastr.show{visibility:visible;animation:toastrFadein .5s,toastrFadeout .5s 2.5s}@keyframes toastrFadein{from{top:0;opacity:0}to{top:30px;opacity:1}}@keyframes toastrFadeout{from{top:30px;opacity:1}to{top:0;opacity:0}}/*# sourceMappingURL=main.css.map */
|
42
sass/toast.scss
Normal file
42
sass/toast.scss
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#toastr {
|
||||||
|
visibility: hidden;
|
||||||
|
min-width: 250px;
|
||||||
|
margin-left: -125px;
|
||||||
|
background-color: rgb(156, 48, 48);
|
||||||
|
color: #fff;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 16px;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
right: 30px;
|
||||||
|
top: 30px;
|
||||||
|
font-size: 17px;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
visibility: visible;
|
||||||
|
animation: toastrFadein 0.5s, toastrFadeout 0.5s 2.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toastrFadein {
|
||||||
|
from {
|
||||||
|
top: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toastrFadeout {
|
||||||
|
from {
|
||||||
|
top: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
44
src/app.js
44
src/app.js
|
@ -9,10 +9,14 @@ import MongoStore from "connect-mongo";
|
||||||
|
|
||||||
import config, { env, mongoDbUri, secret } from "./config";
|
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
|
// Mongoose schema init
|
||||||
require("./models/users");
|
require("./models/users");
|
||||||
|
require("./models/albums");
|
||||||
|
|
||||||
require("./libs/passport")(passport);
|
require("./libs/passport")(passport);
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ if (["production"].indexOf(env) !== -1) {
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
|
|
||||||
app.set("views", path.join(__dirname, "views"));
|
app.set("views", path.join(__dirname, "../views"));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
|
||||||
app.use(express.static(path.join(__dirname, "../public")));
|
app.use(express.static(path.join(__dirname, "../public")));
|
||||||
|
@ -69,45 +73,65 @@ app.use(
|
||||||
express.static(path.join(__dirname, "../node_modules/jquery/dist/"))
|
express.static(path.join(__dirname, "../node_modules/jquery/dist/"))
|
||||||
);
|
);
|
||||||
app.use(
|
app.use(
|
||||||
"/libs/mdbootstrap",
|
"/libs/mdb-ui-kit/css",
|
||||||
express.static(path.join(__dirname, "../node_modules/mdbootstrap"))
|
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("/", indexRouter);
|
||||||
|
app.use("/ajouter-un-album", addAlbumRouter);
|
||||||
|
app.use("/api/v1", importRouterApiV1);
|
||||||
|
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
||||||
|
|
||||||
// Handle 404
|
// Handle 404
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
if (req.xhr) {
|
if (req.xhr || req.rawHeaders.indexOf("application/json") !== -1) {
|
||||||
res.status(404).send({ message: "404: Not found" });
|
res.status(404).send({ message: "404: Not found" });
|
||||||
} else {
|
} else {
|
||||||
res.status(404).render("error", {
|
res.status(404).render("index", {
|
||||||
page: { title: `404: Cette page n'existe pas.` },
|
page: { title: `404: Cette page n'existe pas.` },
|
||||||
errorCode: 404,
|
errorCode: 404,
|
||||||
|
viewname: "error",
|
||||||
user: req.user || null,
|
user: req.user || null,
|
||||||
config,
|
config,
|
||||||
session: req.session || null,
|
session: req.session || null,
|
||||||
flashInfo: null,
|
flashInfo: null,
|
||||||
query: null,
|
query: null,
|
||||||
params: null,
|
params: null,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle 500
|
// Handle 500
|
||||||
app.use((error, req, res, next) => {
|
app.use((error, req, res, next) => {
|
||||||
if (req.xhr) {
|
if (req.xhr || req.rawHeaders.indexOf("application/json") !== -1) {
|
||||||
res.status(error.errorCode || 500).send({ message: error.message });
|
const { message, errorCode, date } = error;
|
||||||
|
res.status(error.errorCode || 500).send({ message, errorCode, date });
|
||||||
} else {
|
} else {
|
||||||
res.status(500);
|
res.status(error.errorCode || 500);
|
||||||
res.render("error", {
|
res.render("index", {
|
||||||
page: { title: "500: Oups… le serveur a crashé !", error },
|
page: { title: "500: Oups… le serveur a crashé !", error },
|
||||||
errorCode: error.errorCode || 500,
|
errorCode: error.errorCode || 500,
|
||||||
|
viewname: "error",
|
||||||
user: req.user || null,
|
user: req.user || null,
|
||||||
config,
|
config,
|
||||||
session: req.session || null,
|
session: req.session || null,
|
||||||
flashInfo: null,
|
flashInfo: null,
|
||||||
query: null,
|
query: null,
|
||||||
params: null,
|
params: null,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -3,4 +3,5 @@ module.exports = {
|
||||||
port: parseInt(process.env.PORT || "3001", 10),
|
port: parseInt(process.env.PORT || "3001", 10),
|
||||||
mongoDbUri: process.env.MONGODB_URI || "mongodb://nodecdtheque-db/cdtheque",
|
mongoDbUri: process.env.MONGODB_URI || "mongodb://nodecdtheque-db/cdtheque",
|
||||||
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
|
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
|
||||||
|
discogsToken: process.env.DISCOGS_TOKEN,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,25 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
/* 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 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
21
src/libs/error.js
Normal 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;
|
|
@ -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) => {
|
export default (res, page) => {
|
||||||
res.status(200).render("index", page.render());
|
res.status(200).render("index", page.render());
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* eslint-disable func-names */
|
/* eslint-disable func-names */
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const LocalStrategy = require("passport-local").Strategy;
|
const LocalStrategy = require("passport-local").Strategy;
|
||||||
|
const { BasicStrategy } = require("passport-http");
|
||||||
|
|
||||||
const Users = mongoose.model("Users");
|
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
85
src/middleware/Albums.js
Normal 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;
|
|
@ -34,6 +34,10 @@ class Pages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPageContent(field, value) {
|
||||||
|
this.pageContent.page[field] = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendu de la page
|
* Rendu de la page
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
@ -46,7 +50,7 @@ class Pages {
|
||||||
this.pageContent.params = this.req.params;
|
this.pageContent.params = this.req.params;
|
||||||
this.pageContent.user = this.user;
|
this.pageContent.user = this.user;
|
||||||
this.pageContent.config = config;
|
this.pageContent.config = config;
|
||||||
this.pageContent.getBaseUrl = getBaseUrl();
|
this.pageContent.getBaseUrl = getBaseUrl(this.req);
|
||||||
|
|
||||||
if (this.req.session.flash && this.req.session.flash.error) {
|
if (this.req.session.flash && this.req.session.flash.error) {
|
||||||
// eslint-disable-next-line prefer-destructuring
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
|
36
src/models/albums.js
Normal file
36
src/models/albums.js
Normal 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
35
src/routes/addAlbum.js
Normal 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;
|
20
src/routes/api/v1/albums.js
Normal file
20
src/routes/api/v1/albums.js
Normal 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;
|
22
src/routes/api/v1/index.js
Normal file
22
src/routes/api/v1/index.js
Normal 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;
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
11
views/error.ejs
Normal file
11
views/error.ejs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<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;" />
|
||||||
|
<% } %>
|
||||||
|
<pre>
|
||||||
|
<%= page.error %>
|
||||||
|
</pre>
|
||||||
|
</section>
|
||||||
|
</div>
|
37
views/index.ejs
Normal file
37
views/index.ejs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="fr">
|
||||||
|
<%- include('partials/head'); %>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<%- include('partials/header'); %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="toastr"></div>
|
||||||
|
|
||||||
|
<main class="mt-4 mb-5">
|
||||||
|
|
||||||
|
<% 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) %>
|
||||||
|
</main>
|
||||||
|
<footer class="bg-light text-lg-start">
|
||||||
|
<%- include('partials/footer'); %>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
144
views/pages/ajouter-un-album/form.ejs
Normal file
144
views/pages/ajouter-un-album/form.ejs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<div class="container-fluid" id="app">
|
||||||
|
<form class="bg-white rounded shadow-5-strong p-5" method="POST" @submit="add">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-sm-3 p-2 text-center">
|
||||||
|
<img src="<%= page.values.thumb %>" alt="Miniature" />
|
||||||
|
<hr />
|
||||||
|
<img v-for="image in album.images" :src="image.uri150" alt="Miniature" style="max-width: 60px;" />
|
||||||
|
<hr />
|
||||||
|
<ol>
|
||||||
|
<li v-for="track in album.tracklist">{{ track.title }} ({{track.duration}})</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-9 p-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-2">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="title" name="title" class="form-control" v-model="album.title" disabled />
|
||||||
|
<label class="form-label" for="artists">Titre</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4" v-for="artist in album.artists">
|
||||||
|
<input type="text" id="artists" name="artists" class="form-control" v-model="artist.name" disabled />
|
||||||
|
<label class="form-label" for="artists">Artiste</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2" v-for="genre in album.genres">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="genres" name="genres" class="form-control" v-model="genre" disabled />
|
||||||
|
<label class="form-label" for="company">Genre</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 p-2" v-for="style in album.styles">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="style" name="style" class="form-control" v-model="style" disabled />
|
||||||
|
<label class="form-label" for="company">Style</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="year" name="year" class="form-control" v-model="album.year" disabled />
|
||||||
|
<label class="form-label" for="year">Année</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="released" name="released" class="form-control" v-model="album.released" disabled />
|
||||||
|
<label class="form-label" for="released">Date de sortie</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="country" name="country" class="form-control" v-model="album.country" disabled />
|
||||||
|
<label class="form-label" for="country">Pays</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<ol>
|
||||||
|
<li v-for="identifier in album.identifiers">
|
||||||
|
{{identifier.value}} ({{identifier.type}})
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 p-2">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<textarea id="notes" id="notes" name="notes" class="form-control" v-model="album.notes" disabled></textarea>
|
||||||
|
<label class="form-label" for="country">Notes</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2" v-for="format in album.formats">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="format" name="format" class="form-control" v-model="format.name" disabled />
|
||||||
|
<label class="form-label" for="format">Format</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2" v-for="label in album.labels">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="label" name="label" class="form-control" v-model="label.name" disabled />
|
||||||
|
<label class="form-label" for="label">Label</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2" v-for="company in album.companies">
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="company" name="company" class="form-control" v-model="company.name" disabled />
|
||||||
|
<label class="form-label" for="company">Société</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<ol>
|
||||||
|
<li v-for="video in album.videos">
|
||||||
|
<a :href="video.uri" target="_blank">{{video.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="col-12 col-sm-6 p-2">
|
||||||
|
<ol>
|
||||||
|
<li v-for="extraartist in album.extraartists">
|
||||||
|
{{extraartist.name}} ({{extraartist.role}})
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Ajouter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const defaultValues = <%- JSON.stringify(page.values) %>;
|
||||||
|
|
||||||
|
Vue.createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
album: defaultValues,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
add(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
axios.post('/api/v1/albums', this.album)
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = '/ma-collection';
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('ERR:', err.response);
|
||||||
|
showToastr(err.response?.data?.message || "Impossible d'ajouter ce album pour le moment…");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).mount('#app')
|
||||||
|
</script>
|
||||||
|
|
105
views/pages/ajouter-un-album/search.ejs
Normal file
105
views/pages/ajouter-un-album/search.ejs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<div class="container-fluid" id="app">
|
||||||
|
<form @submit="search">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="form-outline">
|
||||||
|
<input v-model="q" type="search" id="q" class="form-control" />
|
||||||
|
<label class="form-label" for="q">Nom de l'album ou code barre</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<table class="table table-striped table-hover table-sm align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Pochette</th>
|
||||||
|
<th>Titre</th>
|
||||||
|
<th>Pays</th>
|
||||||
|
<th>Année</th>
|
||||||
|
<th>Format</th>
|
||||||
|
<th>Genres</th>
|
||||||
|
<th>Styles</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="item in items">
|
||||||
|
<td>
|
||||||
|
<img :src="item.thumb" :alt="item.title" style="max-width: 120px;"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a :href="'/ajouter-un-album/' + item.id">{{ item.title }}</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ item.year }}</td>
|
||||||
|
<td>{{ item.country }}</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li v-for="format in item.format">{{ format }}</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li v-for="genre in item.genre">{{ genre }}</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li v-for="style in item.style">{{ style }}</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
Vue.createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
q: '',
|
||||||
|
items: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
axios.get(`/api/v1/search?q=${this.q}`)
|
||||||
|
.then( response => {
|
||||||
|
const {
|
||||||
|
results,
|
||||||
|
} = response.data;
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let i = 0 ; i < results.length ; i += 1 ) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
thumb,
|
||||||
|
year,
|
||||||
|
country,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
style,
|
||||||
|
} = results[i];
|
||||||
|
items.push({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
thumb,
|
||||||
|
year,
|
||||||
|
country,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
style,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items = items;
|
||||||
|
})
|
||||||
|
.catch( err => {
|
||||||
|
console.log('err:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).mount('#app')
|
||||||
|
</script>
|
||||||
|
|
30
views/pages/connexion.ejs
Normal file
30
views/pages/connexion.ejs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-xl-5 col-md-8">
|
||||||
|
<form class="bg-white rounded shadow-5-strong p-5" method="POST">
|
||||||
|
<div class="text-center">
|
||||||
|
<img class="mb-4" src="/img/logo.png" alt="DarKou">
|
||||||
|
</div>
|
||||||
|
<h4>Connexion</h4>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="email" id="email" name="email" class="form-control" />
|
||||||
|
<label class="form-label" for="email">Adresse e-mail</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="password" id="password" name="password" class="form-control" />
|
||||||
|
<label class="form-label" for="password">Mot de passe</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col text-end">
|
||||||
|
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Connexion</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
35
views/pages/inscription.ejs
Normal file
35
views/pages/inscription.ejs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-xl-5 col-md-8">
|
||||||
|
<form class="bg-white rounded shadow-5-strong p-5" method="POST">
|
||||||
|
<div class="text-center">
|
||||||
|
<img class="mb-4" src="/img/logo.png" alt="DarKou">
|
||||||
|
</div>
|
||||||
|
<h4>Inscription</h4>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="text" id="username" name="username" class="form-control" />
|
||||||
|
<label class="form-label" for="username">Nom d'utilisateur</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="email" id="email" name="email" class="form-control" />
|
||||||
|
<label class="form-label" for="email">Adresse e-mail</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-outline mb-4">
|
||||||
|
<input type="password" id="password" name="password" class="form-control" />
|
||||||
|
<label class="form-label" for="password">Mot de passe</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col text-end">
|
||||||
|
<p>Déjà inscrit ? <a href="/connexion">Connectez-vous</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Inscription</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
2
views/partials/footer.ejs
Normal file
2
views/partials/footer.ejs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/libs/mdb-ui-kit/js/mdb.min.js"></script>
|
39
views/partials/head.ejs
Normal file
39
views/partials/head.ejs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<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
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
href="/libs/mdb-ui-kit/css/mdb.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<link href="/css/main.css" rel="stylesheet" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/libs/jquery/jquery.min.js"></script>
|
||||||
|
|
||||||
|
<script src="/libs/axios/axios.min.js"></script>
|
||||||
|
<script src="/libs/vue/vue.global.prod.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function showToastr(message) {
|
||||||
|
var x = document.getElementById("toastr");
|
||||||
|
if ( message ) {
|
||||||
|
x.innerHTML = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
x.className = "show";
|
||||||
|
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
49
views/partials/header.ejs
Normal file
49
views/partials/header.ejs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<button
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-mdb-toggle="collapse"
|
||||||
|
data-mdb-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<i class="fas fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<a class="navbar-brand mt-2 mt-lg-0" href="/">
|
||||||
|
Ma CDThèque
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if ( user ) { %>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<a class="text-reset me-3" href="/ajouter-un-album">
|
||||||
|
Ajouter un album
|
||||||
|
</a>
|
||||||
|
<div class="dropdown">
|
||||||
|
<a
|
||||||
|
class="dropdown-toggle d-flex align-items-center hidden-arrow"
|
||||||
|
href="#"
|
||||||
|
id="navbarDropdownMenuAvatar"
|
||||||
|
role="button"
|
||||||
|
data-mdb-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
Mon compte
|
||||||
|
</a>
|
||||||
|
<ul
|
||||||
|
class="dropdown-menu dropdown-menu-end"
|
||||||
|
aria-labelledby="navbarDropdownMenuAvatar"
|
||||||
|
>
|
||||||
|
<li><a class="dropdown-item" href="/ma-collection">Ma collection</a></li>
|
||||||
|
<li><hr class="dropdown-divider" /></li>
|
||||||
|
<li><a class="dropdown-item" href="/se-deconnecter">Déconnexion</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</nav>
|
Loading…
Reference in a new issue