diff --git a/.env.development b/.env.development index 7013049..076722d 100644 --- a/.env.development +++ b/.env.development @@ -1,12 +1,12 @@ -DATABASE=test_FreePlanet +DATABASE=test_PiuCheBuono UDB=paofreeplanet PDB=mypassword@1A SEND_EMAIL=0 SEND_EMAIL_ORDERS=1 PORT=3000 -appTelegram_TEST=["1","13"] -appTelegram=["1","13"] -appTelegram_DEVELOP=["13"] +appTelegram_TEST=["1","17"] +appTelegram=["1","17"] +appTelegram_DEVELOP=["17"] DOMAIN=mongodb://localhost:27017/ AUTH_MONGODB=0 ENABLE_PUSHNOTIFICATION=1 @@ -29,7 +29,7 @@ GCM_API_KEY="" PROD=0 PROJECT_DESCR_MAIN='__PROJECTS' SECRK=Askb38v23jjDFaoskBOWj92axXCQ -TOKEN_LIFE=1m +TOKEN_LIFE=2h REFRESH_TOKEN_LIFE=14d FTPSERVER_HOST=139.162.166.31 FTPSERVER_PORT=21 @@ -38,4 +38,9 @@ FTPSERVER_PWD=ftpmypwd@1A_ AUTH_NEW_SITES=123123123 SCRIPTS_DIR=admin_scripts CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}] +MIAB_HOST=box.lamiaposta.org +MIAB_ADMIN_EMAIL=admin@lamiaposta.org +MIAB_ADMIN_PASSWORD=passpao1pabox@1A DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7" +SERVER_A_URL="http://51.77.156.69:3000" +API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK" \ No newline at end of file diff --git a/package.json b/package.json index f299662..53c1ad0 100755 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "express": "^4.21.2", "formidable": "^3.5.2", "ghostscript4js": "^3.2.3", + "helmet": "^8.1.0", "i18n": "^0.15.1", "image-downloader": "^4.3.0", "internet-available": "^1.0.0", @@ -42,6 +43,7 @@ "lodash": "^4.17.21", "mongodb": "^6.14.2", "mongoose": "^8.12.1", + "morgan": "^1.10.1", "multer": "^1.4.5-lts.2", "mysql": "^2.18.1", "node-cron": "^3.0.3", diff --git a/src/server/controllers/events.controller.js b/src/server/controllers/events.controller.js new file mode 100644 index 0000000..a4e6da7 --- /dev/null +++ b/src/server/controllers/events.controller.js @@ -0,0 +1,46 @@ +// @ts-check +const EventModel = require('../models/Event'); +const { pick } = require('../utils/pick'); + +async function listEvents(req, res) { + const { limit = '6', page = '1', from, to, sort = 'start' } = /** @type {any} */ (req.query); + const q = {}; + if (from) q.start = { ...(q.start || {}), $gte: new Date(from) }; + if (to) q.start = { ...(q.start || {}), $lte: new Date(to) }; + + const lim = Math.min(Number(limit) || 6, 50); + const skip = (Math.max(Number(page) || 1, 1) - 1) * lim; + + const [items, total] = await Promise.all([ + EventModel.find(q) + .sort({ [sort]: 1 }) + .skip(skip) + .limit(lim) + .lean(), + EventModel.countDocuments(q), + ]); + + res.set('Cache-Control', 'public, max-age=30, stale-while-revalidate=120'); + return res.json({ items, total, page: Number(page), limit: lim }); +} + +async function createEvent(req, res) { + const data = pick(req.body, ['title', 'start', 'end', 'place', 'teaser', 'cover', 'to']); + const doc = await EventModel.create(data); + return res.status(201).json(doc); +} + +async function updateEvent(req, res) { + const data = pick(req.body, ['title', 'start', 'end', 'place', 'teaser', 'cover', 'to']); + const doc = await EventModel.findByIdAndUpdate(req.params.id, data, { new: true }); + if (!doc) return res.status(404).json({ message: 'Evento non trovato' }); + return res.json(doc); +} + +async function deleteEvent(req, res) { + const r = await EventModel.findByIdAndDelete(req.params.id); + if (!r) return res.status(404).json({ message: 'Evento non trovato' }); + return res.status(204).send(); +} + +module.exports = { listEvents, createEvent, updateEvent, deleteEvent }; diff --git a/src/server/controllers/home.controller.js b/src/server/controllers/home.controller.js new file mode 100644 index 0000000..28b0da8 --- /dev/null +++ b/src/server/controllers/home.controller.js @@ -0,0 +1,18 @@ +// @ts-check +const { HomeModel } = require('../models/Home'); + +async function getHome(req, res) { + const doc = await HomeModel.findOne({}); + if (!doc) return res.status(404).json({ message: 'Home non configurata' }); + res.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=300'); + res.set('ETag', `"${doc.updatedAt?.getTime?.() || Date.now()}"`); + return res.json(doc); +} + +async function upsertHome(req, res) { + const payload = req.body || {}; + const doc = await HomeModel.findOneAndUpdate({}, payload, { upsert: true, new: true, setDefaultsOnInsert: true }); + return res.json(doc); +} + +module.exports = { getHome, upsertHome }; \ No newline at end of file diff --git a/src/server/controllers/newsletter.controller.js b/src/server/controllers/newsletter.controller.js new file mode 100644 index 0000000..714f01e --- /dev/null +++ b/src/server/controllers/newsletter.controller.js @@ -0,0 +1,20 @@ +// @ts-check +async function subscribe(req, res) { + const { email } = req.body || {}; + if (!email || !/.+@.+\..+/.test(email)) { + return res.status(400).json({ message: 'Email non valida' }); + } + // Integra qui Mailchimp/Sendinblue se necessario + return res.status(200).json({ ok: true }); +} + +async function unsubscribe(req, res) { + const { email } = req.body || {}; + if (!email || !/.+@.+\..+/.test(email)) { + return res.status(400).json({ message: 'Email non valida' }); + } + // Integra qui Mailchimp/Sendinblue se necessario + return res.status(200).json({ ok: true }); +} + +module.exports = { subscribe, unsubscribe }; \ No newline at end of file diff --git a/src/server/controllers/posts.controller.js b/src/server/controllers/posts.controller.js new file mode 100644 index 0000000..29789e5 --- /dev/null +++ b/src/server/controllers/posts.controller.js @@ -0,0 +1,41 @@ +// @ts-check +const { PostModel } = require('../models/Post'); +const { pick } = require('../utils/pick'); + + async function listPosts(req, res) { + const { limit = '3', page = '1', sort = '-date', category } = /** @type {any} */ (req.query); + const q = {}; + if (category) q.category = category; + + const lim = Math.min(Number(limit) || 3, 50); + const skip = (Math.max(Number(page) || 1, 1) - 1) * lim; + + const [items, total] = await Promise.all([ + PostModel.find(q).sort(sort).skip(skip).limit(lim).lean(), + PostModel.countDocuments(q) + ]); + + res.set('Cache-Control', 'public, max-age=30, stale-while-revalidate=120'); + return res.json({ items, total, page: Number(page), limit: lim }); +} + + async function createPost(req, res) { + const data = pick(req.body, ['title','date','category','teaser','cover','to','bodyMd']); + const doc = await PostModel.create(data); + return res.status(201).json(doc); +} + + async function updatePost(req, res) { + const data = pick(req.body, ['title','date','category','teaser','cover','to','bodyMd']); + const doc = await PostModel.findByIdAndUpdate(req.params.id, data, { new: true }); + if (!doc) return res.status(404).json({ message: 'Post non trovato' }); + return res.json(doc); +} + + async function deletePost(req, res) { + const r = await PostModel.findByIdAndDelete(req.params.id); + if (!r) return res.status(404).json({ message: 'Post non trovato' }); + return res.status(204).send(); +} + +module.exports = { listPosts, createPost, updatePost, deletePost }; \ No newline at end of file diff --git a/src/server/db/connect.js b/src/server/db/connect.js new file mode 100644 index 0000000..6592e3a --- /dev/null +++ b/src/server/db/connect.js @@ -0,0 +1,10 @@ +// @ts-check +const mongoose = require('mongoose'); +const { env } = require('../config/env'); + +export async function connectDB() { + if (mongoose.connection.readyState === 1) return; + mongoose.set('strictQuery', true); + await mongoose.connect(env.MONGO_URI); + console.log('[mongo] connected'); +} diff --git a/src/server/db/seed.js b/src/server/db/seed.js new file mode 100644 index 0000000..15bfe17 --- /dev/null +++ b/src/server/db/seed.js @@ -0,0 +1,59 @@ +const fs = require('fs'); +const path = require('path'); +const url = require('url'); +// const { connectDB } = require('./connect'); +const { HomeModel } = require('../models/Home'); +const { EventModel } = require('../models/Event'); +const { PostModel } = require('../models/Post'); + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +async function run() { + // await connectDB(); + + // Adatta il percorso se frontend e backend sono separati + const samplePath = path.resolve(__dirname, '../../../src/server/mocks/home.sample.json'); + const raw = fs.readFileSync(samplePath, 'utf-8'); + const json = JSON.parse(raw); + + // Home snapshot + await HomeModel.deleteMany({}); + await HomeModel.create({ + hero: json.hero, + pillars: json.pillars, + testimonials: json.testimonials, + gallery: json.gallery, + faq: json.faq, + partners: json.partners, + posts: json.posts + }); + + // Events + await EventModel.deleteMany({}); + await EventModel.insertMany(json.events.map((e) => ({ + title: e.title, + start: new Date(e.start), + end: e.end ? new Date(e.end) : undefined, + place: e.place, + teaser: e.teaser, + cover: e.cover, + to: e.to + }))); + + // Posts + await PostModel.deleteMany({}); + await PostModel.insertMany(json.posts.map((p) => ({ + title: p.title, + date: new Date(p.date), + category: p.category, + teaser: p.teaser, + cover: p.cover, + to: p.to, + bodyMd: `# ${p.title}\n\n${p.teaser}\n` + }))); + + console.log('[seed] done'); + process.exit(0); +} + +run().catch((e) => { console.error(e); process.exit(1); }); diff --git a/src/server/middleware/asyncHandler.js b/src/server/middleware/asyncHandler.js new file mode 100644 index 0000000..1a80f55 --- /dev/null +++ b/src/server/middleware/asyncHandler.js @@ -0,0 +1,6 @@ +// @ts-check +const ah = (fn) => (req, res, next) => + Promise.resolve(fn(req, res, next)).catch((err) => next(err)); + + +module.exports = { ah }; diff --git a/src/server/middleware/error.js b/src/server/middleware/error.js new file mode 100644 index 0000000..37776ac --- /dev/null +++ b/src/server/middleware/error.js @@ -0,0 +1,13 @@ +// @ts-check +function notFound(_req, res) { + res.status(404).json({ message: 'Not Found' }); +} + +function errorHandler(err, _req, res, _next) { + const status = err.status || 500; + const message = err.message || 'Server Error'; + if (status >= 500) console.error(err); + res.status(status).json({ message }); +} + +module.exports = { notFound, errorHandler }; diff --git a/src/server/middleware/rateLimit.js b/src/server/middleware/rateLimit.js new file mode 100644 index 0000000..ef587c7 --- /dev/null +++ b/src/server/middleware/rateLimit.js @@ -0,0 +1,21 @@ +// @ts-check +const buckets = new Map(); +/** 10 secondi */ +const WINDOW_MS = 10_000; +const LIMIT = 100; + +function rateLimit(req, res, next) { + const key = req.ip || 'global'; + const now = Date.now(); + const bucket = buckets.get(key) || { count: 0, ts: now }; + if (now - bucket.ts > WINDOW_MS) { + bucket.count = 0; + bucket.ts = now; + } + bucket.count++; + buckets.set(key, bucket); + if (bucket.count > LIMIT) return res.status(429).json({ message: 'Troppo traffico, riprova tra poco.' }); + next(); +} + +module.exports = { rateLimit }; diff --git a/src/server/models/Event.js b/src/server/models/Event.js new file mode 100644 index 0000000..36ee77e --- /dev/null +++ b/src/server/models/Event.js @@ -0,0 +1,17 @@ +// @ts-check +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const EventSchema = new Schema({ + title: { type: String, required: true }, + start: { type: Date, required: true, index: true }, + end: { type: Date }, + place: String, + teaser: String, + cover: String, + to: String, + createdAt: { type: Date, default: Date.now } +}, { collection: 'events', versionKey: false }); + +var EventModel = module.exports = mongoose.model('Event', EventSchema); + diff --git a/src/server/models/Home.js b/src/server/models/Home.js new file mode 100644 index 0000000..eb10916 --- /dev/null +++ b/src/server/models/Home.js @@ -0,0 +1,63 @@ +// @ts-check +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +mongoose.Promise = global.Promise; +mongoose.level = "F"; + + +const HeroSchema = new Schema({ + title: { type: String, required: true }, + subtitle: String, + badge: String, + mediaUrl: { type: String, required: true }, + ctas: [{ label: String, to: String, href: String }] +}, { _id: false }); + +const PillarSchema = new Schema({ + id: { type: String, required: true }, + icon: { type: String, required: true }, + title: { type: String, required: true }, + excerpt: String, + to: String +}, { _id: false }); + +const TestimonialSchema = new Schema({ + id: String, quote: String, author: String, role: String, avatar: String +}, { _id: false }); + +const GallerySchema = new Schema({ + id: String, src: String, alt: String +}, { _id: false }); + +const FaqSchema = new Schema({ + q: String, a: String +}, { _id: false }); + +const PostLiteSchema = new Schema({ + id: String, title: String, date: String, category: String, teaser: String, cover: String, to: String +}, { _id: false }); + +const PartnerSchema = new Schema({ + id: String, name: String, logo: String, href: String +}, { _id: false }); + +const HomeSchema = new Schema({ + hero: { type: HeroSchema, required: true }, + pillars: { type: [PillarSchema], default: [] }, + testimonials: { type: [TestimonialSchema], default: [] }, + gallery: { type: [GallerySchema], default: [] }, + faq: { type: [FaqSchema], default: [] }, + posts: { type: [PostLiteSchema], default: [] }, + partners: { type: [PartnerSchema], default: [] }, + meta: { title: String, description: String, ogImage: String }, + updatedAt: { type: Date, default: Date.now } +}, { collection: 'home', versionKey: false }); + +HomeSchema.pre('save', function(next) { + this.set('updatedAt', new Date()); + next(); +}); + + +var HomeModel = module.exports = mongoose.model('Home', HomeSchema); diff --git a/src/server/models/Post.js b/src/server/models/Post.js new file mode 100644 index 0000000..df2b12c --- /dev/null +++ b/src/server/models/Post.js @@ -0,0 +1,16 @@ +// @ts-check +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const PostSchema = new Schema({ + title: { type: String, required: true }, + date: { type: Date, required: true, index: true }, + category: String, + teaser: String, + cover: String, + to: String, + bodyMd: String, + createdAt: { type: Date, default: Date.now } +}, { collection: 'posts', versionKey: false }); + +var PostModel = module.exports = mongoose.model('Post', PostSchema); diff --git a/src/server/models/catprod.js b/src/server/models/catprod.js index 8c616ae..7401f86 100755 --- a/src/server/models/catprod.js +++ b/src/server/models/catprod.js @@ -121,7 +121,7 @@ CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp, updateda input: "$products", as: "prod", cond: { - $in: ["$$prod.idStatoProdotto", [1, 4, 34, 45, 46]] + $in: ["$$prod.productInfo.idStatoProdotto", [1, 4, 34, 45, 46]] } } } @@ -152,6 +152,8 @@ CatProdSchema.statics.getCatProdWithTitleCount = async function (idapp, updateda const result = await CatProd.aggregate(myquery); + // console.log(JSON.stringify(myquery, null, 2)) + if (updatedata) { for (const record of result) { await CatProd.updateOne( diff --git a/src/server/models/collana.js b/src/server/models/collana.js index 8dc30cf..67be4f7 100755 --- a/src/server/models/collana.js +++ b/src/server/models/collana.js @@ -76,7 +76,7 @@ module.exports.getCollaneWithTitleCount = async function (idapp, updatedata) { input: "$products", as: "prod", cond: { - $in: ["$$prod.idStatoProdotto", [1, 4, 34, 45, 46]] + $in: ["$$prod.productInfo.idStatoProdotto", [1, 4, 34, 45, 46]] } } } diff --git a/src/server/models/product.js b/src/server/models/product.js index 59a3390..440a031 100755 --- a/src/server/models/product.js +++ b/src/server/models/product.js @@ -977,14 +977,6 @@ module.exports.getArrCatProds = async function (idapp, cosa) { let myquery = [ ...addquery, - { - $lookup: { - from: 'productinfos', - localField: 'idProductInfo', - foreignField: '_id', - as: 'productInfo', - }, - }, { $lookup: { from: 'catprods', @@ -1015,6 +1007,10 @@ module.exports.getArrCatProds = async function (idapp, cosa) { let arr = []; arr = await Product.aggregate(myquery); + if (cosa !== shared_consts.PROD.GAS) { + // console.log(JSON.stringify(myquery, null, 2)) + } + // arr = result.map(category => category.name); return arr; } catch (e) { diff --git a/src/server/models/publisher.js b/src/server/models/publisher.js index 893ac2c..2a5446c 100755 --- a/src/server/models/publisher.js +++ b/src/server/models/publisher.js @@ -68,7 +68,7 @@ module.exports.getEditoriWithTitleCount = async function (idapp, updatedata) { input: "$products", as: "prod", cond: { - $in: ["$$prod.idStatoProdotto", [1, 4, 34, 45, 46]] + $in: ["$$prod.productInfo.idStatoProdotto", [1, 4, 34, 45, 46]] } } } diff --git a/src/server/modules/Macro.js b/src/server/modules/Macro.js index 8a3028a..c4a8645 100644 --- a/src/server/modules/Macro.js +++ b/src/server/modules/Macro.js @@ -721,7 +721,7 @@ class Macro { img3: product.img3 || undefined, img4: product.img4 || undefined, checkout_link: product.checkout_link || undefined, - idStatoProdotto: product.idStatoProdotto || undefined, + idStatoProdotto: product.productInfo.idStatoProdotto || undefined, date_pub: product.date_pub || undefined, sottotitolo: product.sottotitolo || undefined, ...(product.date_updated_fromGM ? { date_updated_fromGM: product.date_updated_fromGM } : {}), diff --git a/src/server/modules/cloudflare.js b/src/server/modules/cloudflare.js index a4e5b67..753308e 100644 --- a/src/server/modules/cloudflare.js +++ b/src/server/modules/cloudflare.js @@ -2,7 +2,7 @@ const axios = require('axios'); const apiUrl = 'https://api.cloudflare.com/client/v4'; // Endpoint -const MailinaboxClass = require('./Mailinabox.js'); +const MailinaboxClass = require('./Mailinabox'); class CloudFlare { constructor(config) { diff --git a/src/server/router/admin_router.js b/src/server/router/admin_router.js index 74bc8d8..428ca3d 100755 --- a/src/server/router/admin_router.js +++ b/src/server/router/admin_router.js @@ -33,8 +33,6 @@ const { RaccoltaCataloghi } = require('../models/raccoltacataloghi'); const server_constants = require('../tools/server_constants'); -const { ImageDownloader } = require('../tools/general.js'); - const path = require('path'); const gs = require('ghostscript4js'); @@ -2351,7 +2349,7 @@ router.post('/cloudflare', authenticate, async (req, res) => { record = req.body.record; // console.log('/cloudflare idapp=', idapp, req.body.script); - const CloudFlareClass = require('../modules/Cloudflare.js'); + const CloudFlareClass = require('../modules/Cloudflare'); const TOKCHECK = 'php8.1_version_762321HSD121nJDokq@?!aFS.tar.gz'; @@ -2419,7 +2417,7 @@ router.post('/miab', authenticate, async (req, res) => { record = req.body.record; tokcheck = req.body.tokcheck; - const MailinaboxClass = require('../modules/Mailinabox.js'); + const MailinaboxClass = require('../modules/Mailinabox'); const TOKCHECK = 'php8.1_version_762321HSD121nJDokq@?!aFS.tar.gz'; diff --git a/src/server/routes/events.routes.js b/src/server/routes/events.routes.js new file mode 100644 index 0000000..565ca90 --- /dev/null +++ b/src/server/routes/events.routes.js @@ -0,0 +1,12 @@ +// @ts-check +const express = require('express'); +const router = express.Router(); +const { ah } = require('../middleware/asyncHandler'); +const { listEvents, createEvent, updateEvent, deleteEvent } = require('../controllers/events.controller'); + +router.get('/', ah(listEvents)); +router.post('/', ah(createEvent)); +router.put('/:id', ah(updateEvent)); +router.delete('/:id', ah(deleteEvent)); + +module.exports = router; \ No newline at end of file diff --git a/src/server/routes/home.routes.js b/src/server/routes/home.routes.js new file mode 100644 index 0000000..c1b62a6 --- /dev/null +++ b/src/server/routes/home.routes.js @@ -0,0 +1,10 @@ +// @ts-check +const express = require('express'); +const router = express.Router(); +const { ah } = require('../middleware/asyncHandler'); +const { getHome, upsertHome } = require('../controllers/home.controller'); + +router.get('/', ah(getHome)); +router.put('/', ah(upsertHome)); // proteggi con auth in prod + +module.exports = router; \ No newline at end of file diff --git a/src/server/routes/index-aa.js b/src/server/routes/index-aa.js new file mode 100644 index 0000000..93d1e79 --- /dev/null +++ b/src/server/routes/index-aa.js @@ -0,0 +1,14 @@ +const express = require('express'); +const router = express.Router(); + +const homeRouter = require('./home.routes'); +const eventsRouter = require('./events.routes'); +const postsRouter = require('./posts.routes'); +const newsletterRouter = require('./newsletter.routes'); + +router.use('/home', homeRouter); +router.use('/events', eventsRouter); +router.use('/posts', postsRouter); +router.use('/newsletter', newsletterRouter); + +module.exports = router; \ No newline at end of file diff --git a/src/server/routes/newsletter.routes.js b/src/server/routes/newsletter.routes.js new file mode 100644 index 0000000..9d25631 --- /dev/null +++ b/src/server/routes/newsletter.routes.js @@ -0,0 +1,9 @@ +// @ts-check +const express = require('express'); +const router = express.Router(); +const { ah } = require('../middleware/asyncHandler'); +const { subscribe } = require('../controllers/newsletter.controller'); + +router.post('/subscribe', ah(subscribe)); + +module.exports = router; \ No newline at end of file diff --git a/src/server/routes/posts.routes.js b/src/server/routes/posts.routes.js new file mode 100644 index 0000000..cd3e917 --- /dev/null +++ b/src/server/routes/posts.routes.js @@ -0,0 +1,12 @@ +// @ts-check +const express = require('express'); +const router = express.Router(); +const { ah } = require('../middleware/asyncHandler'); +const { listPosts, createPost, updatePost, deletePost } = require('../controllers/posts.controller'); + +router.get('/', ah(listPosts)); +router.post('/', ah(createPost)); +router.put('/:id', ah(updatePost)); +router.delete('/:id', ah(deletePost)); + +module.exports = router; \ No newline at end of file diff --git a/src/server/server.js b/src/server/server.js index 38f430a..9dafa4a 100755 --- a/src/server/server.js +++ b/src/server/server.js @@ -21,6 +21,12 @@ var http = require('http'); const WebSocket = require('ws'); const { spawn } = require('child_process'); +const helmet = require('helmet'); +const morgan = require('morgan'); +const apiRouter = require('./routes/index-aa'); +const { notFound, errorHandler } = require('./middleware/error'); +const rateLimit = require('./middleware/rateLimit').rateLimit; + const NUOVO_METODO_TEST = true; const METODO_MULTI_CORS = true; @@ -298,10 +304,7 @@ connectToDatabase(connectionUrl, options) } else { let msgerr = '❌ ERRORE! la decrittazione non funziona! '; console.error(msgerr); - await telegrambot.sendMsgTelegramToTheAdminAllSites( - msgerr, - false - ); + await telegrambot.sendMsgTelegramToTheAdminAllSites(msgerr, false); } await inizia(); @@ -867,9 +870,21 @@ connectToDatabase(connectionUrl, options) } function setupMiddleware(app, corsOptions, isDebug = false) { + app.use(helmet()); app.use(cors(corsOptions)); app.set('trust proxy', true); app.use(express.json()); + + app.use(morgan('dev')); + app.use(rateLimit); + + app.get('/health', (_req, res) => res.json({ ok: true })); + + app.use('/apinew', apiRouter); + + // app.use(notFound); + app.use(errorHandler); + app.options('*', cors(corsOptions)); if (isDebug) { @@ -1058,8 +1073,6 @@ connectToDatabase(connectionUrl, options) process.exit(1); // Termina il processo se non riesce a connettersi }); -function add_numbers(a, b) { - - } +function add_numbers(a, b) {} module.exports = { app }; diff --git a/src/server/utils/pick.js b/src/server/utils/pick.js new file mode 100644 index 0000000..da70f4b --- /dev/null +++ b/src/server/utils/pick.js @@ -0,0 +1,7 @@ +// @ts-check +export function pick(obj, keys) { + /** @type {any} */ + const out = {}; + for (const k of keys) if (k in obj) out[k] = obj[k]; + return out; +} diff --git a/src/server/utils/sanitize.js b/src/server/utils/sanitize.js new file mode 100644 index 0000000..d0035e3 --- /dev/null +++ b/src/server/utils/sanitize.js @@ -0,0 +1,6 @@ +// @ts-check +export function sanitizeString(v, max = 5000) { + if (typeof v !== 'string') return ''; + const s = v.replace(/\u0000/g, '').trim(); + return s.slice(0, max); +} diff --git a/src/server/version.txt b/src/server/version.txt index 88292fb..ca9ab89 100644 --- a/src/server/version.txt +++ b/src/server/version.txt @@ -1 +1 @@ -1.2.69 \ No newline at end of file +1.2.70 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7291709..fbe5184 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1733,6 +1733,13 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + basic-ftp@^5.0.2, basic-ftp@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" @@ -2833,7 +2840,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -4336,6 +4343,11 @@ he@1.2.0, he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +helmet@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-8.1.0.tgz#f96d23fedc89e9476ecb5198181009c804b8b38c" + integrity sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg== + hexoid@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-2.0.0.tgz#fb36c740ebbf364403fa1ec0c7efd268460ec5b9" @@ -6335,6 +6347,17 @@ moo@^0.5.1: resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== +morgan@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.1.tgz#4e02e6a4465a48e26af540191593955d17f61570" + integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.1.0" + mpath@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" @@ -6723,6 +6746,11 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"