feat: return json format of organization page with .json extension and
http content negotiation chore(deps): add negotiator and @types/negotiator refactor: put obfuscating logic in a twig template filter
This commit is contained in:
parent
5dcd4f4f6d
commit
2a56067362
6 changed files with 104 additions and 71 deletions
|
@ -18,6 +18,7 @@
|
|||
"@types/multer": "^1.4.3",
|
||||
"@types/multer-s3": "^2.7.7",
|
||||
"@types/mustache": "^4.0.1",
|
||||
"@types/negotiator": "^0.6.1",
|
||||
"@types/nodemailer": "^6.4.0",
|
||||
"@types/papaparse": "^5.0.6",
|
||||
"@types/sanitize-html": "^1.23.3",
|
||||
|
|
34
src/Utils.ts
34
src/Utils.ts
|
@ -23,4 +23,36 @@ export default class Utils {
|
|||
}
|
||||
return bool
|
||||
}
|
||||
}
|
||||
|
||||
public static obfuscate(original: string) {
|
||||
if (original.length === 0) { return original }
|
||||
let cipher: string[] = []
|
||||
let data: string = Buffer.from(original, 'utf-8').toString('base64')
|
||||
data = data.replace(/A/gm, '$')
|
||||
data = data.replace(/c/gm, '£')
|
||||
data = data.replace(/b/gm, 'ù')
|
||||
data = data.replace(/d/gm, '#')
|
||||
data = data.replace(/e/gm, 'µ')
|
||||
data = data.replace(/z/gm, '§')
|
||||
data = data.replace(/i/gm, 'à')
|
||||
data = data.replace(/f/gm, '|')
|
||||
let remain = data
|
||||
|
||||
let take: number = 0
|
||||
|
||||
while (remain.length != 0) {
|
||||
take = remain.length
|
||||
if (take > 5) { take = 5 }
|
||||
take = Math.floor(Math.random() * (take + 1))
|
||||
if (take === 0) { take = 2 }
|
||||
if (take > remain.length) {
|
||||
take = remain.length
|
||||
}
|
||||
cipher.push('%_' + remain.substr(0, take) + '%_')
|
||||
remain = remain.substr(take)
|
||||
}
|
||||
|
||||
return JSON.stringify(cipher).replace(/"/gm, "'")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -89,6 +89,9 @@ let main = async () => {
|
|||
console.log('> App: Connected to mongodb')
|
||||
})
|
||||
|
||||
// add custom twig extensions (filters and functions)
|
||||
twig.extendFilter('obfuscate', Utils.obfuscate)
|
||||
|
||||
app.set('twig options', {
|
||||
allow_async: true,
|
||||
strict_variables: false
|
||||
|
|
|
@ -12,6 +12,7 @@ import ErrorController from './ErrorController'
|
|||
import Utils from '../Utils'
|
||||
import mongoose from 'mongoose'
|
||||
import MediaService from '../MediaService'
|
||||
import Negotiator from 'negotiator'
|
||||
|
||||
export default class PublicController {
|
||||
|
||||
|
@ -72,7 +73,25 @@ export default class PublicController {
|
|||
}
|
||||
|
||||
static async organization(req: express.Request, res: express.Response) {
|
||||
Organization.find({ slugs: { '$in': req.params.slug } }).then(data => {
|
||||
const rawSlug = req.params.slug
|
||||
let format = 'html'
|
||||
let slug = rawSlug
|
||||
// allow json format by using 'file' extension
|
||||
if (rawSlug.endsWith('.json')) {
|
||||
slug = rawSlug.slice(0, -('.json'.length))
|
||||
format = 'json'
|
||||
}
|
||||
|
||||
// allow json format by Accept header
|
||||
if (req.headers.accept) {
|
||||
let neg = new Negotiator(req)
|
||||
let negotiationResults = neg.mediaTypes(['text/html', 'application/json'])
|
||||
if (negotiationResults.length > 0 && negotiationResults[0] === 'application/json') {
|
||||
format = 'json'
|
||||
}
|
||||
}
|
||||
|
||||
Organization.find({ slugs: { '$in': slug } }).then(data => {
|
||||
if (data.length === 0) {
|
||||
return ErrorController.notFoundHandler()(req, res)
|
||||
} else {
|
||||
|
@ -80,6 +99,7 @@ export default class PublicController {
|
|||
let version = org.get('publishedVersion')
|
||||
let lastPublished = org.get('publishedAt')
|
||||
let isProposed = false
|
||||
|
||||
if (req.query.version === 'proposed') {
|
||||
isProposed = true
|
||||
lastPublished = org.get('updatedAt')
|
||||
|
@ -87,62 +107,23 @@ export default class PublicController {
|
|||
} else if (lastPublished === undefined || lastPublished === null) {
|
||||
return ErrorController.notFoundHandler()(req, res)
|
||||
}
|
||||
|
||||
if (!Utils.isStrUsable(version, 'cover.location')) {
|
||||
version.cover = { location: "https://fva-condorcet.s3.fr-par.scw.cloud/default-cover-plz-get-you-a-real-image.jpg" }
|
||||
}
|
||||
|
||||
let offuscate = (original: string) => {
|
||||
if (original.length === 0) { return original }
|
||||
let cipher: string[] = []
|
||||
let data: string = Buffer.from(original, 'utf-8').toString('base64')
|
||||
data = data.replace(/A/gm, '$')
|
||||
data = data.replace(/c/gm, '£')
|
||||
data = data.replace(/b/gm, 'ù')
|
||||
data = data.replace(/d/gm, '#')
|
||||
data = data.replace(/e/gm, 'µ')
|
||||
data = data.replace(/z/gm, '§')
|
||||
data = data.replace(/i/gm, 'à')
|
||||
data = data.replace(/f/gm, '|')
|
||||
let remain = data
|
||||
|
||||
let take: number = 0
|
||||
|
||||
while (remain.length != 0) {
|
||||
take = remain.length
|
||||
if (take > 5) { take = 5 }
|
||||
take = Math.floor(Math.random() * (take + 1))
|
||||
if (take === 0) { take = 2 }
|
||||
if (take > remain.length) {
|
||||
take = remain.length
|
||||
}
|
||||
cipher.push('%_' + remain.substr(0, take) + '%_')
|
||||
remain = remain.substr(take)
|
||||
}
|
||||
|
||||
return JSON.stringify(cipher).replace(/"/gm, "'")
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse contact
|
||||
*/
|
||||
if (Utils.isUsable(version.contacts)) {
|
||||
if (Utils.isStrUsable(version.contacts, 'email')) {
|
||||
version.contacts.email = offuscate(version.contacts.email)
|
||||
}
|
||||
if (Utils.isStrUsable(version.contacts, 'address')) {
|
||||
version.contacts.address = version.contacts.address.replace('%postalsep%', ' ').split('\n')
|
||||
}
|
||||
if (Utils.isStrUsable(version.contacts, 'phone')) {
|
||||
//let formated: any = formatPhone(version.contacts.phone)
|
||||
version.contacts.phone = offuscate(version.contacts.phone)
|
||||
}
|
||||
if (Utils.isUsable(version.contacts, 'peoples') && Array.isArray(version.contacts.peoples)) {
|
||||
version.contacts.peoples = version.contacts.peoples.map((p: any) => {
|
||||
// let formated: any = formatPhone(p.phone)
|
||||
// p.phoneInt = formated[0]
|
||||
// p.phoneSplit = formated[1]
|
||||
p.email = offuscate(p.email)
|
||||
p.phone = offuscate(p.phone)
|
||||
return p
|
||||
})
|
||||
}
|
||||
|
@ -202,8 +183,6 @@ export default class PublicController {
|
|||
media.isVideo = media.contentType.indexOf('video/') !== -1
|
||||
return media
|
||||
})
|
||||
version.firstGallery = version.gallery.slice(0, 5)
|
||||
version.secondGallery = version.gallery.slice(5)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -217,7 +196,6 @@ export default class PublicController {
|
|||
when: days.map((day: any) => {
|
||||
return {
|
||||
day,
|
||||
wow: 'wow',
|
||||
hours: s.when
|
||||
.filter((w: any) => w.day === day)
|
||||
.map((hour: any) => { return { from: hour.from, to: hour.to } })
|
||||
|
@ -229,18 +207,6 @@ export default class PublicController {
|
|||
})
|
||||
}
|
||||
|
||||
if (version.schedule.length > 8) {
|
||||
let isFirst = true;
|
||||
version.scheduleParts = [[], []]
|
||||
version.schedule.forEach((schedule: any) => {
|
||||
version.scheduleParts[isFirst ? 0 : 1].push(schedule)
|
||||
isFirst = !isFirst
|
||||
})
|
||||
} else {
|
||||
version.scheduleParts = [version.schedule]
|
||||
}
|
||||
|
||||
version.cutDescription = Utils.isStrUsable(version, 'descriptionLong') && version.descriptionLong.length > 800
|
||||
|
||||
// if (version.cutDescription) {
|
||||
// version.descriptionFirstHalf = version.descriptionLong.substr(0, 200) // not gonna lie
|
||||
|
@ -251,18 +217,44 @@ export default class PublicController {
|
|||
// hour = '0' + hour
|
||||
// }
|
||||
|
||||
/**
|
||||
* Obfuscate emails and phone
|
||||
*/
|
||||
if (format == 'html') {
|
||||
// Do some adjustements that are specifics to the html format
|
||||
|
||||
// TODO: Clarify this piece of code
|
||||
// TODO: don't split up in two use directly the display grid or any other better way of doing this on the view side
|
||||
if (version.schedule.length > 8) {
|
||||
let isFirst = true;
|
||||
version.scheduleParts = [[], []]
|
||||
version.schedule.forEach((schedule: any) => {
|
||||
version.scheduleParts[isFirst ? 0 : 1].push(schedule)
|
||||
isFirst = !isFirst
|
||||
})
|
||||
} else {
|
||||
version.scheduleParts = [version.schedule]
|
||||
}
|
||||
|
||||
version.cutDescription = Utils.isStrUsable(version, 'descriptionLong') && version.descriptionLong.length > 800
|
||||
|
||||
// Split gallery in two parts to only show at max 5 media on the page
|
||||
if (Array.isArray(version.gallery)) {
|
||||
version.firstGallery = version.gallery.slice(0, 5)
|
||||
version.secondGallery = version.gallery.slice(5)
|
||||
}
|
||||
|
||||
res.render('organization.twig', {
|
||||
layout: 'standalone',
|
||||
data: version,
|
||||
//lastPublished: lastPublished.toLocaleDateString('fr-FR') + ' ' + hour.substr(0, 5),
|
||||
isProposed,
|
||||
id: org.get('_id')
|
||||
})
|
||||
}
|
||||
if (format == 'json') {
|
||||
res.json({
|
||||
...version
|
||||
})
|
||||
}
|
||||
|
||||
res.render('organization.twig', {
|
||||
layout: 'standalone',
|
||||
data: version,
|
||||
//lastPublished: lastPublished.toLocaleDateString('fr-FR') + ' ' + hour.substr(0, 5),
|
||||
isProposed,
|
||||
id: org.get('_id')
|
||||
})
|
||||
}
|
||||
}).catch(err => () => {
|
||||
console.error(err)
|
||||
|
|
|
@ -256,7 +256,7 @@
|
|||
href="mailto:data"
|
||||
class="people-email-content offuscated"
|
||||
data-source-type="email"
|
||||
data-source="{{ data.contacts.email }}">
|
||||
data-source="{{ data.contacts.email | obfuscate }}">
|
||||
</a>
|
||||
</div>
|
||||
{% if data.contacts.phone is not empty %}
|
||||
|
@ -268,7 +268,7 @@
|
|||
href="telto:data"
|
||||
class="people-email-content offuscated"
|
||||
data-source-type="phone"
|
||||
data-source="{{ data.contacts.phone }}">
|
||||
data-source="{{ data.contacts.phone | obfuscate }}">
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -293,7 +293,7 @@
|
|||
href="mailto:data"
|
||||
class="people-email-content offuscated"
|
||||
data-source-type="email"
|
||||
data-source="{{ people.email }}">
|
||||
data-source="{{ people.email | obfuscate }}">
|
||||
</a>
|
||||
</div>
|
||||
{% if people.phone is not empty %}
|
||||
|
@ -305,7 +305,7 @@
|
|||
href="telto:data"
|
||||
class="people-email-content offuscated"
|
||||
data-source-type="phone"
|
||||
data-source="{{ people.phone }}">
|
||||
data-source="{{ people.phone | obfuscate }}">
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -759,6 +759,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.0.1.tgz#e4d421ed2d06d463b120621774185a5cd1b92d77"
|
||||
integrity sha512-wH6Tu9mbiOt0n5EvdoWy0VGQaJMHfLIxY/6wS0xLC7CV1taM6gESEzcYy0ZlWvxxiiljYvfDIvz4hHbUUDRlhw==
|
||||
|
||||
"@types/negotiator@^0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/negotiator/-/negotiator-0.6.1.tgz#4c75543f6ef87f427f4705e731a933595b7397f5"
|
||||
integrity sha512-c4mvXFByghezQ/eVGN5HvH/jI63vm3B7FiE81BUzDAWmuiohRecCO6ddU60dfq29oKUMiQujsoB2h0JQC7JHKA==
|
||||
|
||||
"@types/node@*":
|
||||
version "14.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499"
|
||||
|
|
Loading…
Reference in a new issue