Salut, tovarăși. Pe serverele mele de luptă se află minunatul care funcționează din 2006 și, de-a lungul anilor de administrare, am acumulat multe configurații și șabloane. Am lăudat mult nginx și cumva a ieșit că am creat chiar și hub-ul nginx pe Habr, mărețea m/
Prietenii m-au rugat să le ridic o fermă de dezvoltare și, în loc să le transport șabloanele mele specifice, mi-am amintit de un proiect interesant , care împarte configurațiile și pregătește totul pentru lets encrypt etc. M-am gândit, de ce nu? Cu toate acestea, m-a enervat faptul că nginxconfig îmi oferă să descărcăm un zip în browser, fără a-mi permite să-l transfer direct pe server prin wget/fetch/curl. Ce absurditate, de ce îmi trebuie în browser, eu am nevoie de el pe server din consolă. Am fost supărat și m-am uitat pe github la structura proiectului, ceea ce a dus la un fork și, ca urmare, la un pull request. Despre care nu aș fi scris dacă nu ar fi fost interesant 😉
Desigur, înainte de a săpa în sursă, am verificat de unde ia Chromium arhiva zip generată cu configurațiile, și acolo m-a așteptat o adresă care începe cu „blob:”, op! Deja a devenit clar că, la urma urmei, serviciul nu generează nimic; de fapt, totul este realizat de js. Într-adevăr, arhiva zip este generată de client, browser, javascript. Adică, frumusețea este că proiectul poate fi pur și simplu salvat ca o pagină html, uploadată pe vreun și va funcționa ) Este o soluție foarte amuzantă și interesantă, dar este extrem de incomodă pentru configurarea serverelor, tocmai pentru ceea ce a fost creat acest proiect. Să descarci arhiva generată cu browserul și apoi să o transferi pe server folosind nc... în 2019? Mi-am propus să găsesc o modalitate de a descărca configurația obținută direct pe server.
După ce am realizat un fork al proiectului, am început să mă gândesc la ce opțiuni am. Sarcina a fost complicată de faptul că nu voiam să deviez de la condiția ca proiectul să rămână un front-end curat, fără niciun back-end. Desigur, cea mai simplă soluție ar fi fost să introduc nodejs și să-l fac să genereze arhiva cu configurațiile pe linkuri directe.
Opțiunile, de fapt, nu erau multe. Adică, mi-a venit în minte doar una. Trebuie să configurăm fișierele și să obținem un link pe care să-l putem copia în consola serverului, pentru a obține arhiva zip.
Câteva fișiere text din arhiva zip generată au avut o dimensiune foarte mică, de câteva kilobytes. O soluție evidentă a fost să obținem un string base64 din arhiva zip generată și să-l plasăm în clipboard, în timp ce pe server... comanda în consola.
echo 'base64string' | base64 --decode > config.zipam putea crea chiar acest fișier zip.
a fost scris în AngularJS, nu îmi imagin că ar fi fost nevoie de kilometri de cod dacă autorul nu ar fi ales un framework JS reactiv. Însă îmi pot închipui cât de mult mai simplu și mai frumos ar fi fost să implementăm totul în VueJS, deși este o poveste complet diferită.
În sursele proiectului vedem metoda de generare a arhivei zip:
$scope.downloadZip = function() {
var zip = new JSZip();
var sourceCodes = $window.document.querySelectorAll('main .file .code.source');
for (var i = 0; i < sourceCodes.length; i++) {
var sourceCode = sourceCodes[i];
var name = sourceCode.dataset.filename;
var content = sourceCode.children[0].children[0].innerText;
if (!$scope.isSymlink() && name.match(/^sites-available\/)) {
name = name.replace(/^sites-available\/, 'sites-enabled\/');
}
zip.file(name, content);
if (name.match(/^sites-available\/)) {
zip.file(name.replace(/^sites-available\/, 'sites-enabled\/'), '..\' + name, {
unixPermissions: parseInt('120755', 8),
});
}
}
zip.generateAsync({
type: 'blob',
platform: 'UNIX',
}).then(function(content) {
saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_zip',
});
};
totul este destul de simplu, cu ajutorul bibliotecii se generează un zip în care sunt incluse fișierele de configurație. După ce arhiva zip este creată, js o trimite browserului prin ajutorul bibliotecii :
saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');
unde content este obiectul blob obținut din arhiva zip.
Ok, tot ce trebuia să fac era să adaug un buton lângă și, la apăsarea lui, să nu salvez arhiva zip obținută în browser, ci să obțin un cod base64 din ea. După puțin experiment, am obținut 2 metode în loc de o singură downloadZip:
$scope.downloadZip = function() {
generateZip(function (content) {
saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_zip',
});
};
$scope.downloadBase64 = function() {
generateZip(function (content) {
var reader = new FileReader();
reader.readAsDataURL(content);
reader.onloadend = function() {
var base64 = reader.result.replace(/^data:.+;base64,/, '');
// în variabila base64 se află exact arhiva zip de care am nevoie sub formă de string base64
}
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_base64',
});
};
Așa cum ați observat, generarea efectivă a arhivei zip a fost mutată într-o metodă privată numită generateZip, și în această măsură, având în vedere că este AngularJS, iar autorul folosește callback-uri, nu am implementat-o prin promisiuni. downloadZip continua să folosească saveAs, în timp ce downloadBase64 făcea ceva puțin diferit. Creăm un obiect FileReader, care a fost introdus în html5 și este deja pentru utilizare. Acesta, la vremea lui, știe să transforme un blob într-un șir base64, mai exact, generează un șir DataURL, dar nu este atât de important, deoarece DataURL conține exact ceea ce ne trebuie. Bingo, o mică problemă m-a așteptat când am încercat să pun totul în clipboard. Autorul a folosit în proiect o bibliotecă , care permite lucrul cu clipboard-ul fără obiecte flash, pe baza textului selectat. Inițial am decis să pun base64-ul meu într-un element cu display:none;, dar în acest caz nu reușeam să-l copiez în clipboard, deoarece nu există o selecție. Așadar, în loc de display:none;, am făcut
position: absolute;
z-index: -1;
opacity: 0;
ceea ce mi-a permis să ascund elementul din vedere și să-l las, de fapt, pe pagină. Voilà, sarcina a fost îndeplinită; la apăsarea butonului meu, în clipboard se punea un șir de forma:
echo 'base64string' | base64 --decode > config.zippe care pur și simplu îl inseram în consola serverului și imediat primeam arhiva zip cu toate configurațiile.
Și, desigur, am trimis o cerere de pull autorului, deoarece proiectul este activ și viu; vreau să văd și actualizări de la autor și să am butonul meu ) Pentru cei interesați, iată al proiectului și , unde puteți vedea ce am corectat/completat.
Toată lumea să aibă o dezvoltare productivă )
Sursa: habr.com
