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": "^1.4.3",
|
||||||
"@types/multer-s3": "^2.7.7",
|
"@types/multer-s3": "^2.7.7",
|
||||||
"@types/mustache": "^4.0.1",
|
"@types/mustache": "^4.0.1",
|
||||||
|
"@types/negotiator": "^0.6.1",
|
||||||
"@types/nodemailer": "^6.4.0",
|
"@types/nodemailer": "^6.4.0",
|
||||||
"@types/papaparse": "^5.0.6",
|
"@types/papaparse": "^5.0.6",
|
||||||
"@types/sanitize-html": "^1.23.3",
|
"@types/sanitize-html": "^1.23.3",
|
||||||
|
|
32
src/Utils.ts
32
src/Utils.ts
|
@ -23,4 +23,36 @@ export default class Utils {
|
||||||
}
|
}
|
||||||
return bool
|
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')
|
console.log('> App: Connected to mongodb')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// add custom twig extensions (filters and functions)
|
||||||
|
twig.extendFilter('obfuscate', Utils.obfuscate)
|
||||||
|
|
||||||
app.set('twig options', {
|
app.set('twig options', {
|
||||||
allow_async: true,
|
allow_async: true,
|
||||||
strict_variables: false
|
strict_variables: false
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ErrorController from './ErrorController'
|
||||||
import Utils from '../Utils'
|
import Utils from '../Utils'
|
||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import MediaService from '../MediaService'
|
import MediaService from '../MediaService'
|
||||||
|
import Negotiator from 'negotiator'
|
||||||
|
|
||||||
export default class PublicController {
|
export default class PublicController {
|
||||||
|
|
||||||
|
@ -72,7 +73,25 @@ export default class PublicController {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async organization(req: express.Request, res: express.Response) {
|
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) {
|
if (data.length === 0) {
|
||||||
return ErrorController.notFoundHandler()(req, res)
|
return ErrorController.notFoundHandler()(req, res)
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,6 +99,7 @@ export default class PublicController {
|
||||||
let version = org.get('publishedVersion')
|
let version = org.get('publishedVersion')
|
||||||
let lastPublished = org.get('publishedAt')
|
let lastPublished = org.get('publishedAt')
|
||||||
let isProposed = false
|
let isProposed = false
|
||||||
|
|
||||||
if (req.query.version === 'proposed') {
|
if (req.query.version === 'proposed') {
|
||||||
isProposed = true
|
isProposed = true
|
||||||
lastPublished = org.get('updatedAt')
|
lastPublished = org.get('updatedAt')
|
||||||
|
@ -87,62 +107,23 @@ export default class PublicController {
|
||||||
} else if (lastPublished === undefined || lastPublished === null) {
|
} else if (lastPublished === undefined || lastPublished === null) {
|
||||||
return ErrorController.notFoundHandler()(req, res)
|
return ErrorController.notFoundHandler()(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Utils.isStrUsable(version, 'cover.location')) {
|
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" }
|
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
|
* Parse contact
|
||||||
*/
|
*/
|
||||||
if (Utils.isUsable(version.contacts)) {
|
if (Utils.isUsable(version.contacts)) {
|
||||||
if (Utils.isStrUsable(version.contacts, 'email')) {
|
|
||||||
version.contacts.email = offuscate(version.contacts.email)
|
|
||||||
}
|
|
||||||
if (Utils.isStrUsable(version.contacts, 'address')) {
|
if (Utils.isStrUsable(version.contacts, 'address')) {
|
||||||
version.contacts.address = version.contacts.address.replace('%postalsep%', ' ').split('\n')
|
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)) {
|
if (Utils.isUsable(version.contacts, 'peoples') && Array.isArray(version.contacts.peoples)) {
|
||||||
version.contacts.peoples = version.contacts.peoples.map((p: any) => {
|
version.contacts.peoples = version.contacts.peoples.map((p: any) => {
|
||||||
// let formated: any = formatPhone(p.phone)
|
// let formated: any = formatPhone(p.phone)
|
||||||
// p.phoneInt = formated[0]
|
// p.phoneInt = formated[0]
|
||||||
// p.phoneSplit = formated[1]
|
// p.phoneSplit = formated[1]
|
||||||
p.email = offuscate(p.email)
|
|
||||||
p.phone = offuscate(p.phone)
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -202,8 +183,6 @@ export default class PublicController {
|
||||||
media.isVideo = media.contentType.indexOf('video/') !== -1
|
media.isVideo = media.contentType.indexOf('video/') !== -1
|
||||||
return media
|
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) => {
|
when: days.map((day: any) => {
|
||||||
return {
|
return {
|
||||||
day,
|
day,
|
||||||
wow: 'wow',
|
|
||||||
hours: s.when
|
hours: s.when
|
||||||
.filter((w: any) => w.day === day)
|
.filter((w: any) => w.day === day)
|
||||||
.map((hour: any) => { return { from: hour.from, to: hour.to } })
|
.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) {
|
// if (version.cutDescription) {
|
||||||
// version.descriptionFirstHalf = version.descriptionLong.substr(0, 200) // not gonna lie
|
// version.descriptionFirstHalf = version.descriptionLong.substr(0, 200) // not gonna lie
|
||||||
|
@ -251,18 +217,44 @@ export default class PublicController {
|
||||||
// hour = '0' + hour
|
// hour = '0' + hour
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/**
|
if (format == 'html') {
|
||||||
* Obfuscate emails and phone
|
// 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 => () => {
|
}).catch(err => () => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
|
@ -256,7 +256,7 @@
|
||||||
href="mailto:data"
|
href="mailto:data"
|
||||||
class="people-email-content offuscated"
|
class="people-email-content offuscated"
|
||||||
data-source-type="email"
|
data-source-type="email"
|
||||||
data-source="{{ data.contacts.email }}">
|
data-source="{{ data.contacts.email | obfuscate }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% if data.contacts.phone is not empty %}
|
{% if data.contacts.phone is not empty %}
|
||||||
|
@ -268,7 +268,7 @@
|
||||||
href="telto:data"
|
href="telto:data"
|
||||||
class="people-email-content offuscated"
|
class="people-email-content offuscated"
|
||||||
data-source-type="phone"
|
data-source-type="phone"
|
||||||
data-source="{{ data.contacts.phone }}">
|
data-source="{{ data.contacts.phone | obfuscate }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -293,7 +293,7 @@
|
||||||
href="mailto:data"
|
href="mailto:data"
|
||||||
class="people-email-content offuscated"
|
class="people-email-content offuscated"
|
||||||
data-source-type="email"
|
data-source-type="email"
|
||||||
data-source="{{ people.email }}">
|
data-source="{{ people.email | obfuscate }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% if people.phone is not empty %}
|
{% if people.phone is not empty %}
|
||||||
|
@ -305,7 +305,7 @@
|
||||||
href="telto:data"
|
href="telto:data"
|
||||||
class="people-email-content offuscated"
|
class="people-email-content offuscated"
|
||||||
data-source-type="phone"
|
data-source-type="phone"
|
||||||
data-source="{{ people.phone }}">
|
data-source="{{ people.phone | obfuscate }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -759,6 +759,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.0.1.tgz#e4d421ed2d06d463b120621774185a5cd1b92d77"
|
resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.0.1.tgz#e4d421ed2d06d463b120621774185a5cd1b92d77"
|
||||||
integrity sha512-wH6Tu9mbiOt0n5EvdoWy0VGQaJMHfLIxY/6wS0xLC7CV1taM6gESEzcYy0ZlWvxxiiljYvfDIvz4hHbUUDRlhw==
|
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@*":
|
"@types/node@*":
|
||||||
version "14.6.0"
|
version "14.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499"
|
||||||
|
|
Loading…
Reference in a new issue