From 1bb080f7f1372fc206f350c97b54b12b8d090b21 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 18 Jul 2020 10:43:13 +0000 Subject: [PATCH] update and error handling --- src/MediaService.ts | 2 +- src/app.ts | 17 ++++++--- src/controllers/ErrorController.ts | 23 ++++++++++++ src/controllers/PublicController.ts | 11 ++++-- src/models/Organization.ts | 4 +-- static/assets/error.css | 35 +++++++++++++++++++ static/assets/home.css | 10 +++++- static/assets/js/organization.js | 54 +++++++++++++++++++++++++---- static/assets/organization.css | 25 ++++++++++--- views/base.twig | 5 +++ views/error.twig | 26 ++++++++++++++ views/organization.twig | 21 +++++++---- 12 files changed, 203 insertions(+), 30 deletions(-) create mode 100644 src/controllers/ErrorController.ts create mode 100644 static/assets/error.css create mode 100644 views/error.twig diff --git a/src/MediaService.ts b/src/MediaService.ts index d6886ec..c55a4d1 100644 --- a/src/MediaService.ts +++ b/src/MediaService.ts @@ -67,7 +67,7 @@ export default class MediaService { // @ts-ignore contentType: file.contentType, // @ts-ignore - location: file.location, + location: file.location.replace('http://', 'https://'), // @ts-ignore size: file.size, // @ts-ignore diff --git a/src/app.ts b/src/app.ts index cf514c7..26f5573 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,6 +14,7 @@ import PublicController from './controllers/PublicController' import cors from 'cors' import twig from 'twig' import EmailService from './EmailService' +import ErrorController from './controllers/ErrorController' dotenv.config({ path: __dirname + '/../.env' @@ -42,6 +43,7 @@ let main = async () => { console.log('> App: Connected to mongodb') }) + app.set("twig options", { allow_async: true, strict_variables: false @@ -49,11 +51,6 @@ let main = async () => { app.use(cors()) 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('/association/:slug', PublicController.organization) app.get('/a-propos', PublicController.about) @@ -111,6 +108,16 @@ let main = async () => { 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, () => { console.log(`> App: API listening on ${host}:${port}`) }) diff --git a/src/controllers/ErrorController.ts b/src/controllers/ErrorController.ts new file mode 100644 index 0000000..1ecb0ae --- /dev/null +++ b/src/controllers/ErrorController.ts @@ -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) + } +} \ No newline at end of file diff --git a/src/controllers/PublicController.ts b/src/controllers/PublicController.ts index d763d00..96c69bb 100644 --- a/src/controllers/PublicController.ts +++ b/src/controllers/PublicController.ts @@ -8,6 +8,7 @@ import fs from 'fs' import { IconService, IconInterface } from '../IconService' import IORedis from 'ioredis' import Tag from '../models/Tag' +import ErrorController from './ErrorController' export default class PublicController { @@ -43,14 +44,14 @@ export default class PublicController { static async organization(req: express.Request, res: express.Response) { Organization.find({ slug: req.params.slug }).then(data => { if (data.length === 0) { - return res.status(404).render('not-found.twig') + return ErrorController.notFoundHandler()(req, res) } else { const org = data[0] let version = org.get('publishedVersion') if (req.query.version === 'proposed') { version = org.get('proposedVersion') } 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 (typeof version.contacts.address === 'string') { @@ -74,6 +75,12 @@ export default class PublicController { 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', { layout: 'standalone', data: version diff --git a/src/models/Organization.ts b/src/models/Organization.ts index 8cc9453..e4bc10e 100644 --- a/src/models/Organization.ts +++ b/src/models/Organization.ts @@ -74,14 +74,14 @@ const OrganizationVersion = { priceLabel: { type: String, required: true }, description: { type: String } }], - tag: { type: Tag } + tag: [Tag] } const Organization = new Schema({ adminName: { type: String, required: true }, email: { type: String, required: true }, token: { type: String, required: true }, - slug: { type: String, required: true }, + slug: [{ type: String }], // aliases system validationState: { type: AllowedString, required: true, diff --git a/static/assets/error.css b/static/assets/error.css new file mode 100644 index 0000000..de08b15 --- /dev/null +++ b/static/assets/error.css @@ -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%; + } +} diff --git a/static/assets/home.css b/static/assets/home.css index 235167c..3aaea36 100644 --- a/static/assets/home.css +++ b/static/assets/home.css @@ -6,6 +6,7 @@ } .header-container { + position: relative; display: flex; justify-content: space-between; } @@ -15,6 +16,9 @@ } .header-menu { + position: absolute; + right: 0; + bottom: 0; display: flex; align-items: flex-end; } @@ -246,6 +250,9 @@ .card-container { width: 100%; } + .header-left { + padding-bottom: 1em; + } } @media (max-width: 1000px) { @@ -261,7 +268,8 @@ } .header-menu { justify-content: center; - margin-top: 1em; + position: relative; + margin-top: 0; } .header-content { padding: 0; diff --git a/static/assets/js/organization.js b/static/assets/js/organization.js index 1d0ea5b..b567f08 100644 --- a/static/assets/js/organization.js +++ b/static/assets/js/organization.js @@ -24,21 +24,61 @@ document.querySelectorAll('.schedule-category').forEach(node => { * Gallery modal to view media in large */ 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.opacity = 1 + mediaModalContent.innerHTML = "" + let attr = document.createAttribute('src') attr.value = url - mediaModalImage.attributes.setNamedItem(attr) - document.body.style.height = "100vh" - document.body.style.overflow = "hidden" + let el = null + if (isVideo) { + 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 = () => { mediaModal.style.visibility = 'hidden' mediaModal.style.opacity = 0 - document.body.style.overflow = "initial" - document.body.style.height = "initial" + document.body.style.overflow = 'initial' + document.body.style.height = 'initial' + let video = document.querySelector('#media-modal video') + if (video !== null) { + video.pause() + } } diff --git a/static/assets/organization.css b/static/assets/organization.css index 18ed8fa..e423878 100644 --- a/static/assets/organization.css +++ b/static/assets/organization.css @@ -118,7 +118,6 @@ display: flex; justify-content: center; align-items: center; - font-size: 3.5em; position: absolute; left: 0; @@ -131,12 +130,13 @@ position: relative; } -.media-overlay i { +.media-overlay svg { + width: 3.5em; cursor: pointer; transition: all 0.2s; } -.media-overlay i:hover { +.media-overlay svg:hover { transform: scale(1.2); } @@ -194,6 +194,7 @@ } */ .media { + cursor: pointer; height: 100%; border-radius: 4px; background-size: cover; @@ -204,7 +205,7 @@ * Media modal */ .media-modal-container { - position: absolute; + position: fixed; width: 100%; height: 100vh; z-index: 99; @@ -226,11 +227,15 @@ width: 50%; } -.media-modal img { +.media-modal .media-modal-content { border-radius: 4px; width: 100%; } +.media-modal img, .media-modal video { + width: 100%; +} + .media-close { position: absolute; width: 1.5em; @@ -559,6 +564,9 @@ section { .schedule-category-days-container { margin-right: 25em; } + .media-modal { + width: 70%; + } } @@ -637,6 +645,10 @@ section { grid-column: 1 / span 2; } + .media-modal { + width: 80%; + } + .schedule-category-header { padding-left: 1em; padding-right: 1em; @@ -689,4 +701,7 @@ section { .return-title { display: none; } + .media-modal { + width: 90%; + } } \ No newline at end of file diff --git a/views/base.twig b/views/base.twig index 93828a4..19e57ab 100644 --- a/views/base.twig +++ b/views/base.twig @@ -1,3 +1,8 @@ + diff --git a/views/error.twig b/views/error.twig new file mode 100644 index 0000000..3f9dfc3 --- /dev/null +++ b/views/error.twig @@ -0,0 +1,26 @@ +{% extends "./base.twig" %} +{% block title %}{{ title }}{% endblock %} +{% block head %} + +{% endblock %} +{% block content %} +
+
+
+ {{ emoji }} +
+
+

{{ title }}

+

{{ subTitle }}

+
+ {% if details|length > 0 %} +

Pour les experts voici les détails :

+
+
+        {{ details }}
+        
+
+ {% endif %} +
+
+{% endblock %} diff --git a/views/organization.twig b/views/organization.twig index 1ccca63..cff4b0f 100644 --- a/views/organization.twig +++ b/views/organization.twig @@ -38,18 +38,25 @@ {% if data.gallery|length > 0 %}
{% for media in data.gallery %} -
-
+
+
+ {% if media.isVideo %} +
+ +
+ {% endif %}
{% endfor %} - {#
-
-
-
#}
- +
+ +