#35 - Je peux accéder à mon compte #44
13 changed files with 284 additions and 57 deletions
0
public/robots.txt
Normal file
0
public/robots.txt
Normal file
|
@ -11,4 +11,12 @@
|
||||||
.header {
|
.header {
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: $success-color;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -132,6 +132,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
background-color: var(--default-color);
|
||||||
|
|
||||||
.navbar-link {
|
.navbar-link {
|
||||||
background-color: var(--default-hl-color);
|
background-color: var(--default-hl-color);
|
||||||
color: rgba(0,0,0,.7);
|
color: rgba(0,0,0,.7);
|
||||||
|
@ -252,6 +254,13 @@
|
||||||
padding-bottom: .5rem;
|
padding-bottom: .5rem;
|
||||||
padding-top: .5rem;
|
padding-top: .5rem;
|
||||||
|
|
||||||
|
hr {
|
||||||
|
background-color: var(--font-color);
|
||||||
|
border: none;
|
||||||
|
height: 2px;
|
||||||
|
margin: .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-item {
|
.navbar-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
|
|
44
src/app.js
44
src/app.js
|
@ -15,6 +15,7 @@ import { isXhr } from "./helpers";
|
||||||
|
|
||||||
import indexRouter from "./routes";
|
import indexRouter from "./routes";
|
||||||
import maCollectionRouter from "./routes/ma-collection";
|
import maCollectionRouter from "./routes/ma-collection";
|
||||||
|
import monCompteRouter from "./routes/mon-compte";
|
||||||
import collectionRouter from "./routes/collection";
|
import collectionRouter from "./routes/collection";
|
||||||
|
|
||||||
import importJobsRouter from "./routes/jobs";
|
import importJobsRouter from "./routes/jobs";
|
||||||
|
@ -83,6 +84,7 @@ app.use(
|
||||||
);
|
);
|
||||||
|
|
||||||
app.use("/", indexRouter);
|
app.use("/", indexRouter);
|
||||||
|
app.use("/mon-compte", monCompteRouter);
|
||||||
app.use("/ma-collection", maCollectionRouter);
|
app.use("/ma-collection", maCollectionRouter);
|
||||||
app.use("/collection", collectionRouter);
|
app.use("/collection", collectionRouter);
|
||||||
app.use("/jobs", importJobsRouter);
|
app.use("/jobs", importJobsRouter);
|
||||||
|
@ -97,15 +99,22 @@ app.use((req, res) => {
|
||||||
} else {
|
} else {
|
||||||
res.status(404).render("index", {
|
res.status(404).render("index", {
|
||||||
page: { title: `404: Cette page n'existe pas.` },
|
page: { title: `404: Cette page n'existe pas.` },
|
||||||
errorCode: 404,
|
|
||||||
viewname: "error",
|
viewname: "error",
|
||||||
user: req.user || null,
|
|
||||||
config,
|
|
||||||
session: req.session || null,
|
session: req.session || null,
|
||||||
flashInfo: null,
|
flash: {
|
||||||
query: null,
|
info: req.flash("info"),
|
||||||
params: null,
|
error: [
|
||||||
error: null,
|
...req.flash("error"),
|
||||||
|
...(req.session?.flash?.error || []),
|
||||||
|
],
|
||||||
|
success: req.flash("success"),
|
||||||
|
},
|
||||||
|
query: req.query,
|
||||||
|
params: req.params,
|
||||||
|
user: req.user,
|
||||||
|
config,
|
||||||
|
getBaseUrl: null,
|
||||||
|
errorCode: 404,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -122,15 +131,22 @@ app.use((error, req, res, next) => {
|
||||||
title: error.title || "500: Oups… le serveur a crashé !",
|
title: error.title || "500: Oups… le serveur a crashé !",
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
errorCode: error.errorCode || 500,
|
|
||||||
viewname: "error",
|
viewname: "error",
|
||||||
user: req.user || null,
|
|
||||||
config,
|
|
||||||
session: req.session || null,
|
session: req.session || null,
|
||||||
flashInfo: null,
|
flash: {
|
||||||
query: null,
|
info: req.flash("info"),
|
||||||
params: null,
|
error: [
|
||||||
error: null,
|
...req.flash("error"),
|
||||||
|
...(req.session?.flash?.error || []),
|
||||||
|
],
|
||||||
|
success: req.flash("success"),
|
||||||
|
},
|
||||||
|
query: req.query,
|
||||||
|
params: req.params,
|
||||||
|
user: req.user,
|
||||||
|
config,
|
||||||
|
getBaseUrl: null,
|
||||||
|
errorCode: error.errorCode || 500,
|
||||||
});
|
});
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -98,7 +98,7 @@ class Albums extends Pages {
|
||||||
if (!this.req.user && !collectionUserId) {
|
if (!this.req.user && !collectionUserId) {
|
||||||
throw new ErrorEvent(
|
throw new ErrorEvent(
|
||||||
401,
|
401,
|
||||||
"Cette collection n'est pas publique",
|
"Collection",
|
||||||
"Cette collection n'est pas publique"
|
"Cette collection n'est pas publique"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ class Albums extends Pages {
|
||||||
) {
|
) {
|
||||||
throw new ErrorEvent(
|
throw new ErrorEvent(
|
||||||
401,
|
401,
|
||||||
"Cette collection n'est pas publique",
|
"Collection",
|
||||||
"Cette collection n'est pas publique"
|
"Cette collection n'est pas publique"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -233,6 +233,7 @@ class Albums extends Pages {
|
||||||
if (!user || !user.isPublicCollection) {
|
if (!user || !user.isPublicCollection) {
|
||||||
throw new ErrorEvent(
|
throw new ErrorEvent(
|
||||||
401,
|
401,
|
||||||
|
"Collection non partagée",
|
||||||
"Cet utilisateur ne souhaite pas partager sa collection"
|
"Cet utilisateur ne souhaite pas partager sa collection"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
|
|
||||||
import UsersModel from "../models/users";
|
import UsersModel from "../models/users";
|
||||||
|
import Pages from "./Pages";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe permettant la gestion de l'utilisateur connecté
|
* Classe permettant la gestion de l'utilisateur connecté
|
||||||
*/
|
*/
|
||||||
class Me {
|
class Me extends Pages {
|
||||||
constructor(req) {
|
|
||||||
this.req = req;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Méthode permettant de modifier le profil d'un utilisateur
|
* Méthode permettant de modifier le profil d'un utilisateur
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
@ -40,6 +37,33 @@ class Me {
|
||||||
|
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthode permettant de modifier le mot de passe d'un utilisateur
|
||||||
|
*/
|
||||||
|
async updatePassword() {
|
||||||
|
const { body } = this.req;
|
||||||
|
const { _id } = this.req.user;
|
||||||
|
|
||||||
|
const schema = Joi.object({
|
||||||
|
oldPassword: Joi.string().required(),
|
||||||
|
password: Joi.string().required(),
|
||||||
|
passwordConfirm: Joi.ref("password"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = await schema.validateAsync(body);
|
||||||
|
const user = await UsersModel.findById(_id);
|
||||||
|
|
||||||
|
if (!user.validPassword(value.oldPassword)) {
|
||||||
|
throw new Error("Votre ancien mot de passe n'est pas valide");
|
||||||
|
}
|
||||||
|
|
||||||
|
user.salt = value.password;
|
||||||
|
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
this.req.flash("success", "Profil correctement mis à jour");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Me;
|
export default Me;
|
||||||
|
|
|
@ -52,21 +52,20 @@ class Pages {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
this.pageContent.session = this.req.session;
|
this.pageContent.session = this.req.session;
|
||||||
this.pageContent.flashInfo = this.req.flash("info");
|
this.pageContent.flash = {
|
||||||
this.pageContent.error = this.req.flash("error") || null;
|
info: this.req.flash("info"),
|
||||||
|
error: [
|
||||||
|
...this.req.flash("error"),
|
||||||
|
...(this.req.session?.flash?.error || []),
|
||||||
|
],
|
||||||
|
success: this.req.flash("success"),
|
||||||
|
};
|
||||||
this.pageContent.query = this.req.query;
|
this.pageContent.query = this.req.query;
|
||||||
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.req);
|
this.pageContent.getBaseUrl = getBaseUrl(this.req);
|
||||||
|
|
||||||
if (this.req.session.flash && this.req.session.flash.error) {
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
this.pageContent.page.failureFlash =
|
|
||||||
this.req.session.flash.error[0];
|
|
||||||
this.req.session.flash = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.pageContent;
|
return this.pageContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/routes/mon-compte.js
Normal file
35
src/routes/mon-compte.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import express from "express";
|
||||||
|
import { ensureLoggedIn } from "connect-ensure-login";
|
||||||
|
|
||||||
|
import Me from "../middleware/Me";
|
||||||
|
|
||||||
|
import render from "../libs/format";
|
||||||
|
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router
|
||||||
|
.route("/")
|
||||||
|
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const page = new Me(req, "mon-compte/index");
|
||||||
|
|
||||||
|
page.setPageTitle("Mon compte");
|
||||||
|
|
||||||
|
render(res, page);
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.post(ensureLoggedIn("/connexion"), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const page = new Me(req, "mon-compte/index");
|
||||||
|
|
||||||
|
await page.updatePassword();
|
||||||
|
} catch (err) {
|
||||||
|
req.flash("error", err.toString());
|
||||||
|
}
|
||||||
|
res.redirect("/mon-compte");
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
|
@ -1,10 +1,8 @@
|
||||||
<main class="layout-maxed error">
|
<main class="layout-maxed error">
|
||||||
<h1><%= page.title %></h1>
|
<h1><%= page.title %></h1>
|
||||||
<% if ( errorCode && errorCode === 404 ) { %>
|
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
|
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
|
||||||
</p>
|
</p>
|
||||||
<% } %>
|
|
||||||
<% if ( process.env.NODE_ENV !== 'production' ) { %>
|
<% if ( process.env.NODE_ENV !== 'production' ) { %>
|
||||||
<div>
|
<div>
|
||||||
<pre><%= page.error %></pre>
|
<pre><%= page.error %></pre>
|
||||||
|
|
|
@ -83,6 +83,10 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown">
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="/mon-compte">
|
||||||
|
Mon compte
|
||||||
|
</a>
|
||||||
|
<hr />
|
||||||
<a class="navbar-item" href="/ma-collection">
|
<a class="navbar-item" href="/ma-collection">
|
||||||
Ma collection
|
Ma collection
|
||||||
</a>
|
</a>
|
||||||
|
@ -125,32 +129,50 @@
|
||||||
<span></span>
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if ( page.failureFlash || (error && error.length > 0 ) ) {%>
|
<%
|
||||||
|
if ( flash.error.length > 0 ) {
|
||||||
|
for ( let i = 0 ; i < flash.error.length ; i += 1 ) {
|
||||||
|
%>
|
||||||
<div class="flash">
|
<div class="flash">
|
||||||
<% if ( page.failureFlash ) {%>
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
Erreur
|
Erreur
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<%= page.failureFlash %>
|
<%= flash.error[i].replace('Error: ', '') %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
|
||||||
<%
|
<%
|
||||||
if (error && error.length > 0) {
|
}
|
||||||
for( let i = 0 ; i < error.length ; i += 1 ) {
|
}
|
||||||
|
if ( flash.info.length > 0 ) {
|
||||||
|
for ( let i = 0 ; i < flash.info.length ; i += 1 ) {
|
||||||
%>
|
%>
|
||||||
|
<div class="flash info">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
Erreur
|
Information
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<%= error %>
|
<%= flash.info[i] %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( flash.success.length > 0 ) {
|
||||||
|
for ( let i = 0 ; i < flash.success.length ; i += 1 ) {
|
||||||
|
%>
|
||||||
|
<div class="flash success">
|
||||||
|
<div class="header">
|
||||||
|
Succès
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<%= flash.success[i] %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%
|
<%
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<%- include(viewname) %>
|
<%- include(viewname) %>
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,22 @@
|
||||||
Ceci est une erreur
|
Ceci est une erreur
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flash info">
|
||||||
|
<div class="header">
|
||||||
|
Information
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
Ceci est une information
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flash success">
|
||||||
|
<div class="header">
|
||||||
|
Succès
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
Ceci est un succès
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<pre>
|
<pre>
|
||||||
<div class="flash">
|
<div class="flash">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
|
99
views/pages/mon-compte/index.ejs
Normal file
99
views/pages/mon-compte/index.ejs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<main class="layout-maxed collection" id="app">
|
||||||
|
<h1>
|
||||||
|
Mon compte
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
||||||
|
<form method="POST" action="/mon-compte" @submit="updateProfil">
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label for="email">Adresse e-mail</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="email"
|
||||||
|
id="email"
|
||||||
|
v-model="email"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="username">Nom d'utilisateur</label>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
v-model="username"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="oldPassword">Mot de passe actuel</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="oldPassword"
|
||||||
|
id="oldPassword"
|
||||||
|
required
|
||||||
|
placeholder="Saisisssez votre mot de passe actuel"
|
||||||
|
v-model="oldPassword"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="password">Nouveau mot de passe</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
id="password"
|
||||||
|
required
|
||||||
|
placeholder="Saisisssez votre nouveau mot de passe"
|
||||||
|
v-model="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="passwordConfirm">Nouveau mot de passe (confirmation)</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="passwordConfirm"
|
||||||
|
id="passwordConfirm"
|
||||||
|
required
|
||||||
|
placeholder="Confirmez votre nouveau mot de passe"
|
||||||
|
v-model="passwordConfirm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="button is-primary mt-10" :disabled="loading">
|
||||||
|
<span v-if="!loading">Mettre à jour</span>
|
||||||
|
<i class="icon-spin animate-spin" v-if="loading"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
email: '<%= user.email %>',
|
||||||
|
username: '<%= user.username %>',
|
||||||
|
oldPassword: '',
|
||||||
|
password: '',
|
||||||
|
passwordConfirm: '',
|
||||||
|
loading: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async updateProfil(event) {
|
||||||
|
// try {
|
||||||
|
// if ( this.password !== this.passwordConfirm ) {
|
||||||
|
// throw "La confirnation du mot de passe ne correspond pas";
|
||||||
|
// }
|
||||||
|
// } catch(err) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// showToastr(err);
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
Loading…
Reference in a new issue