Hallo zusammen. Auf meinen Servern läuft ein hervorragendes seit 2006, und in den Jahren meiner Verwaltung habe ich viele Konfigurationen und Vorlagen gesammelt. Ich habe nginx oft gelobt und so kam es, dass ich sogar den nginx-Hub auf Habr gegründet habe, ganz schön selbstbewusst, oder?
Freunde haben mich gebeten, ihnen eine Entwicklerfarm einzurichten, und anstatt ihnen meine speziellen Vorlagen mitzubringen, erinnerte ich mich an ein interessantes Projekt , das Konfigurationen strukturiert und alles für Let's Encrypt vorbereitet, und so weiter. Ich dachte mir, warum nicht? Allerdings hat mich gestört, dass nginxconfig mir anbietet, das ZIP-Archiv im Browser herunterzuladen, anstatt es direkt auf den Server über wget/fetch/curl zu übertragen. Was ist das für ein Unsinn? Ich brauche das auf dem Server in der Konsole. Wütend habe ich GitHub durchstöbert, um die Details des Projekts zu sehen, was zu einem Fork führte und letztendlich zu einem Pull Request. Darüber würde ich nicht schreiben, wenn es nicht interessant wäre 😉.
Natürlich habe ich, bevor ich mit den Quellcodes experimentierte, geschaut, woher Chrome das generierte ZIP-Archiv mit den Konfigurationen zieht. Dort fand ich eine Adresse, die mit „blob:“ beginnt. Es wurde klar, dass der Dienst offensichtlich nichts generiert; tatsächlich macht das alles JavaScript. Das ZIP-Archiv wird also vom Client, dem Browser, generiert, also von JavaScript. Das Schöne daran ist, dass das Projekt einfach als HTML-Seite gespeichert und auf eine Plattform wie hochgeladen werden kann, und es würde funktionieren :) Das ist eine sehr amüsante und interessante Lösung, jedoch äußerst unpraktisch für die Serverkonfiguration, wofür dieses Projekt ursprünglich gedacht war. Das generierte Archiv mit dem Browser herunterzuladen und es dann mit nc auf den Server zu übertragen... im Jahr 2019? Ich habe mir die Aufgabe gestellt, einen Weg zu finden, um die erhaltene Konfiguration direkt auf den Server zu laden.
Nachdem ich das Projekt geforkt hatte, begann ich nach Möglichkeiten zu suchen. Die Herausforderung bestand darin, dass ich die Bedingung nicht aufgeben wollte, dass das Projekt ein reines Frontend bleiben sollte, ohne irgendein Backend. Natürlich wäre die einfachste Lösung, Node.js zu integrieren und es zu zwingen, 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 Konfiguration anpassen und einen Link erhalten, den wir in die Serverkonsole kopieren können, um ein ZIP-Archiv zu erhalten.
Einige Textdateien im erhaltenen ZIP-Archiv hatten ein sehr geringes Gewicht, nur wenige Kilobyte. Eine offensichtliche Lösung war es, eine Base64-Zeichenkette aus dem generierten ZIP-Archiv zu erstellen und sie in die Zwischenablage zu kopieren, während wir auf dem Server mit einem Konsolenbefehl
echo 'base64string' | base64 --decode > config.zipwir diese ZIP-Datei erstellen konnten.
wurde in AngularJS geschrieben, ich kann mir gar nicht vorstellen, wie viele Kilometer Code notwendig gewesen wären, wenn der Autor nicht das reaktive JS-Framework gewählt hätte. Ich kann mir jedoch gut vorstellen, wie viel einfacher und schöner alles mit VueJS umgesetzt werden könnte, aber das ist ein ganz anderes Thema.
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',
});
};
Es ist alles recht einfach, mit der Bibliothek wird eine Zip-Datei erstellt, in die die Konfigurationsdateien gelegt werden. Nach dem Erstellen des Zip-Archivs wird die js-Bibliothek es dem Browser übergeben. :
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 beim Klick darauf sollte das erhaltene Zip-Archiv nicht im Browser gespeichert, sondern der Base64-Code daraus generiert werden. Nach ein wenig Tüftelei habe ich zwei Methoden erhalten, statt 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 das benötigte zip-Archiv als base64-String
}
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_base64',
});
};
Wie Sie vielleicht bemerkt haben, habe ich die Erstellung des zip-Archivs in eine private Methode namens generateZip ausgelagert. Da es AngularJS ist und der Autor auf Callback-Funktionen setzt, habe ich mich entschieden, keine Promises zu verwenden. downloadZip führt nach wie vor saveAs aus, während downloadBase64 etwas anderes macht. Wir erstellen ein FileReader-Objekt, das in HTML5 eingeführt wurde und bereits verwendbar ist. Dieses kann blob in base64-Strings umwandeln; genauer gesagt, es erstellt eine DataURL-String, aber das ist nicht so wichtig für uns, da die DataURL genau das enthält, was wir benötigen. Bingo, ich stieß auf eine kleine Herausforderung, als ich versuchte, all das in die Zwischenablage zu legen. Der Autor verwendete in diesem Projekt die Bibliothek , die es ermöglicht, mit der Zwischenablage ohne Flash-Objekte zu arbeiten, basierend auf dem ausgewählten Text. Zunächst hatte ich beschlossen, mein Base64 in ein Element mit display:none; zu legen, aber in diesem Fall konnte ich es nicht in die Zwischenablage legen, da keine Auswahl stattfand. Daher habe ich anstelle von display:none; die Entscheidung getroffen,
position: absolute;
z-index: -1;
opacity: 0;
, was es mir ermöglichte, das Element aus dem Blickfeld zu entfernen, es aber tatsächlich auf der Seite zu belassen. Voilà, die Aufgabe ist gelöst, beim Klicken auf meinen Button wurde eine Zeichenfolge in die Zwischenablage gelegt:
echo 'base64string' | base64 --decode > config.zip, die ich einfach in die Konsole auf dem Server eingefügt habe und sofort ein Zip-Archiv mit allen Konfigurationen erhalten habe.
Natürlich habe ich auch einen Pull-Request an den Autor gesendet, da das Projekt aktiv und lebendig ist. Ich möchte sowohl die Updates des Autors sehen als auch meinen Button haben :) Für Interessierte, hier ist des Projekts und selbst , wo man sehen kann, was ich geändert/ergänzt habe.
Allen viel Spaß beim Programmieren :)
Quelle: habr.com
