update and error handling

This commit is contained in:
root 2020-07-18 10:43:13 +00:00
parent fa6bc5f3f3
commit 1bb080f7f1
12 changed files with 203 additions and 30 deletions

View file

@ -67,7 +67,7 @@ export default class MediaService {
// @ts-ignore // @ts-ignore
contentType: file.contentType, contentType: file.contentType,
// @ts-ignore // @ts-ignore
location: file.location, location: file.location.replace('http://', 'https://'),
// @ts-ignore // @ts-ignore
size: file.size, size: file.size,
// @ts-ignore // @ts-ignore

View file

@ -14,6 +14,7 @@ import PublicController from './controllers/PublicController'
import cors from 'cors' import cors from 'cors'
import twig from 'twig' import twig from 'twig'
import EmailService from './EmailService' import EmailService from './EmailService'
import ErrorController from './controllers/ErrorController'
dotenv.config({ dotenv.config({
path: __dirname + '/../.env' path: __dirname + '/../.env'
@ -42,6 +43,7 @@ let main = async () => {
console.log('> App: Connected to mongodb') console.log('> App: Connected to mongodb')
}) })
app.set("twig options", { app.set("twig options", {
allow_async: true, allow_async: true,
strict_variables: false strict_variables: false
@ -49,11 +51,6 @@ let main = async () => {
app.use(cors()) app.use(cors())
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use((err: any, req: express.Request, res: express.Response, next: express.RequestHandler) => {
console.error(err.stack)
res.status(res.statusCode).json({ success: false, error: err.stack })
})
app.get('/', PublicController.home) app.get('/', PublicController.home)
app.get('/association/:slug', PublicController.organization) app.get('/association/:slug', PublicController.organization)
app.get('/a-propos', PublicController.about) app.get('/a-propos', PublicController.about)
@ -111,6 +108,16 @@ let main = async () => {
app.use(express.static(path.resolve('./static'))) app.use(express.static(path.resolve('./static')))
app.get('/500', ErrorController.internalError)
app.use((err: any, req: express.Request, res: express.Response, next: express.RequestHandler) => {
console.error(err.stack)
//res.status(res.statusCode).json({ success: false, error: err.stack })
ErrorController.handle(500, 'Erreur interne', 'Ouups je sais pas quoi dire', '💥', err.stack)(req, res)
})
app.use(ErrorController.notFoundHandler())
app.listen(port, host, () => { app.listen(port, host, () => {
console.log(`> App: API listening on ${host}:${port}`) console.log(`> App: API listening on ${host}:${port}`)
}) })

View file

@ -0,0 +1,23 @@
import * as express from 'express'
export default class ErrorController {
static handle(code: number, title: string, subTitle: string, emoji: string, details = '') {
return (req: express.Request, res: express.Response) => {
res.status(code).render('error.twig', {
title,
subTitle,
emoji,
layout: -1,
details
})
}
}
static notFoundHandler() {
return ErrorController.handle(404, 'Page introuvable', 'Mais où peut donc se trouver cette page ?', '🔍 🕵️')
}
static internalError(req: express.Request, res: express.Response) {
console.log(res.locals.blabla.blabla)
}
}

View file

@ -8,6 +8,7 @@ import fs from 'fs'
import { IconService, IconInterface } from '../IconService' import { IconService, IconInterface } from '../IconService'
import IORedis from 'ioredis' import IORedis from 'ioredis'
import Tag from '../models/Tag' import Tag from '../models/Tag'
import ErrorController from './ErrorController'
export default class PublicController { export default class PublicController {
@ -43,14 +44,14 @@ export default class PublicController {
static async organization(req: express.Request, res: express.Response) { static async organization(req: express.Request, res: express.Response) {
Organization.find({ slug: req.params.slug }).then(data => { Organization.find({ slug: req.params.slug }).then(data => {
if (data.length === 0) { if (data.length === 0) {
return res.status(404).render('not-found.twig') return ErrorController.notFoundHandler()(req, res)
} else { } else {
const org = data[0] const org = data[0]
let version = org.get('publishedVersion') let version = org.get('publishedVersion')
if (req.query.version === 'proposed') { if (req.query.version === 'proposed') {
version = org.get('proposedVersion') version = org.get('proposedVersion')
} else if (org.get('publishedAt') === undefined || org.get('publishedAt') === null) { } else if (org.get('publishedAt') === undefined || org.get('publishedAt') === null) {
return res.status(404).render('not-found.twig') return ErrorController.notFoundHandler()(req, res)
} }
if (version.contacts !== null && version.contacts !== undefined) { if (version.contacts !== null && version.contacts !== undefined) {
if (typeof version.contacts.address === 'string') { if (typeof version.contacts.address === 'string') {
@ -74,6 +75,12 @@ export default class PublicController {
version.contacts.phoneSplit = phoneSplit version.contacts.phoneSplit = phoneSplit
} }
} }
if (Array.isArray(version.gallery)) {
version.gallery = version.gallery.slice(0, 5).map((media: any) => {
media.isVideo = media.contentType.indexOf('video/') !== -1
return media
})
}
res.render('organization.twig', { res.render('organization.twig', {
layout: 'standalone', layout: 'standalone',
data: version data: version

View file

@ -74,14 +74,14 @@ const OrganizationVersion = {
priceLabel: { type: String, required: true }, priceLabel: { type: String, required: true },
description: { type: String } description: { type: String }
}], }],
tag: { type: Tag } tag: [Tag]
} }
const Organization = new Schema({ const Organization = new Schema({
adminName: { type: String, required: true }, adminName: { type: String, required: true },
email: { type: String, required: true }, email: { type: String, required: true },
token: { type: String, required: true }, token: { type: String, required: true },
slug: { type: String, required: true }, slug: [{ type: String }], // aliases system
validationState: { validationState: {
type: AllowedString, type: AllowedString,
required: true, required: true,

35
static/assets/error.css Normal file
View file

@ -0,0 +1,35 @@
.error-container {
width: 100%;
margin-top: 4em;
}
.error {
width: 60%;
margin: 0 auto;
text-align: center;
color: #2c3e50;
}
.error-title {
font-family: 'Roboto Slab', serif;
}
.error-icons {
font-size: 5em;
margin-bottom: .25em;
}
.expert {
text-align: left;
padding: 1em;
background-color: black;
color: rgba(0, 255, 0);
border: 2px solid gray;
border-radius: 3px;
}
@media (max-width: 1350px) {
.error {
width: 100%;
}
}

View file

@ -6,6 +6,7 @@
} }
.header-container { .header-container {
position: relative;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
@ -15,6 +16,9 @@
} }
.header-menu { .header-menu {
position: absolute;
right: 0;
bottom: 0;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
} }
@ -246,6 +250,9 @@
.card-container { .card-container {
width: 100%; width: 100%;
} }
.header-left {
padding-bottom: 1em;
}
} }
@media (max-width: 1000px) { @media (max-width: 1000px) {
@ -261,7 +268,8 @@
} }
.header-menu { .header-menu {
justify-content: center; justify-content: center;
margin-top: 1em; position: relative;
margin-top: 0;
} }
.header-content { .header-content {
padding: 0; padding: 0;

View file

@ -24,21 +24,61 @@ document.querySelectorAll('.schedule-category').forEach(node => {
* Gallery modal to view media in large * Gallery modal to view media in large
*/ */
let mediaModal = document.querySelector('#media-modal') let mediaModal = document.querySelector('#media-modal')
let mediaModalImage = document.querySelector('#media-modal-image') let mediaModalContent = document.querySelector('#media-modal-content')
// let mediaModalImage = document.querySelector('#media-modal img')
// let mediaModalVideo = document.querySelector('#media-modal video')
// let mediaModalSource = document.querySelector('#media-modal video source')
let openModal = url => { // function disableScroll() {
// // Get the current page scroll position
// scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
// // if any scroll is attempted, set this to the previous value
// window.onscroll = function() {
// window.scrollTo(scrollLeft, scrollTop);
// };
// }
// function enableScroll() {
// window.onscroll = function() {};
// }
let openModal = (url, isVideo) => {
mediaModal.style.visibility = 'visible' mediaModal.style.visibility = 'visible'
mediaModal.style.opacity = 1 mediaModal.style.opacity = 1
mediaModalContent.innerHTML = ""
let attr = document.createAttribute('src') let attr = document.createAttribute('src')
attr.value = url attr.value = url
mediaModalImage.attributes.setNamedItem(attr) let el = null
document.body.style.height = "100vh" if (isVideo) {
document.body.style.overflow = "hidden" el = document.createElement('video')
el.setAttribute('controls', '')
el.setAttribute('autoplay', '')
el.setAttribute('name', 'media')
let source = document.createElement('source')
source.setAttribute('src', url)
source.setAttribute('type', 'video/mp4')
el.appendChild(source)
} else {
el = document.createElement('img')
el.attributes.setNamedItem(attr)
}
mediaModalContent.appendChild(el)
//document.body.style.height = '100vh'
document.body.style.overflow = 'hidden'
document.body.style.touchAction = 'none'
} }
let closeModal = () => { let closeModal = () => {
mediaModal.style.visibility = 'hidden' mediaModal.style.visibility = 'hidden'
mediaModal.style.opacity = 0 mediaModal.style.opacity = 0
document.body.style.overflow = "initial" document.body.style.overflow = 'initial'
document.body.style.height = "initial" document.body.style.height = 'initial'
let video = document.querySelector('#media-modal video')
if (video !== null) {
video.pause()
}
} }

View file

@ -118,7 +118,6 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 3.5em;
position: absolute; position: absolute;
left: 0; left: 0;
@ -131,12 +130,13 @@
position: relative; position: relative;
} }
.media-overlay i { .media-overlay svg {
width: 3.5em;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
} }
.media-overlay i:hover { .media-overlay svg:hover {
transform: scale(1.2); transform: scale(1.2);
} }
@ -194,6 +194,7 @@
} */ } */
.media { .media {
cursor: pointer;
height: 100%; height: 100%;
border-radius: 4px; border-radius: 4px;
background-size: cover; background-size: cover;
@ -204,7 +205,7 @@
* Media modal * Media modal
*/ */
.media-modal-container { .media-modal-container {
position: absolute; position: fixed;
width: 100%; width: 100%;
height: 100vh; height: 100vh;
z-index: 99; z-index: 99;
@ -226,11 +227,15 @@
width: 50%; width: 50%;
} }
.media-modal img { .media-modal .media-modal-content {
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
} }
.media-modal img, .media-modal video {
width: 100%;
}
.media-close { .media-close {
position: absolute; position: absolute;
width: 1.5em; width: 1.5em;
@ -559,6 +564,9 @@ section {
.schedule-category-days-container { .schedule-category-days-container {
margin-right: 25em; margin-right: 25em;
} }
.media-modal {
width: 70%;
}
} }
@ -637,6 +645,10 @@ section {
grid-column: 1 / span 2; grid-column: 1 / span 2;
} }
.media-modal {
width: 80%;
}
.schedule-category-header { .schedule-category-header {
padding-left: 1em; padding-left: 1em;
padding-right: 1em; padding-right: 1em;
@ -689,4 +701,7 @@ section {
.return-title { .return-title {
display: none; display: none;
} }
.media-modal {
width: 90%;
}
} }

View file

@ -1,3 +1,8 @@
<!--
Site développé par Matthieu Bessat - lefuturiste -
Portfolio: https://lefuturiste.fr
Github: https://github.com/lefuturiste
-->
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>

26
views/error.twig Normal file
View file

@ -0,0 +1,26 @@
{% extends "./base.twig" %}
{% block title %}{{ title }}{% endblock %}
{% block head %}
<link rel="stylesheet" href="/assets/error.css" />
{% endblock %}
{% block content %}
<div class="error-container">
<div class="error">
<div class="error-icons">
{{ emoji }}
</div>
<hr>
<h1 class="error-title">{{ title }}</h1>
<h4 class="error-sub-title">{{ subTitle }}</h4>
<hr>
{% if details|length > 0 %}
<p>Pour les experts voici les détails :</p>
<div class="expert">
<pre>
{{ details }}
</pre>
</div>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -38,18 +38,25 @@
{% if data.gallery|length > 0 %} {% if data.gallery|length > 0 %}
<div class="media-mosaic mosaic-{{ data.gallery|length }}"> <div class="media-mosaic mosaic-{{ data.gallery|length }}">
{% for media in data.gallery %} {% for media in data.gallery %}
<div class="media-container" onclick="openModal('{{ media.location }}')"> <div class="media-container" onclick="openModal('{{ media.location }}', {{ media.isVideo }})">
<div class="media" style="background-image: url({{ media.location }})"></div> <div
class="media {% if media.isVideo %}media-video-bg{% endif %}"
{% if not media.isVideo %}style="background-image: url({{ media.location }})"{% endif %}></div>
{% if media.isVideo %}
<div class="media-overlay">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm115.7 272l-176 101c-15.8 8.8-35.7-2.5-35.7-21V152c0-18.4 19.8-29.8 35.7-21l176 107c16.4 9.2 16.4 32.9 0 42z"></path>
</svg>
</div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
{# <div class="media-container media-1-2">
<div class="media" style="background-image: url({{ data.gallery[2].location }})"></div>
<div class="media-overlay"><i class="fas fa-play-circle"></i></div>
</div> #}
</div> </div>
<div class="media-modal-container" id="media-modal"> <div class="media-modal-container" id="media-modal">
<div class="media-modal"> <div class="media-modal">
<img src="" id="media-modal-image" /> <div class="media-modal-content" id="media-modal-content">
</div>
<div class="media-close" onclick="closeModal()"> <div class="media-close" onclick="closeModal()">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path> <path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path>