- aggiunto componenti per Home Template... ma ancora da provare

- sistemato catprods
- Sistemato menu
This commit is contained in:
Surya Paolo
2025-09-22 19:09:02 +02:00
parent 4a05ddee50
commit 08cf4b6d9f
31 changed files with 475 additions and 31 deletions

View File

@@ -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"

View File

@@ -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",

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

10
src/server/db/connect.js Normal file
View File

@@ -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');
}

59
src/server/db/seed.js Normal file
View File

@@ -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); });

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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);

63
src/server/models/Home.js Normal file
View File

@@ -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);

16
src/server/models/Post.js Normal file
View File

@@ -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);

View File

@@ -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(

View File

@@ -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]]
}
}
}

View File

@@ -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) {

View File

@@ -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]]
}
}
}

View File

@@ -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 } : {}),

View File

@@ -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) {

View File

@@ -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';

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 };

7
src/server/utils/pick.js Normal file
View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -1 +1 @@
1.2.69
1.2.70

View File

@@ -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"