update
This commit is contained in:
parent
51208cad8d
commit
76fd9fb811
19 changed files with 3439 additions and 325 deletions
25
.env.example
25
.env.example
|
@ -1,18 +1,23 @@
|
||||||
S3_ORGANIZATION_ID=
|
S3_ORGANIZATION_ID=XXXX
|
||||||
S3_ACCESS_KEY=
|
S3_ACCESS_KEY=XXXXX
|
||||||
S3_SECRET_KEY=
|
S3_SECRET_KEY=XXXXX
|
||||||
|
S3_BUCKET=development-bucket
|
||||||
|
|
||||||
MONGO_URI=mongodb://root:root@127.0.0.1:27017/forumvirt?authSource=admin
|
MONGO_URI=mongodb://admin:password@127.0.0.1:57129/fva_dev?authSource=test
|
||||||
|
|
||||||
SMTP_HOST=
|
SMTP_HOST=smtp.mailgun.org
|
||||||
SMTP_PORT=
|
SMTP_PORT=587
|
||||||
SMTP_USERNAME=
|
|
||||||
SMTP_PASSWORD=
|
|
||||||
SMTP_SECURE=false
|
SMTP_SECURE=false
|
||||||
|
SMTP_USERNAME=spamfree@matthieubessat.fr
|
||||||
|
SMTP_PASSWORD=XXXX
|
||||||
|
|
||||||
ADMIN_TOKEN=pass
|
ADMIN_TOKEN=2cf5ddea54d
|
||||||
ADMIN_EMAIL=spamfree@matthieubessat.fr
|
ADMIN_EMAIL=spamfree@matthieubessat.fr
|
||||||
|
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=localhost
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_PASSWORD=root
|
|
||||||
|
BASE_URL=http://srv.espacecondorcet.org:8001
|
||||||
|
WEB_UI_URL=http://localhost:8080
|
||||||
|
|
||||||
|
MOCK_EMAIL=false
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/html-to-text": "^5.1.1",
|
"@types/html-to-text": "^5.1.1",
|
||||||
"@types/ioredis": "^4.17.0",
|
"@types/ioredis": "^4.17.0",
|
||||||
|
"@types/jest": "^26.0.4",
|
||||||
"@types/mongoose": "^5.7.28",
|
"@types/mongoose": "^5.7.28",
|
||||||
"@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/nodemailer": "^6.4.0",
|
"@types/nodemailer": "^6.4.0",
|
||||||
|
"@types/sanitize-html": "^1.23.3",
|
||||||
"@types/twig": "^1.12.3",
|
"@types/twig": "^1.12.3",
|
||||||
"aws-sdk": "^2.706.0",
|
"aws-sdk": "^2.706.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
|
@ -22,6 +24,7 @@
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"html-to-text": "^5.1.1",
|
"html-to-text": "^5.1.1",
|
||||||
"ioredis": "^4.17.3",
|
"ioredis": "^4.17.3",
|
||||||
|
"jest": "^26.1.0",
|
||||||
"mongoose": "^5.9.20",
|
"mongoose": "^5.9.20",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"multer-s3": "^2.9.0",
|
"multer-s3": "^2.9.0",
|
||||||
|
@ -29,7 +32,9 @@
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.0.1",
|
||||||
"nanoid": "^3.1.10",
|
"nanoid": "^3.1.10",
|
||||||
"nodemailer": "^6.4.10",
|
"nodemailer": "^6.4.10",
|
||||||
|
"sanitize-html": "^1.27.1",
|
||||||
"slugify": "^1.4.4",
|
"slugify": "^1.4.4",
|
||||||
|
"ts-jest": "^26.1.2",
|
||||||
"twig": "^1.15.1",
|
"twig": "^1.15.1",
|
||||||
"typescript": "^3.9.5"
|
"typescript": "^3.9.5"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,12 @@ import fs from 'fs'
|
||||||
import twig from 'twig'
|
import twig from 'twig'
|
||||||
|
|
||||||
export default class EmailService {
|
export default class EmailService {
|
||||||
|
static init() {
|
||||||
|
if (this.isMocked()) {
|
||||||
|
console.warn('> EmailService: WARNING: EMAIL IS MOCKED!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static getTransporter() {
|
static getTransporter() {
|
||||||
if (process.env.SMTP_HOST == undefined || process.env.SMTP_PORT == undefined) {
|
if (process.env.SMTP_HOST == undefined || process.env.SMTP_PORT == undefined) {
|
||||||
throw new Error("Invalid SMTP config")
|
throw new Error("Invalid SMTP config")
|
||||||
|
@ -17,7 +23,6 @@ export default class EmailService {
|
||||||
pass: process.env.SMTP_PASSWORD,
|
pass: process.env.SMTP_PASSWORD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(config)
|
|
||||||
return nodemailer.createTransport(config)
|
return nodemailer.createTransport(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +46,11 @@ export default class EmailService {
|
||||||
static send(to: string, subject: string, templateName: string, templateParams: any): Promise<any> {
|
static send(to: string, subject: string, templateName: string, templateParams: any): Promise<any> {
|
||||||
const viewPath: string = __dirname + '/../views/emails/'
|
const viewPath: string = __dirname + '/../views/emails/'
|
||||||
const data: Buffer = fs.readFileSync(viewPath + templateName + '.twig')
|
const data: Buffer = fs.readFileSync(viewPath + templateName + '.twig')
|
||||||
const html: string = twig.twig({ data: data.toString() }).render(templateParams)
|
const html: string = twig.twig({
|
||||||
|
// @ts-ignore
|
||||||
|
allowInlineIncludes: true,
|
||||||
|
data: data.toString()
|
||||||
|
}).render(templateParams)
|
||||||
const text: string = htmlToText.fromString(html, { wordwrap: 130 })
|
const text: string = htmlToText.fromString(html, { wordwrap: 130 })
|
||||||
|
|
||||||
// for now replace every email by a predefined one
|
// for now replace every email by a predefined one
|
||||||
|
@ -54,13 +63,34 @@ export default class EmailService {
|
||||||
from: '"Forum des associations - Espace Condorcet Centre Social" <ne-pas-repondre@espacecondorcet.org>',
|
from: '"Forum des associations - Espace Condorcet Centre Social" <ne-pas-repondre@espacecondorcet.org>',
|
||||||
to, subject, text, html
|
to, subject, text, html
|
||||||
}
|
}
|
||||||
console.log(config.html)
|
if (this.isMocked()) {
|
||||||
this.nativeSend(config).then(res => {
|
delete config.html
|
||||||
console.log(res)
|
console.log(config)
|
||||||
resolve()
|
resolve()
|
||||||
}).catch(err => {
|
} else {
|
||||||
console.error(err)
|
this.nativeSend(config).then(res => {
|
||||||
})
|
console.log('> A "' + templateName + '" email was sent')
|
||||||
|
resolve()
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('> A "' + templateName + '" email failed to be sent')
|
||||||
|
console.error(err)
|
||||||
|
reject()
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getWebUiBaseUrl() {
|
||||||
|
return process.env.WEB_UI_URL === undefined ? "URL_NOT_FOUND" : process.env.WEB_UI_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
static getBaseUrl() {
|
||||||
|
return process.env.BASE_URL === undefined ? "URL_NOT_FOUND" : process.env.BASE_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
static isMocked() {
|
||||||
|
// WARNING ATTENTION REQUIRED
|
||||||
|
// CUSTOM DESACTIVATION PLEASE ENABLE IN PRODUCTION
|
||||||
|
return process.env.MOCK_EMAIL === "true"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import aws from 'aws-sdk'
|
import aws from 'aws-sdk'
|
||||||
|
import multer from 'multer'
|
||||||
|
import multerS3 from 'multer-s3'
|
||||||
|
|
||||||
export default class MediaService {
|
export default class MediaService {
|
||||||
|
|
||||||
|
@ -18,4 +20,59 @@ export default class MediaService {
|
||||||
static getBucket(): string {
|
static getBucket(): string {
|
||||||
return process.env.S3_BUCKET === undefined ? '___BUCKET_NOT_FOUND_ENV_VAR_ISSUE___' : process.env.S3_BUCKET
|
return process.env.S3_BUCKET === undefined ? '___BUCKET_NOT_FOUND_ENV_VAR_ISSUE___' : process.env.S3_BUCKET
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static delete(key: string, context: string) {
|
||||||
|
MediaService.getS3().deleteObject({
|
||||||
|
Bucket: MediaService.getBucket(),
|
||||||
|
Key: key
|
||||||
|
}, (err, _) => {
|
||||||
|
if (err !== null) {
|
||||||
|
console.error('> MediaCleanup: in context "' + context + '" - Cannot delete a media from a organization - key: ' + key)
|
||||||
|
console.log(err, err.stack)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static deleteMany(keys: string[], context: string) {
|
||||||
|
console.log('> MediaCleanup: in context "' + context + '" deleteMany', keys)
|
||||||
|
keys.forEach((key: string) => {
|
||||||
|
if (key === undefined || key === null || key.length <= 2) { return }
|
||||||
|
MediaService.delete(key, context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static multer(type: string, multiple: boolean = false) {
|
||||||
|
let instance = multer({
|
||||||
|
storage: multerS3({
|
||||||
|
s3: MediaService.getS3(),
|
||||||
|
bucket: MediaService.getBucket(),
|
||||||
|
acl: 'public-read',
|
||||||
|
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||||
|
key: (_: any, file: any, cb: any) => {
|
||||||
|
console.log(file)
|
||||||
|
cb(null, Date.now().toString() + '_' + type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (multiple) {
|
||||||
|
return instance.array('file')
|
||||||
|
}
|
||||||
|
return instance.single('file')
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildMedia(file: any, type: any) {
|
||||||
|
return {
|
||||||
|
// @ts-ignore
|
||||||
|
key: file.key,
|
||||||
|
// @ts-ignore
|
||||||
|
contentType: file.contentType,
|
||||||
|
// @ts-ignore
|
||||||
|
location: file.location,
|
||||||
|
// @ts-ignore
|
||||||
|
size: file.size,
|
||||||
|
// @ts-ignore
|
||||||
|
originalFileName: file.originalname,
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
26
src/Utils.ts
Normal file
26
src/Utils.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
export default class Utils {
|
||||||
|
public static isStrUsable(value: any, keys: string = '') {
|
||||||
|
return this.isUsable(value, keys, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isUsable(value: any, keys: string = '', isStr = false) {
|
||||||
|
let bool: boolean = value !== undefined && value !== null
|
||||||
|
if (keys === '') {
|
||||||
|
if (isStr) { bool = bool && typeof value === 'string' && value.length > 0 }
|
||||||
|
return bool
|
||||||
|
}
|
||||||
|
let parts: string[] = keys.split('.')
|
||||||
|
let levels: number = parts.length
|
||||||
|
let explored: any = null
|
||||||
|
for (var i = 0; i < levels + 1; i++) {
|
||||||
|
explored = explored === null ? value : explored[parts[i - 1]]
|
||||||
|
bool = bool && (
|
||||||
|
explored !== undefined && explored !== null
|
||||||
|
)
|
||||||
|
if (i - 1 === levels && isStr) {
|
||||||
|
bool = bool && typeof explored === 'string' && explored.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bool
|
||||||
|
}
|
||||||
|
}
|
15
src/app.ts
15
src/app.ts
|
@ -1,4 +1,4 @@
|
||||||
|
import path from 'path'
|
||||||
import bodyParser from 'body-parser'
|
import bodyParser from 'body-parser'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
|
@ -13,6 +13,7 @@ import DelegateAuthMiddleware from './middlewares/DelegateAuthMiddleware'
|
||||||
import PublicController from './controllers/PublicController'
|
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'
|
||||||
|
|
||||||
dotenv.config({
|
dotenv.config({
|
||||||
path: __dirname + '/../.env'
|
path: __dirname + '/../.env'
|
||||||
|
@ -25,6 +26,8 @@ const port: number = 8001
|
||||||
twig.cache(false)
|
twig.cache(false)
|
||||||
|
|
||||||
let main = async () => {
|
let main = async () => {
|
||||||
|
EmailService.init()
|
||||||
|
|
||||||
mongoose.connection.on('error', err => {
|
mongoose.connection.on('error', err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
|
@ -36,7 +39,7 @@ let main = async () => {
|
||||||
useUnifiedTopology: true
|
useUnifiedTopology: true
|
||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
console.log('> Connected to mongodb')
|
console.log('> App: Connected to mongodb')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.set("twig options", {
|
app.set("twig options", {
|
||||||
|
@ -84,7 +87,7 @@ let main = async () => {
|
||||||
app.use('/delegate', express.Router()
|
app.use('/delegate', express.Router()
|
||||||
.use(DelegateAuthMiddleware.handle)
|
.use(DelegateAuthMiddleware.handle)
|
||||||
.get('/', DelegateController.get)
|
.get('/', DelegateController.get)
|
||||||
.post('/test', DelegateController.test)
|
//.post('/test', DelegateController.test)
|
||||||
.put('/', DelegateController.update)
|
.put('/', DelegateController.update)
|
||||||
.post('/submit', DelegateController.submit)
|
.post('/submit', DelegateController.submit)
|
||||||
.post('/thumbnail', DelegateController.uploadThumbnail())
|
.post('/thumbnail', DelegateController.uploadThumbnail())
|
||||||
|
@ -102,13 +105,13 @@ let main = async () => {
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
app.post('/api/media', MediaController.uploadRoute())
|
//app.post('/api/media', MediaController.uploadRoute())
|
||||||
//app.delete('/api/media/:key', MediaController.delete)
|
//app.delete('/api/media/:key', MediaController.delete)
|
||||||
|
|
||||||
app.use(express.static('/apps/forum_server/static'))
|
app.use(express.static(path.resolve('./static')))
|
||||||
|
|
||||||
app.listen(port, host, () => {
|
app.listen(port, host, () => {
|
||||||
console.log(`API listening on ${host}:${port}`)
|
console.log(`> App: API listening on ${host}:${port}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,17 @@ import EmailService from '../EmailService'
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
import { Document } from 'mongoose'
|
import { Document } from 'mongoose'
|
||||||
import MediaService from '../MediaService'
|
import MediaService from '../MediaService'
|
||||||
|
import Utils from '../Utils'
|
||||||
|
|
||||||
export default class AdminOrganizationController {
|
export default class AdminOrganizationController {
|
||||||
static getMany(req: express.Request, res: express.Response) {
|
static getMany(req: express.Request, res: express.Response) {
|
||||||
Organization.find().then(data => {
|
Organization.find().then(data => res.json({ success: true, data }))
|
||||||
res.json({ success: true, data })
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getOne(req: express.Request, res: express.Response) {
|
static getOne(req: express.Request, res: express.Response) {
|
||||||
Organization.findById(req.params.id).then(data => {
|
Organization.findById(req.params.id)
|
||||||
res.json({ success: true, data })
|
.then(data => res.json({ success: true, data }))
|
||||||
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
.catch(err => res.status(400).json({ success: false, errors: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
static store(req: express.Request, res: express.Response) {
|
static store(req: express.Request, res: express.Response) {
|
||||||
|
@ -42,17 +41,8 @@ export default class AdminOrganizationController {
|
||||||
}
|
}
|
||||||
Organization.create(body).then(data => {
|
Organization.create(body).then(data => {
|
||||||
AdminOrganizationController.sendEmailTokenUniversal(data)
|
AdminOrganizationController.sendEmailTokenUniversal(data)
|
||||||
res.json({
|
res.json({ success: true, data })
|
||||||
success: true,
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors }))
|
||||||
data
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err)
|
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static update(req: express.Request, res: express.Response) {
|
static update(req: express.Request, res: express.Response) {
|
||||||
|
@ -64,15 +54,9 @@ export default class AdminOrganizationController {
|
||||||
...req.body,
|
...req.body,
|
||||||
...extra,
|
...extra,
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
}).then(data => {
|
})
|
||||||
res.json({
|
.then(data => res.json({ success: true, data }))
|
||||||
success: true,
|
.catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
data
|
|
||||||
})
|
|
||||||
}).catch(err => res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static destroy(req: express.Request, res: express.Response) {
|
static destroy(req: express.Request, res: express.Response) {
|
||||||
|
@ -95,19 +79,7 @@ export default class AdminOrganizationController {
|
||||||
keys = keys.concat(proposedVersion.gallery.map((m: any) => m.key))
|
keys = keys.concat(proposedVersion.gallery.map((m: any) => m.key))
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.forEach((key: string) => {
|
MediaService.deleteMany(keys, 'destroyOrganizationFromAdmin')
|
||||||
if (key === undefined || key === null || key.length <= 2) { return }
|
|
||||||
console.log('> OrganizationDestroyMediaCleanup: Deleted ' + key)
|
|
||||||
MediaService.getS3().deleteObject({
|
|
||||||
Bucket: MediaService.getBucket(),
|
|
||||||
Key: key
|
|
||||||
}, (err, _) => {
|
|
||||||
if (err !== null) {
|
|
||||||
console.error('> OrganizationDestroyMediaCleanup: Cannot delete a media from a organization which will be deleted')
|
|
||||||
console.log(err, err.stack)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let isSuccess = data.deletedCount !== undefined && data.deletedCount > 0
|
let isSuccess = data.deletedCount !== undefined && data.deletedCount > 0
|
||||||
res.status(isSuccess ? 200 : 400).json({
|
res.status(isSuccess ? 200 : 400).json({
|
||||||
|
@ -118,6 +90,12 @@ export default class AdminOrganizationController {
|
||||||
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will send a email at the address linked to this organization containing the current token
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
static sendEmailToken(req: express.Request, res: express.Response) {
|
static sendEmailToken(req: express.Request, res: express.Response) {
|
||||||
Organization.findById(req.params.id).then(data => {
|
Organization.findById(req.params.id).then(data => {
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
|
@ -129,50 +107,119 @@ export default class AdminOrganizationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
static resetToken(req: express.Request, res: express.Response) {
|
static resetToken(req: express.Request, res: express.Response) {
|
||||||
Organization.updateOne({ _id: req.params.id }, {
|
Organization.updateOne({ _id: req.params.id }, { token: this.generateToken(), updatedAt: new Date() })
|
||||||
token: this.generateToken(),
|
.then(data => res.json({ success: true, data }))
|
||||||
updatedAt: new Date()
|
.catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
}).then(data => {
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}).catch(err => res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve (or publish) the organization
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
static approve(req: express.Request, res: express.Response) {
|
static approve(req: express.Request, res: express.Response) {
|
||||||
|
Organization.findById(req.params.id).then(data => {
|
||||||
|
if (
|
||||||
|
data === null ||
|
||||||
|
data.get('proposedVersion') === null ||
|
||||||
|
data.get('validationState') !== 'pending' ||
|
||||||
|
data.get('proposedVersion') === undefined
|
||||||
|
) {
|
||||||
|
return res.status(500).json({ success: false, errors: [{ code: 'cannot-approve', message: 'cannot approve this organization' }] })
|
||||||
|
}
|
||||||
|
|
||||||
|
const publishedVersion: any = data.get('publishedVersion')
|
||||||
|
const proposedVersion: any = data.get('proposedVersion')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean all the unused media from the publishedVersion
|
||||||
|
*/
|
||||||
|
let mediaKeysToDelete: string[] = []
|
||||||
|
// only delete the published thumbnail when it is
|
||||||
|
// the published thumbnail must be defined AND (the published thumbnail must be different from the proposed one OR the proposed one is not defined)
|
||||||
|
if (
|
||||||
|
Utils.isStrUsable(publishedVersion, 'thumbnail.key') &&
|
||||||
|
(
|
||||||
|
(Utils.isStrUsable(proposedVersion, 'thumbnail.key') && proposedVersion.thumbnail.key !== publishedVersion.thumbnail.key) ||
|
||||||
|
!Utils.isStrUsable(proposedVersion, 'thumbnail.key')
|
||||||
|
)
|
||||||
|
) { mediaKeysToDelete.push(publishedVersion.thumbnail.key) }
|
||||||
|
|
||||||
|
// only delete the published cover if the cover in the proposedVersion is different or if the cover in the proposedVersion is undefined
|
||||||
|
if (
|
||||||
|
Utils.isStrUsable(publishedVersion, 'cover.key') &&
|
||||||
|
(
|
||||||
|
(Utils.isStrUsable(proposedVersion, 'cover.key') && proposedVersion.cover.key !== publishedVersion.cover.key) ||
|
||||||
|
!Utils.isStrUsable(proposedVersion, 'cover.key')
|
||||||
|
)
|
||||||
|
) { mediaKeysToDelete.push(publishedVersion.cover.key) }
|
||||||
|
|
||||||
|
// for each usable published gallery media, verify if it is still present on the proposedVersion
|
||||||
|
if (Utils.isUsable(publishedVersion, 'gallery') && Array.isArray(publishedVersion.gallery)) {
|
||||||
|
// add all medias keys only if the proposedVersion gallery is unusable
|
||||||
|
const deleteAll = !Utils.isUsable(proposedVersion, 'gallery')
|
||||||
|
mediaKeysToDelete = mediaKeysToDelete.concat(publishedVersion.gallery.filter((m: any) => {
|
||||||
|
// if we don't find the media on the proposedVersion gallery we can delete it
|
||||||
|
return deleteAll || proposedVersion.gallery.filter((_m: any) => _m.key === m.key).length === 0
|
||||||
|
}).map((m: any) => m.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaService.deleteMany(mediaKeysToDelete, 'approval')
|
||||||
|
/**
|
||||||
|
* End of media cleanup
|
||||||
|
*/
|
||||||
|
|
||||||
|
Organization.updateOne({ _id: req.params.id }, {
|
||||||
|
publishedVersion: data.get('proposedVersion'),
|
||||||
|
rejectionDescription: '',
|
||||||
|
validationState: 'published',
|
||||||
|
publishedAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}).then(updateData => {
|
||||||
|
EmailService.send(
|
||||||
|
data.get('email'),
|
||||||
|
"Félicitation, vos changements ont été approvés et publiés !",
|
||||||
|
"published",
|
||||||
|
{
|
||||||
|
adminName: data.get('adminName'),
|
||||||
|
link: EmailService.getBaseUrl() + '/association/' + data.get('slug')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
res.json({ success: true, data: updateData })
|
||||||
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
|
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
static reject(req: express.Request, res: express.Response) {
|
static reject(req: express.Request, res: express.Response) {
|
||||||
Organization.findById(req.params.id).then(data => {
|
Organization.findById(req.params.id).then(data => {
|
||||||
Organization.updateOne({ _id: req.params.id }, {
|
if (
|
||||||
|
data === null ||
|
||||||
|
data.get('adminName') === null ||
|
||||||
|
data.get('token') === null ||
|
||||||
|
data.get('validationState') !== 'pending' ||
|
||||||
|
!Utils.isStrUsable(req.body.rejectionDescription)
|
||||||
|
) {
|
||||||
|
return res.status(400).json({ success: false, errors: [{ code: 'cannot-approve', message: 'cannot approve this organization' }] })
|
||||||
|
}
|
||||||
|
|
||||||
|
Organization.updateOne({ _id: req.params.id }, {
|
||||||
|
validationState: 'rejected',
|
||||||
|
rejectionDescription: req.body.rejectionDescription,
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
}).then(data => {
|
}).then(updateData => {
|
||||||
EmailService.send(
|
EmailService.send(
|
||||||
data.get('email'),
|
data.get('email'),
|
||||||
"Votre lien secret pour modifier votre association",
|
"Oups, vos modifications n'ont pas été acceptées",
|
||||||
"token",
|
"rejected",
|
||||||
{ adminName: data.get('adminName'), rejectionDescription: data.get('rejectionDescription') }
|
{
|
||||||
).then(() => {
|
adminName: data.get('adminName'),
|
||||||
console.log('> A token email was sent')
|
rejectionDescription: req.body.rejectionDescription,
|
||||||
}).catch(() => {
|
link: EmailService.getWebUiBaseUrl() + '/delegate?delegateToken=' + data.get('token')
|
||||||
console.error('> Token email failed to be sent')
|
}
|
||||||
})
|
)
|
||||||
res.json({
|
res.json({ success: true, data: updateData })
|
||||||
success: true,
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
data
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
}).catch(err => res.status(400).json({ success: false, errors: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +228,6 @@ export default class AdminOrganizationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
static sendEmailTokenUniversal(data: Document) {
|
static sendEmailTokenUniversal(data: Document) {
|
||||||
const baseUrl = process.env.WEB_UI_URL === undefined ? "URL_NOT_FOUND" : process.env.WEB_UI_URL
|
|
||||||
EmailService.send(
|
EmailService.send(
|
||||||
data.get('email'),
|
data.get('email'),
|
||||||
"Votre lien secret pour modifier votre association",
|
"Votre lien secret pour modifier votre association",
|
||||||
|
@ -189,12 +235,8 @@ export default class AdminOrganizationController {
|
||||||
{
|
{
|
||||||
adminName: data.get('adminName'),
|
adminName: data.get('adminName'),
|
||||||
token: data.get('token'),
|
token: data.get('token'),
|
||||||
link: baseUrl + '/delegate?delegateToken=' + data.get('token')
|
link: EmailService.getWebUiBaseUrl() + '/delegate?delegateToken=' + data.get('token')
|
||||||
}
|
}
|
||||||
).then(() => {
|
)
|
||||||
console.log('> A token email was sent')
|
|
||||||
}).catch(() => {
|
|
||||||
console.error('> Token email failed to be sent')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,9 +3,9 @@ import Organization from '../models/Organization'
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import EmailService from '../EmailService'
|
import EmailService from '../EmailService'
|
||||||
import MediaService from '../MediaService'
|
import MediaService from '../MediaService'
|
||||||
import multer from 'multer'
|
|
||||||
import multerS3 from 'multer-s3'
|
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
|
import Utils from '../Utils'
|
||||||
|
import sanitizeHtml from 'sanitize-html'
|
||||||
|
|
||||||
export default class DelegateController {
|
export default class DelegateController {
|
||||||
|
|
||||||
|
@ -21,14 +21,14 @@ export default class DelegateController {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static test(req: express.Request, res: express.Response) {
|
// static test(req: express.Request, res: express.Response) {
|
||||||
res.json({ success: true, body: req.body, organization: res.locals.organization })
|
// res.json({ success: true, body: req.body, organization: res.locals.organization })
|
||||||
}
|
// }
|
||||||
|
|
||||||
static update(req: express.Request, res: express.Response) {
|
static update(req: express.Request, res: express.Response) {
|
||||||
const organization: any = res.locals.organization
|
const organization: any = res.locals.organization
|
||||||
if (organization.validationState === 'pending') {
|
if (organization.validationState === 'pending') {
|
||||||
return res.json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
errors: [{ code: 'update-forbidden', message: 'Organization cannot be updated while the validationState is set to "pending"' }]
|
errors: [{ code: 'update-forbidden', message: 'Organization cannot be updated while the validationState is set to "pending"' }]
|
||||||
})
|
})
|
||||||
|
@ -39,6 +39,10 @@ export default class DelegateController {
|
||||||
let proposedVersion: any = req.body
|
let proposedVersion: any = req.body
|
||||||
proposedVersion.tag = tag
|
proposedVersion.tag = tag
|
||||||
proposedVersion.descriptionLong = proposedVersion.descriptionLong.replace(/\n/g, '')
|
proposedVersion.descriptionLong = proposedVersion.descriptionLong.replace(/\n/g, '')
|
||||||
|
proposedVersion.descriptionLong = sanitizeHtml(
|
||||||
|
proposedVersion.descriptionLong,
|
||||||
|
{ allowedTags: ['h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'li', 'b', 'i', 'abbr', 'strong', 'em', 'hr', 'br', 'div', 'pre'] }
|
||||||
|
)
|
||||||
|
|
||||||
// validate contact.address
|
// validate contact.address
|
||||||
// validate all fields to not overflow
|
// validate all fields to not overflow
|
||||||
|
@ -50,21 +54,23 @@ export default class DelegateController {
|
||||||
proposedVersion.gallery = []
|
proposedVersion.gallery = []
|
||||||
}
|
}
|
||||||
if (Array.isArray(organization.proposedVersion.gallery)) {
|
if (Array.isArray(organization.proposedVersion.gallery)) {
|
||||||
organization.proposedVersion.gallery.forEach((media: any) => {
|
let toDeleteMedias = organization.proposedVersion.gallery.filter((media: any) =>
|
||||||
// if a existing media is not in the new version we delete it
|
// if a existing media is not in the new version we delete it AND in the published version
|
||||||
if (proposedVersion.gallery.filter((m: any) => m.key === media.key).length === 0) {
|
proposedVersion.gallery.filter((m: any) => m.key === media.key).length === 0)
|
||||||
console.log('> Old media cleanup: we must delete ', media)
|
MediaService.deleteMany(toDeleteMedias, 'galleryUpdated')
|
||||||
MediaService.getS3().deleteObject({
|
}
|
||||||
Bucket: MediaService.getBucket(),
|
|
||||||
Key: media.key
|
// delete unused cover
|
||||||
}, (err, _) => {
|
// if the cover is reset we must delete the old one only if it is unused in the published version
|
||||||
if (err !== null) {
|
if (
|
||||||
console.error('> Cannot delete a old media element')
|
Utils.isStrUsable(organization, 'proposedVersion.cover.key') &&
|
||||||
console.log(err, err.stack)
|
!Utils.isStrUsable(proposedVersion, 'cover.key') &&
|
||||||
}
|
!(
|
||||||
})
|
Utils.isStrUsable(organization, 'publishedVersion.cover.key') &&
|
||||||
}
|
organization.publishedVersion.cover.key === organization.proposedVersion.cover.key
|
||||||
})
|
)
|
||||||
|
) {
|
||||||
|
MediaService.delete(organization.proposedVersion.cover.key, 'coverDeleted')
|
||||||
}
|
}
|
||||||
|
|
||||||
// format schedule, pricing
|
// format schedule, pricing
|
||||||
|
@ -84,12 +90,7 @@ export default class DelegateController {
|
||||||
data,
|
data,
|
||||||
body: req.body
|
body: req.body
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.tag !== undefined && req.body.tag !== null && req.body.tag.length > 2) {
|
if (req.body.tag !== undefined && req.body.tag !== null && req.body.tag.length > 2) {
|
||||||
|
@ -131,184 +132,79 @@ export default class DelegateController {
|
||||||
organization.email,
|
organization.email,
|
||||||
"L'association \"" + organization.adminName + "\" demande à être vérifé",
|
"L'association \"" + organization.adminName + "\" demande à être vérifé",
|
||||||
"approval",
|
"approval",
|
||||||
{ adminName: organization.adminName, email: organization.email }
|
{
|
||||||
).then(() => {
|
adminName: organization.adminName,
|
||||||
console.log('> A "approval asked" email was sent')
|
email: organization.email,
|
||||||
}).catch(() => {
|
link: EmailService.getWebUiBaseUrl() + '/admin?approval=' + organization._id
|
||||||
console.error('> "approval asked" email failed to be sent')
|
}
|
||||||
})
|
)
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
console.log(err)
|
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uploadThumbnail(): express.RequestHandler[] {
|
static uploadThumbnail(): express.RequestHandler[] {
|
||||||
return [
|
return [
|
||||||
multer({
|
MediaService.multer('thumbnail'),
|
||||||
storage: multerS3({
|
|
||||||
s3: MediaService.getS3(),
|
|
||||||
bucket: 'development-bucket',
|
|
||||||
acl: 'public-read',
|
|
||||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
|
||||||
key: (_: any, file: any, cb: any) => {
|
|
||||||
console.log(file)
|
|
||||||
cb(null, Date.now().toString() + '_thumbnail')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).single('file'),
|
|
||||||
(req: express.Request, res: express.Response) => {
|
(req: express.Request, res: express.Response) => {
|
||||||
// if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
|
// if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
|
||||||
// THEN we delete the current thumbnail
|
// THEN we delete the current thumbnail
|
||||||
let proposedVersion: any = res.locals.organization.proposedVersion
|
let proposedVersion: any = res.locals.organization.proposedVersion
|
||||||
const publishedVersion: any = res.locals.organization.publishedVersion
|
const publishedVersion: any = res.locals.organization.publishedVersion
|
||||||
if (
|
if (
|
||||||
proposedVersion !== undefined && proposedVersion !== null &&
|
Utils.isStrUsable(proposedVersion, 'thumbnail.location') &&
|
||||||
proposedVersion.thumbnail !== undefined && proposedVersion.thumbnail !== null &&
|
Utils.isStrUsable(publishedVersion, 'thumbnail.location') &&
|
||||||
publishedVersion !== undefined && publishedVersion !== null &&
|
|
||||||
publishedVersion.thumbnail !== undefined && publishedVersion.thumbnail !== null &&
|
|
||||||
publishedVersion.thumbnail.location !== proposedVersion.thumbnail.location
|
publishedVersion.thumbnail.location !== proposedVersion.thumbnail.location
|
||||||
) {
|
) {
|
||||||
console.log(' we must delete ', proposedVersion.thumbnail)
|
MediaService.delete(proposedVersion.thumbnail.key, 'thumbnailUpdated')
|
||||||
MediaService.getS3().deleteObject({
|
|
||||||
Bucket: MediaService.getBucket(),
|
|
||||||
Key: proposedVersion.thumbnail.key
|
|
||||||
}, (err, _) => {
|
|
||||||
if (err !== null) {
|
|
||||||
console.error('> Cannot delete a old thumbnail')
|
|
||||||
console.log(err, err.stack)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const thumbnail: any = {
|
|
||||||
// @ts-ignore
|
|
||||||
key: req.file.key,
|
|
||||||
// @ts-ignore
|
|
||||||
contentType: req.file.contentType,
|
|
||||||
// @ts-ignore
|
|
||||||
location: req.file.location,
|
|
||||||
// @ts-ignore
|
|
||||||
size: req.file.size,
|
|
||||||
// @ts-ignore
|
|
||||||
originalFileName: req.file.originalname,
|
|
||||||
type: 'thumbnail'
|
|
||||||
}
|
}
|
||||||
|
const thumbnail: any = MediaService.buildMedia(req.file, 'thumbnail')
|
||||||
proposedVersion = { ...res.locals.organization.proposedVersion, thumbnail }
|
proposedVersion = { ...res.locals.organization.proposedVersion, thumbnail }
|
||||||
Organization.updateOne({ _id: res.locals.organization._id }, {
|
Organization.updateOne({ _id: res.locals.organization._id }, {
|
||||||
proposedVersion,
|
proposedVersion,
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
res.json({ success: true, data, thumbnail, proposedVersion })
|
res.json({ success: true, data, thumbnail, proposedVersion })
|
||||||
}).catch(err => {
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
static uploadCover(): express.RequestHandler[] {
|
static uploadCover(): express.RequestHandler[] {
|
||||||
return [
|
return [
|
||||||
multer({
|
MediaService.multer('cover'),
|
||||||
storage: multerS3({
|
|
||||||
s3: MediaService.getS3(),
|
|
||||||
bucket: 'development-bucket',
|
|
||||||
acl: 'public-read',
|
|
||||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
|
||||||
key: (_: any, file: any, cb: any) => {
|
|
||||||
console.log(file)
|
|
||||||
cb(null, Date.now().toString() + '_cover')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).single('file'),
|
|
||||||
(req: express.Request, res: express.Response) => {
|
(req: express.Request, res: express.Response) => {
|
||||||
// if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
|
// if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
|
||||||
// THEN we delete the current thumbnail
|
// THEN we delete the current thumbnail
|
||||||
let proposedVersion: any = res.locals.organization.proposedVersion
|
let proposedVersion: any = res.locals.organization.proposedVersion
|
||||||
const publishedVersion: any = res.locals.organization.publishedVersion
|
const publishedVersion: any = res.locals.organization.publishedVersion
|
||||||
if (
|
if (
|
||||||
proposedVersion !== undefined && proposedVersion !== null &&
|
Utils.isStrUsable(proposedVersion, 'cover.location') &&
|
||||||
proposedVersion.cover !== undefined && proposedVersion.cover !== null &&
|
Utils.isStrUsable(publishedVersion, 'cover.location') &&
|
||||||
publishedVersion !== undefined && publishedVersion !== null &&
|
|
||||||
publishedVersion.cover !== undefined && publishedVersion.cover !== null &&
|
|
||||||
publishedVersion.cover.location !== proposedVersion.cover.location
|
publishedVersion.cover.location !== proposedVersion.cover.location
|
||||||
) {
|
) {
|
||||||
console.log(' we must delete ', proposedVersion.cover)
|
MediaService.delete(proposedVersion.cover.key, 'coverUpdated')
|
||||||
MediaService.getS3().deleteObject({
|
|
||||||
Bucket: MediaService.getBucket(),
|
|
||||||
Key: proposedVersion.cover.key
|
|
||||||
}, (err, _) => {
|
|
||||||
if (err !== null) {
|
|
||||||
console.error('> Cannot delete a old cover')
|
|
||||||
console.log(err, err.stack)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const cover: any = {
|
|
||||||
// @ts-ignore
|
|
||||||
key: req.file.key,
|
|
||||||
// @ts-ignore
|
|
||||||
contentType: req.file.contentType,
|
|
||||||
// @ts-ignore
|
|
||||||
location: req.file.location,
|
|
||||||
// @ts-ignore
|
|
||||||
size: req.file.size,
|
|
||||||
// @ts-ignore
|
|
||||||
originalFileName: req.file.originalname,
|
|
||||||
type: 'cover'
|
|
||||||
}
|
}
|
||||||
|
const cover: any = MediaService.buildMedia(req.file, 'cover')
|
||||||
proposedVersion = { ...res.locals.organization.proposedVersion, cover }
|
proposedVersion = { ...res.locals.organization.proposedVersion, cover }
|
||||||
Organization.updateOne({ _id: res.locals.organization._id }, {
|
Organization.updateOne({ _id: res.locals.organization._id }, { proposedVersion, updatedAt: new Date() })
|
||||||
proposedVersion,
|
.then(data => res.json({ success: true, data, cover, proposedVersion }))
|
||||||
updatedAt: new Date()
|
.catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
}).then(data => {
|
|
||||||
res.json({ success: true, data, cover, proposedVersion })
|
|
||||||
}).catch(err => {
|
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
static uploadMedias(): express.RequestHandler[] {
|
static uploadMedias(): express.RequestHandler[] {
|
||||||
return [
|
return [
|
||||||
multer({
|
MediaService.multer('media', true),
|
||||||
storage: multerS3({
|
|
||||||
s3: MediaService.getS3(),
|
|
||||||
bucket: 'development-bucket',
|
|
||||||
acl: 'public-read',
|
|
||||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
|
||||||
key: (_: any, file: any, cb: any) => {
|
|
||||||
console.log(file)
|
|
||||||
cb(null, Date.now().toString() + '_media')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).array('file'),
|
|
||||||
(req: express.Request, res: express.Response) => {
|
(req: express.Request, res: express.Response) => {
|
||||||
let proposedVersion: any = res.locals.organization.proposedVersion
|
let proposedVersion: any = res.locals.organization.proposedVersion
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
req.files.forEach((file: any) => {
|
req.files.forEach((file: any) => {
|
||||||
proposedVersion.gallery.push({
|
proposedVersion.gallery.push(MediaService.buildMedia(file, 'media'))
|
||||||
key: file.key,
|
|
||||||
contentType: file.contentType,
|
|
||||||
location: file.location,
|
|
||||||
size: file.size,
|
|
||||||
type: 'image',
|
|
||||||
originalFileName: file.originalname
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Organization.updateOne({ _id: res.locals.organization._id }, {
|
Organization.updateOne({ _id: res.locals.organization._id }, {
|
||||||
|
@ -322,12 +218,7 @@ export default class DelegateController {
|
||||||
gallery: proposedVersion.gallery
|
gallery: proposedVersion.gallery
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
errors: err.errors !== undefined ? err.errors : err
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,19 @@ export default class PublicController {
|
||||||
res.render('home.twig', {
|
res.render('home.twig', {
|
||||||
tags,
|
tags,
|
||||||
tagsJSON: JSON.stringify(tags),
|
tagsJSON: JSON.stringify(tags),
|
||||||
organizationsJSON: JSON.stringify(organizations.map(o => {
|
organizationsJSON: JSON.stringify(organizations
|
||||||
const version = o.get('proposedVersion')
|
.filter(o => o.get('publishedAt') !== undefined && o.get('publishedAt') !== null)
|
||||||
return {
|
.map(o => {
|
||||||
name: version.name,
|
const version = o.get('publishedVersion')
|
||||||
description: version.descriptionShort,
|
return {
|
||||||
thumbnail: version.thumbnail.location,
|
name: version.name,
|
||||||
tag: version.tag._id,
|
description: version.descriptionShort,
|
||||||
slug: o.get('slug')
|
thumbnail: version.thumbnail.location,
|
||||||
}
|
tag: version.tag._id,
|
||||||
}))
|
slug: o.get('slug')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -40,9 +43,15 @@ 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) {
|
||||||
res.status(404).render('not-found.twig')
|
return res.status(404).render('not-found.twig')
|
||||||
} else {
|
} else {
|
||||||
const version = data[0].get('proposedVersion')
|
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')
|
||||||
|
}
|
||||||
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') {
|
||||||
version.contacts.address = version.contacts.address.split('\n')
|
version.contacts.address = version.contacts.address.split('\n')
|
||||||
|
@ -65,7 +74,6 @@ export default class PublicController {
|
||||||
version.contacts.phoneSplit = phoneSplit
|
version.contacts.phoneSplit = phoneSplit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(version)
|
|
||||||
res.render('organization.twig', {
|
res.render('organization.twig', {
|
||||||
layout: 'standalone',
|
layout: 'standalone',
|
||||||
data: version
|
data: version
|
||||||
|
|
|
@ -85,9 +85,9 @@ const Organization = new Schema({
|
||||||
validationState: {
|
validationState: {
|
||||||
type: AllowedString,
|
type: AllowedString,
|
||||||
required: true,
|
required: true,
|
||||||
default: 'none',
|
default: 'unaware',
|
||||||
name: 'ValidationState',
|
name: 'ValidationState',
|
||||||
allowedValues: ['none', 'pending', 'rejected', 'published']
|
allowedValues: ['unaware', 'pending', 'rejected', 'published']
|
||||||
},
|
},
|
||||||
rejectionDescription: { type: String },
|
rejectionDescription: { type: String },
|
||||||
proposedVersion: OrganizationVersion,
|
proposedVersion: OrganizationVersion,
|
||||||
|
|
5
src/test.ts
Normal file
5
src/test.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import Utils from './Utils'
|
||||||
|
|
||||||
|
console.log(Utils.isUsable({
|
||||||
|
medias: null
|
||||||
|
}, 'medias'))
|
|
@ -236,6 +236,17 @@ section {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description blockquote {
|
||||||
|
border-left: 3px solid #95a5a6;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description blockquote p {
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
/* *****************************************************************************
|
/* *****************************************************************************
|
||||||
|
|
||||||
* SCHEDULE
|
* SCHEDULE
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<p>Bonjour,</p>
|
<p>Bonjour,</p>
|
||||||
|
|
||||||
<p>L'association {{ adminName }} vient de proposer de nouveau changements à propos de son association sur la platforme.</p>
|
<p>L'association "{{ adminName }}" vient de proposer de nouveau changements à propos de son association sur la platforme.</p>
|
||||||
|
|
||||||
<p>L'email associé à cette association est le suivant : <pre>{{ email }}</pre></p>
|
<p>L'email associé à cette association est {{ email }}</p>
|
||||||
|
|
||||||
<p>On vous invite à valider (ou rejeter) tout ça !</p>
|
<p>On vous invite à valider (ou rejeter) tout ça !</p>
|
||||||
|
|
||||||
<p>Coordialement</p>
|
<p><a href="{{ link }}">Lien pour consulter les changements</a></p>
|
||||||
|
|
||||||
|
<p>Coordialement</p>
|
||||||
|
|
9
views/emails/delegateFooter.twig
Normal file
9
views/emails/delegateFooter.twig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Vous recevez cet email car cette adresse email a été designé comme étant celle qui doit recevoir les messages concernant l'association "{{ adminName }}" sur la platforme du Forum des associations 2020 mise en place par l'Espace Condorcet Centre Social.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Cet email est un message automatique, veuillez à ne pas répondre.
|
||||||
|
</p>
|
13
views/emails/published.twig
Normal file
13
views/emails/published.twig
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<p>Bonjour,</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Nous vous informons que les changements qui ont été soumis à verification concernant l'association "{{ adminName }}" viennent d'être publiés. Félicitations !
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Nous vous invitons à consulter la page web générée à partir des informations que vous avez soumises : <a href="{{ link }}">{{ link }}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Coordialement</p>
|
||||||
|
|
||||||
|
{% include 'views/emails/delegateFooter.twig' %}
|
|
@ -1,9 +1,5 @@
|
||||||
<p>Bonjour,</p>
|
<p>Bonjour,</p>
|
||||||
|
|
||||||
<p>
|
|
||||||
Vous recevez cet email car cette adresse email a été designé comme étant celle qui doit recevoir les notifications concernant l'association "{{ adminName }}" sur la platforme du Forum des associations 2020 mise en place par l'Espace Condorcet Centre Social.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Nous vous informons que les changements qui ont été soumis à verification concernant l'association "{{ adminName }}" ne peuvent pas en l'état être publiées.
|
Nous vous informons que les changements qui ont été soumis à verification concernant l'association "{{ adminName }}" ne peuvent pas en l'état être publiées.
|
||||||
</p>
|
</p>
|
||||||
|
@ -12,12 +8,14 @@ Nous vous informons que les changements qui ont été soumis à verification con
|
||||||
La personne vérifiant ces modifications a laissé un message indiquant la raison pour laquelle ces modifications ne peuvent pas être publiés :
|
La personne vérifiant ces modifications a laissé un message indiquant la raison pour laquelle ces modifications ne peuvent pas être publiés :
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
{{ rejectionDescription }}
|
{{ rejectionDescription }}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
N'hésitez pas à corriger les informations et à resoumettre pour une nouvelle verification et peut être cette fois ci vous serez publiés...
|
N'hésitez pas à corriger les informations et à resoumettre pour une nouvelle verification et peut être cette fois ci vous serez publié...
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -26,3 +24,4 @@ Pour rappel, vous pouvez utiliser ce lien pour vous connecter sur votre interfac
|
||||||
|
|
||||||
<p>Coordialement</p>
|
<p>Coordialement</p>
|
||||||
|
|
||||||
|
{% include 'views/emails/delegateFooter.twig' %}
|
||||||
|
|
|
@ -11,3 +11,5 @@ Vous pouvez utilisez ce lien pour vous connecter sur l'interface web permettant
|
||||||
<p>Voici votre clée: {{ token }}</p>
|
<p>Voici votre clée: {{ token }}</p>
|
||||||
|
|
||||||
<p>Coordialement</p>
|
<p>Coordialement</p>
|
||||||
|
|
||||||
|
{% include 'views/emails/delegateFooter.twig' %}
|
||||||
|
|
Loading…
Reference in a new issue