- MultiDominio: api.riso.app, api.gruppomacro.app

This commit is contained in:
Surya Paolo
2024-07-23 12:25:10 +02:00
parent 017ac82d8b
commit 21862f87a1
17 changed files with 484 additions and 97 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -26,10 +26,11 @@ PATH_CERT_KEY=key.pem
PATH_SERVER_CRT=cert.pem
PATH_SSL_ROOT_PEM=root.pem
PATH_SSL_CHAIN_PEM=chain.pem
PROD=0
PROD=1
PROJECT_DESCR_MAIN='__PROJECTS'
SECRK=iUUb38v23jjDFaosWj92axkBOXCQ
SECRTK=jAxKm02emx5SeJvz2IGmtRf6YqCgope
TOKEN_LIFE=2h
REFRESH_TOKEN_LIFE=14d
AUTH_NEW_SITES=B234HDSAOJ734ndcsdKWNVZZ
DOMAINS=[{"hostname": "piuchebuono.app","port": 3000 },{"hostname":"gruppomacro.app","port": 3010}]

View File

@@ -5,14 +5,59 @@ source ./.env.prod.pcb
echo "Sincronizzazione in corso PCB PRODUZIONE ... /var/www/$SERVERDIR_WEBSITE/"
echo ""
rsync -avz -e 'ssh -p 8822' css pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' docs pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' emails pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' images pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' plugins pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' sass pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' src pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/
rsync -avz -e 'ssh -p 8822' .env.prod.pcb pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/.env.production
rsync -avz -e 'ssh -p 8822' package.json pcbuser@pcb:/var/www/$SERVERDIR_WEBSITE/package.json
#!/bin/bash
# Configurazione
REMOTE_USER="pcbuser"
REMOTE_HOST="pcb"
REMOTE_PORT="8822"
REMOTE_DIR="/var/www/$SERVERDIR_WEBSITE"
SSH_OPTIONS="-p $REMOTE_PORT"
# Array di cartelle e file da sincronizzare
SYNC_ITEMS=(
"css"
"docs"
"emails"
"plugins"
"sass"
"src"
)
echo $REMOTE_DIR
echo ""
echo "*** Copia Cartelle ... "
# Esegui rsync per le cartelle
rsync -avz --delete \
--exclude='src/server/router/upload/' \
-e "ssh $SSH_OPTIONS" \
"${SYNC_ITEMS[@]}" \
"$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/"
echo ""
echo "*** Copia del file .env.production ... "
# Sincronizza i file specifici
rsync -avz -e "ssh $SSH_OPTIONS" \
.env.prod.pcb "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/.env.production"
echo ""
echo "*** Copia del file package.json ... "
rsync -avz -e "ssh $SSH_OPTIONS" \
package.json "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/package.json"
echo ""
echo "*** Copia del file ecosystem.config.js ... "
rsync -avz -e "ssh $SSH_OPTIONS" \
ecosystem.config.pcb.js "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/ecosystem.config.js"
# Verifica il risultato
if [ $? -eq 0 ]; then
echo "Sincronizzazione completata con successo. SERVER PCB! "
else
echo "Errore durante la sincronizzazione. Controlla l'output per i dettagli."
fi
echo "Sincronizzazione TERMINATA! - SERVER PCB!"

View File

@@ -0,0 +1,26 @@
<VirtualHost 89.36.222.238:443>
SuexecUserGroup "#1008" "#1008"
ServerName riso.app
ServerAlias www.riso.app
DocumentRoot /var/www/riso.app
ErrorLog /var/www/riso.app/logs//error.log
CustomLog /var/www/riso.app/logs/access.log combined
<Directory /var/www/riso.app/>
Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
allow from all
AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
Require all granted
</Directory>
<If "%{HTTP_HOST} == 'www.riso.app'">
Redirect permanent / https://riso.app/
</If>
RewriteEngine on
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/riso.app/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/riso.app/privkey.pem
</VirtualHost>
</IfModule>

View File

@@ -0,0 +1,34 @@
<VirtualHost 65.108.222.97:443>
ServerName api.gruppomacro.app
ServerAlias www.api.gruppomacro.app
ServerAdmin surya@riso.app
SSLEngine On
SSLProtocol -ALL +TLSv1.2 +TLSv1.3
SSLCompression Off
SSLHonorCipherOrder off
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:E>
SSLVerifyDepth 10
SSLCertificateFile /etc/letsencrypt/live/gruppomacro.app/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/gruppomacro.app/privkey.pem
ProxyPreserveHost On
SSLProxyEngine On
SSLProxyVerify None
SSLProxyCheckPeerCN Off
SSLProxyCheckPeerName Off
ProxyPass / https://localhost:3010/ retry=0 timeout=5 connectiontimeout=2
ProxyPassReverse / https://localhost:3010/
ProxyTimeout 5
LogLevel debug proxy:trace5
ErrorLog "/var/customers/logs/paolouser-error.log"
CustomLog "/var/customers/logs/paolouser-access.log" combined
<Location />
ProxyPassReverse /
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
</Location>
</VirtualHost>

View File

@@ -0,0 +1,34 @@
<VirtualHost 89.36.222.238:443>
ServerName api.riso.app
ServerAlias www.api.riso.app
ServerAdmin surya@riso.app
SSLEngine On
SSLProtocol -ALL +TLSv1.2 +TLSv1.3
SSLCompression Off
SSLHonorCipherOrder off
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:E>
SSLVerifyDepth 10
SSLCertificateFile /etc/letsencrypt/live/riso.app/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/riso.app/privkey.pem
ProxyPreserveHost On
SSLProxyEngine On
SSLProxyVerify None
SSLProxyCheckPeerCN Off
SSLProxyCheckPeerName Off
ProxyPass / https://localhost:3005/ retry=0 timeout=5 connectiontimeout=2
ProxyPassReverse / https://localhost:3005/
ProxyTimeout 5
LogLevel debug proxy:trace5
ErrorLog "/var/www/riso.app/logs/error.log"
CustomLog "/var/www/riso.app/logs/access.log" combined
<Location />
ProxyPassReverse /
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
</Location>
</VirtualHost>

View File

@@ -0,0 +1,22 @@
<VirtualHost 65.108.222.97:443>
ServerName abitaregliiblei.it
ServerAlias *.abitaregliiblei.it
ServerAdmin surya@riso.app
SSLEngine On
SSLProtocol -ALL +TLSv1.2
SSLCompression Off
SSLHonorCipherOrder off
SSLCipherSuite ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128
SSLVerifyDepth 10
SSLCertificateFile /etc/letsencrypt/live/abitaregliiblei.it/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/abitaregliiblei.it/privkey.pem
DocumentRoot "/var/customers/webs/paolouser/abitaregliiblei.it"
<Directory "/var/customers/webs/paolouser/abitaregliiblei.it/">
CGIPassAuth On
Require all granted
AllowOverride All
</Directory>
LogLevel warn
ErrorLog "/var/customers/logs/paolouser-error.log"
CustomLog "/var/customers/logs/paolouser-access.log" combined
</VirtualHost>

View File

@@ -0,0 +1,22 @@
<VirtualHost 65.108.222.97:443>
ServerName piuchebuono.app
ServerAlias *.piuchebuono.app
ServerAdmin surya@riso.app
SSLEngine On
SSLProtocol -ALL +TLSv1.2
SSLCompression Off
SSLHonorCipherOrder off
SSLCipherSuite ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128
SSLVerifyDepth 10
SSLCertificateFile /etc/letsencrypt/live/piuchebuono.app/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/piuchebuono.app/privkey.pem
DocumentRoot "/var/customers/webs/paolouser/piuchebuono.app"
<Directory "/var/customers/webs/paolouser/piuchebuono.app/">
CGIPassAuth On
Require all granted
AllowOverride All
</Directory>
LogLevel warn
ErrorLog "/var/customers/logs/paolouser-error.log"
CustomLog "/var/customers/logs/paolouser-access.log" combined
</VirtualHost>

View File

@@ -9,15 +9,12 @@ module.exports = {
//autorestart: true,
instances: 1,
env: {
"PORT": 3000,
"NODE_ENV": "development",
},
env_test: {
"PORT": 3001,
"NODE_ENV": "test",
},
env_production: {
"PORT": 3000,
"NODE_ENV": "production",
},
log_file: "logs/combined.outerr.log",

21
ecosystem.config.pcb.js Executable file
View File

@@ -0,0 +1,21 @@
module.exports = {
apps: [
{
name: "PRODUZIONE PiuCheBuono_ServerSide",
script: "/var/www/www.freeplanet_server/src/server/server.js",
ignore_watch: ["node_modules", "logs"],
interpreter: "/root/.nvm/versions/node/v16.19.0/bin/node",
//autorestart: true,
instances: 1,
watch: false,
env: {
"NODE_ENV": "production"
},
log_file: "logs/combined.test.outerr.log",
error_file: "logs/errtest.log",
out_file: "logs/outtest.log",
merge_logs: true,
log_date_format: "YYYY-MM-DD HH:mm:ss.SSSS Z"
}
]
};

View File

@@ -61,6 +61,7 @@
"superagent": "^8.0.0",
"url-parse": "^1.5.10",
"validator": "^13.7.0",
"vhost": "^3.0.2",
"web-push": "^3.6.7",
"xoauth2": "^1.2.0"
},

View File

@@ -50,6 +50,7 @@ const catalogo = new Schema(
productTypes: [{ type: Number }],
excludeproductTypes: [{ type: Number }],
Editore: [{ type: String }],
pdf: { type: Boolean },
// formato: [{ type: String, default: '' }],
// categoria: [{ type: String, default: '' }],
}

View File

@@ -7,6 +7,8 @@ const shared_consts = require('../tools/shared_nodejs');
const tools = require('../tools/general');
const fs = require('fs');
const { City } = require('../models/city');
const Product = require('../models/product');
const Inventariogm = require('../models/inventariogm');
@@ -18,6 +20,10 @@ const Publisher = require('../models/publisher');
const SubCatProd = require('../models/subcatprod');
const Gasordine = require('../models/gasordine');
const { ImageDownloader } = require('../tools/general.js');
const path = require('path');
var { authenticate } = require('../middleware/authenticate');
router.post('/updateval', authenticate, async (req, res) => {
@@ -45,6 +51,35 @@ function fixURL(url) {
return url.replace(/https:\//g, 'https://');
}
async function downloadImgIfMissing(productInfo) {
if (!productInfo.image_link)
return { prodInfo: null, aggiornatoimg: false };
const img = 'upload/products/' + productInfo.image_link.split('/').pop();
const savePath = path.resolve(__dirname, img); // Sostituisci con il percorso dove salvare l'immagine
if (!productInfo.img || !fs.existsSync(savePath)) {
// Download image from the URL productInfo.image_link
productInfo.img = img;
const downloader = new ImageDownloader();
const aggiornatoimg = await downloader.downloadImage(productInfo.image_link, savePath).then(result => {
if (result) {
// console.log('Download completato con successo!');
} else {
console.log('Download non riuscito.');
}
return result;
});
return { prodInfo: productInfo, aggiornatoimg };
}
return { prodInfo: null, aggiornatoimg: false };
}
async function completaSettaggioProduct_AndProductInfo(arrcampi_productInfo, arrcampi_product, rec, product, productInfo) {
@@ -329,6 +364,8 @@ router.post('/import', authenticate, async (req, res) => {
let indprod = 0;
let newprod = 0;
let numprod = dataObjects.length;
for (const product of dataObjects) {
let isnuovo = false
let setta = false
@@ -367,6 +404,8 @@ router.post('/import', authenticate, async (req, res) => {
let versione = 0;
console.log(indprod + '/' + numprod);
productInfo.productTypes = [];
// console.log('indprod', indprod, 'arrversGM', arrversGM, 'versione', product.Versione);
@@ -553,6 +592,12 @@ router.post('/import', authenticate, async (req, res) => {
product.idProductInfo = risrecInfo._id;
recnewInfo = await ProductInfo.findOne({ code: productInfo.code }).lean();
let { prodInfo, aggiornatoimg } = await downloadImgIfMissing(recnewInfo);
if (aggiornatoimg) {
await ProductInfo.findOneAndUpdate({ code: productInfo.code }, { $set: prodInfo });
}
if (risrecInfo._id) {
// Record existed, so it was updated
let arrfieldchange = tools.differentObjects(productInfo, recnewInfo);

View File

@@ -721,11 +721,14 @@ router.post('/login', checkBlocked, (req, res) => {
failedLoginAttempts[body.username]++;
}
let numvolteerrati = failedLoginAttempts[body.username];
const msg = 'Tentativo (' + failedLoginAttempts[body.username] + ') di Login ERRATO [' + body.username + ' , ' + ']\n' + '[IP: ' + tools.getiPAddressUser(req) + ']';
if (numvolteerrati > 2) {
const msg = 'Tentativo (' + numvolteerrati + ') di Login ERRATO [' + body.username + ' , ' + ']\n' + '[IP: ' + tools.getiPAddressUser(req) + ']';
tools.mylogshow(msg);
await telegrambot.sendMsgTelegramToTheAdmin(myuser.idapp, msg, true);
tools.writeErrorLog(msg);
}
// telegrambot.sendMsgTelegramToTheManagers(body.idapp, msg);
if (failedLoginAttempts[body.username] >= MAX_FAILED_ATTEMPTS) {

View File

@@ -12,18 +12,23 @@ const cors = require('cors');
// console.log(" 2) fs");
const fs = require('fs');
var https = require('https');
var http = require('http');
const NUOVO_METODO_TEST = true;
const METODO_MULTI_CORS = false;
const server_constants = require('./tools/server_constants');
//const throttle = require('express-throttle-bandwidth');
// app.use(throttle(1024 * 128)) // throttling bandwidth
const port = process.env.PORT;
// var cookieParser = require('cookie-parser')
// var csrf = require('csurf')
const express = require('express');
const vhost = require('vhost');
const bodyParser = require('body-parser');
const path = require('path');
@@ -246,79 +251,14 @@ myLoad().then(ris => {
if ((process.env.NODE_ENV === 'production') ||
(process.env.NODE_ENV === 'test')) {
const keyStream = path.resolve(`./${process.env.PATH_CERT_KEY}`);
const certificateStream = path.resolve(`./${process.env.PATH_SERVER_CRT}`);
const privateKey = fs.readFileSync(keyStream, "utf8");
const certificate = fs.readFileSync(certificateStream, "utf8");
if (NUOVO_METODO_TEST) {
credentials = {
key: privateKey,
cert: certificate,
};
} else {
// NON USATO !
credentials = {
key: privateKey,
cert: certificate,
ca: [
fs.readFileSync(process.env.PATH_SSL_ROOT_PEM, 'utf8'),
fs.readFileSync(process.env.PATH_SSL_CHAIN_PEM, 'utf8'),
],
};
}
var https = require('https');
} else {
if (process.env.HTTPS_LOCALHOST === "true") {
var privateKey = fs.readFileSync(process.env.PATH_CERT_KEY, 'utf8');
var certificate = fs.readFileSync(process.env.PATH_SERVER_CRT, 'utf8');
credentials = {
key: privateKey,
cert: certificate,
ciphers: 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384',
honorCipherOrder: true,
secureProtocol: 'TLSv1_2_method',
};
var https = require('https');
} else {
var http = require('http');
}
}
if ((process.env.NODE_ENV === 'production') ||
(process.env.NODE_ENV === 'test') || process.env.HTTPS_LOCALHOST === "true") {
if (false) {
/*pem.createCertificate({ days: 1, selfSigned: true }, (err, keys) => {
if (err) {
throw err
}
const httpsServer = https.createServer({ key: keys.clientKey, cert: keys.certificate }, (req, res) => {
res.end('o hai!')
}).listen(port)
}) */
} else {
const httpsServer = https.createServer(credentials, app);
console.log('httpsServer: port ', port);
httpsServer.listen(port);
}
} else {
console.log('httpServer: port ', port);
const httpServer = http.createServer(app);
httpServer.listen(port);
}
startServer(app, process.env.PORT);
mystart();
});
// app.use(throttle(1024 * 128)); // throttling bandwidth
// app.use((req, res, next) => {
@@ -769,5 +709,104 @@ async function faitest() {
}
}
/*const domains = [
{ hostname: 'piuchebuono.app', port: 3000 },
{ hostname: 'gruppomacro.app', port: 3010 },
];*/
function getCredentials(hostname) {
if (NUOVO_METODO_TEST) {
if (METODO_MULTI_CORS) {
const fileprivkey = `/etc/letsencrypt/live/${hostname}/privkey.pem`;
const filecert = `/etc/letsencrypt/live/${hostname}/cert.pem`;
console.log('fileprivkey: ', fileprivkey, ' filecert: ', filecert);
/* return {
SNICallback: function (hostname, callback) {
console.log('hostname: ', hostname);
if (domains.includes(hostname)) {
const fileprivkey = `/etc/letsencrypt/live/${hostname}/privkey.pem`;
const filecert = `/etc/letsencrypt/live/${hostname}/fullchain.pem`;
// console.log('fileprivkey: ', fileprivkey, ' filecert: ', filecert);
const domainCert = {
key: fs.readFileSync(fileprivkey, "utf8"),
cert: fs.readFileSync(filecert, "utf8"),
};
callback(null, domainCert);
} else {
callback(null, { key: privateKey, cert: certificate });
}
}
};*/
return {
key: fs.readFileSync(fileprivkey, "utf8"),
cert: fs.readFileSync(filecert, "utf8")
};
} else {
const keyStream = path.resolve(`./${process.env.PATH_CERT_KEY}`);
const certificateStream = path.resolve(`./${process.env.PATH_SERVER_CRT}`);
const privateKey = fs.readFileSync(keyStream, "utf8");
const certificate = fs.readFileSync(certificateStream, "utf8");
return { key: privateKey, cert: certificate };
}
} else if (process.env.HTTPS_LOCALHOST === "true") {
try {
return {
key: fs.readFileSync(process.env.PATH_CERT_KEY, 'utf8'),
cert: fs.readFileSync(process.env.PATH_SERVER_CRT, 'utf8'),
ciphers: 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384',
honorCipherOrder: true,
secureProtocol: 'TLSv1_2_method'
};
} catch (error) {
console.error('Errore durante la lettura dei file di certificazione, error:', error.message);
throw error;
}
}
// Caso di default non specificato, potrebbe essere necessario aggiungere una gestione degli errori qui
}
function startServer(app, port) {
try {
const isProduction = ['production', 'test'].includes(process.env.NODE_ENV);
let domains = [];
try {
domains = JSON.parse(process.env.DOMAINS);
} catch (error) {
console.error("Errore durante la conversione della stringa DOMAINS:", error);
}
console.log('domains', domains);
if (isProduction) {
for (let i = 0; i < domains.length; i++) {
const credentials = getCredentials(domains[i].hostname);
// console.log('credentials: ', credentials);
const httpsServer = https.createServer(credentials, app);
console.log('⭐️⭐️⭐️⭐️⭐️ HTTPS server: ' + domains[i].hostname + ' Port:', domains[i].port);
httpsServer.listen(domains[i].port);
}
} else {
const httpServer = http.createServer(app);
console.log('⭐️⭐️⭐️ HTTP server IN LOCALE : port', port);
httpServer.listen(port);
}
} catch (e) {
console.log('error ' + e);
}
}
module.exports = { app };

View File

@@ -399,6 +399,70 @@ const textlang = {
},
};
/**
* Scarica un'immagine da una URL e la salva in una directory locale
* @param {string} url - L'URL dell'immagine da scaricare
* @param {string} filepath - Il percorso dove salvare l'immagine scaricata
*/
class ImageDownloader {
/**
* Scarica un'immagine da una URL e la salva in una directory locale.
* Tenta di scaricare fino a 3 volte in caso di errore, con un ritardo tra i tentativi.
*
* @param {string} url - L'URL dell'immagine da scaricare
* @param {string} filepath - Il percorso dove salvare l'immagine scaricata
* @param {number} maxRetries - Numero massimo di tentativi in caso di fallimento (default: 3)
* @param {number} delay - Ritardo in millisecondi tra i tentativi (default: 1000)
* @returns {Promise<boolean>}
*/
async downloadImage(url, filepath, maxRetries = 1, delay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const writer = fs.createWriteStream(filepath);
console.log('url da scaricare:', url);
const response = await axios({
url,
method: 'GET',
responseType: 'stream',
headers: {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
}
});
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', () => {
console.info('✅ Immagine scaricata ' + url + ' in ' + filepath);
resolve(true);
});
writer.on('error', reject);
});
return true;
} catch (error) {
console.error(`❌ Tentativo ${attempt} fallito per l'URL ${url}. Errore:`, error.message);
if (attempt === maxRetries) {
console.error('❌ Tutti i tentativi sono falliti. Scaricamento interrotto.');
return false;
} else {
console.info(`🔁 Ritentando... (${attempt + 1} di ${maxRetries})`);
await sleep(delay);
}
}
}
}
}
// Funzione per implementare il ritardo tra i tentativi
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = {
MYAPPS: [],
INITDB_FIRSTIME: true,
@@ -5195,5 +5259,7 @@ module.exports = {
}
}
},
ImageDownloader,
};

View File

@@ -10731,7 +10731,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -10749,6 +10749,15 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -10805,7 +10814,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -10819,6 +10828,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -11611,6 +11627,11 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vhost@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vhost/-/vhost-3.0.2.tgz#2fb1decd4c466aa88b0f9341af33dc1aff2478d5"
integrity sha512-S3pJdWrpFWrKMboRU4dLYgMrTgoPALsmYwOvyebK2M6X95b9kQrjZy5rwl3uzzpfpENe/XrNYu/2U+e7/bmT5g==
vinyl-contents@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/vinyl-contents/-/vinyl-contents-1.0.0.tgz#f8f3456fe2dfb208a62c85653c2b682f87b5a2d5"
@@ -11863,7 +11884,16 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==