Hallo, Freunde. Auf meinen Produktionsservern läuft ein hervorragendes seit 2006 und während meiner Jahre der Administration habe ich viele Konfigurationen und Vorlagen gesammelt. Ich habe nginx oft gelobt und irgendwie kam es dazu, dass ich sogar den nginx-Hub auf Habré gegründet habe, ein bisschen Prahlerei m/
Freunde haben mich gebeten, ihnen eine Entwicklungsfarm aufzubauen, und anstatt meine spezifischen Vorlagen zu ihnen zu bringen, erinnerte ich mich an ein interessantes Projekt , das die Konfigurationen aufgliedert und alles für Lets Encrypt vorbereitet usw. Ich dachte, warum nicht? Allerdings ärgerte mich der Umstand, dass nginxconfig mir vorschlägt, das zip-Archiv im Browser herunterzuladen, ohne die Möglichkeit, es direkt auf den Server mit wget/fetch/curl zu übertragen. Was für ein Unsinn, warum brauche ich das im Browser, ich will es auf dem Server aus der Konsole. Verärgert sah ich mir auf GitHub die inneren Abläufe des Projekts an, was zu einem Fork und letztlich zu einem Pull Request führte. Über den ich nicht geschrieben hätte, hätte er nicht interessiert, 😉
Natürlich schauten ich mir, bevor ich den Quellcode durchschaute, an, wo Chrome das generierte zip-Archiv mit Konfigurationen herunterlädt, und dort erwartete mich eine Adresse, die mit "blob:" begann, hoppla. Es wurde bereits klar, dass der Dienst anscheinend nichts generiert, sondern dass dies alles von js gemacht wird. Tatsächlich generiert der Client selbst, also der Browser, das zip-Archiv durch JavaScript. Das bedeutet, dass das Projekt einfach als HTML-Seite gespeichert, auf irgendeinem hochgeladen werden kann und es trotzdem funktioniert ) Das ist eine sehr amüsante und interessante Lösung, jedoch äußerst unpraktisch für die Serverkonfigurationen, für die dieses Projekt tatsächlich geschaffen wurde. Ein generiertes Archiv mit dem Browser herunterzuladen und es dann über nc auf den Server zu übertragen... im Jahr 2019? Ich stellte mir die Aufgabe, einen Weg zu finden, die erhaltene Konfiguration direkt auf den Server herunterzuladen.
Nachdem ich das Projekt geforkt hatte, begann ich zu überlegen, welche Optionen mir zur Verfügung standen. Die Herausforderung bestand darin, dass ich nicht vom Ziel abweichen wollte, dass das Projekt ein reines Front-End bleiben sollte, ohne jegliches Back-End. Natürlich wäre die einfachste Lösung gewesen, nodejs zu nutzen und es dazu zu bringen, das Archiv mit den Konfigurationen über direkte Links zu generieren.
Es gab nicht viele Optionen. Genauer gesagt, mir fiel nur eine ein. Wir müssen die Konfigurationen einrichten und einen Link erhalten, den wir in die Konsole des Servers kopieren können, um das zip-Archiv zu erhalten.
Einige Textdateien im erhaltenen Zip-Archiv wogen nur sehr wenig, buchstäblich ein paar Kilobyte. Eine offensichtliche Lösung war, den Base64-String aus dem erzeugten Zip-Archiv zu erhalten und ihn in die Zwischenablage zu kopieren, während auf dem Server mit einem Konsolenbefehl
echo 'base64string' | base64 --decode > config.zipwir diese Zip-Datei erstellen könnten.
wurde mit AngularJS geschrieben, ich kann mir nicht vorstellen, wie viele Kilometer Code erforderlich gewesen wären, wenn der Autor sich nicht für ein reaktives JS-Framework entschieden hätte. Ich kann mir jedoch gut vorstellen, wie viel einfacher und schöner das Ganze mit VueJS hätte umgesetzt werden können, obwohl das ein ganz anderes Thema ist.
Im Quellcode des Projekts sehen wir die Methode zur Generierung des Zip-Archivs:
$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',
});
};
alles ist ziemlich einfach, mit Hilfe der Bibliothek wird ein Zip erstellt, in das die Konfigurationsdateien gelegt werden. Nach der Erstellung des Zip-Archivs wird JS es dem Browser mit Hilfe der Bibliothek :
saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');
wobei content das erhaltene Blob-Objekt des Zip-Archivs ist.
Okay, alles, was ich tun musste, war, einen weiteren Button daneben hinzuzufügen und bei dessen Betätigung das erhaltene Zip-Archiv nicht im Browser zu speichern, sondern den Base64-Code daraus zu erhalten. Nach ein wenig Tüftelei erhielt ich 2 Methoden anstelle einer 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\/, '');
// in der Variable base64 befindet sich genau das Zip-Archiv, das ich in Form eines Base64-Strings benötige
}
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_base64',
});
};
Wie Sie vielleicht bemerkt haben, habe ich die Generierung des eigentlichen Zip-Archivs in eine private Methode namens generateZip ausgelagert. Da dies AngularJS ist und der Autor Callback-Funktionen verwendet, habe ich mich entschieden, dies nicht mit Promises zu realisieren. downloadZip verwendet nach wie vor saveAs, während downloadBase64 etwas anderes macht. Wir erstellen ein FileReader-Objekt, das uns in HTML5 zur Verfügung steht und bereits völlig ist. Dieses kann, damals, aus Blob eine Base64-Zeichenkette erzeugen; genauer gesagt, es erzeugt eine DataURL-Zeichenkette, aber das ist für uns nicht so wichtig, da die DataURL genau das enthält, was wir brauchen. Bingo, eine kleine Hürde erwartete mich, als ich versuchte, all dies in die Zwischenablage zu legen. Der Autor verwendete in dem Projekt die Bibliothek , die es ermöglicht, ohne Flash-Objekte mit der Zwischenablage zu arbeiten, basierend auf dem markierten Text. Ursprünglich wollte ich meine Base64 in ein Element mit display:none; legen, aber in diesem Fall konnte ich es nicht in die Zwischenablage kopieren, da keine Markierung stattfindet. Daher habe ich anstelle von display:none; gemacht
position: absolute;
z-index: -1;
opacity: 0;
, was es mir ermöglichte, das Element unsichtbar zu machen und es faktisch auf der Seite zu belassen. Voilà, die Aufgabe war erledigt; beim Klicken auf meinen Button wurde eine Zeichenkette wie folgt in die Zwischenablage eingefügt:
echo 'base64string' | base64 --decode > config.zipdie ich einfach in die Konsole auf dem Server einfügte und sofort das Zip-Archiv mit allen Konfigurationen erhielt.
Und natürlich habe ich dem Autor einen Pull-Request geschickt, da das Projekt aktiv und lebendig ist; ich möchte sowohl die Updates des Autors sehen als auch meinen eigenen Button haben ) Wer interessiert ist, hier ist des Projekts und der , wo man sehen kann, was ich geändert bzw. ergänzt habe.
Allen viel Erfolg bei der Entwicklung )
Quelle: habr.com
