Hej, venner. På mine produktionsservere er der en fremragende kørende siden 2006, og gennem årene har jeg opbygget mange konfigurationer og skabeloner. Jeg har rost nginx mange gange, og det endte med, at jeg endda startede nginx-hubben på Habr, sjovt nok.
Mine venner bad mig om at oprette en udviklerfarm, og i stedet for at dele mine specifikke skabeloner med dem, kom jeg i tanke om et spændende projekt , som organiserer konfigurationerne og forbereder dem til Let's Encrypt osv. Jeg tænkte, hvorfor ikke? Dog blev jeg irriteret over, at nginxconfig tilbydede mig at downloade zip-arkivet i browseren, uden at give mig mulighed for at hente det direkte til serveren via wget/fetch/curl. Hvad er det for en skør idé, hvorfor skal det være i browseren? Jeg har brug for det på serveren fra konsollen. Irriteret gik jeg ind på GitHub for at se, hvad der gemte sig i projektet, hvilket førte til en fork og, som følge heraf, et pull request. Jeg ville ikke have skrevet om det, hvis ikke det var interessant 😉
Selvfølgelig, før jeg begyndte at granske koden, kiggede jeg på, hvor Chrome henter det genererede zip-arkiv med konfigurationerne, og der fandt jeg en adresse, der begyndte med 'blob:', åh. Det blev hurtigt klart, at tjenesten overhovedet ikke genererer noget, det er faktisk det hele, som js gør. Det vil sige, at zip-arkivet genereres af klienten, browseren, javascript. Det vil sige, at skønheden i projektet kan gemmes som en html-side, uploades til en eller anden og så vil det fungere. 🙂 Dette er en meget sjov og interessant løsning, men den er virkelig upraktisk til serverkonfigurationer, lige præcis det, som projektet blev skabt til. At hente det genererede arkiv fra browseren og derefter overføre det til serveren med nc… i 2019? Jeg satte mig for at finde en måde at hente den opnåede konfiguration direkte til serveren.
Efter at have forked projektet begyndte jeg at overveje, hvilke muligheder jeg havde. Opgaven blev kompliceret af, at jeg ikke ønskede at gå væk fra betingelsen, at projektet skulle forblive et rent front-end, uden nogen back-end. Naturligvis ville den enkleste løsning have været at trække nodejs ind og få det til at generere arkivet med konfigurationer via direkte links.
Der var reelt ikke mange muligheder. Der kom kun én i tankerne. Vi skal konfigurere konfigurationerne og få et link, som vi kan kopiere ind i serverens konsol for at få zip-arkivet.
Flere tekstfiler i den genererede zip-arkiv vejede faktisk meget lidt, kun et par kilobyte. Et åbenlyst valg var at få base64-strengen fra den genererede zip-arkiv og smide den i bufferet, mens man brugte en kommando på serveren.
echo 'base64string' | base64 --decode > config.zipvi kunne oprette denne zip-fil.
var skrevet i AngularJS, og jeg kan ikke forestille mig, hvor mange kilometer kode der ville være nødvendige, hvis forfatteren ikke havde valgt et reaktivt JS-rammeværk. Men jeg kan bestemt forestille mig, hvor meget lettere og smukkere alt dette kunne være implementeret i VueJS, selvom det er et helt andet emne.
I projektets kilder ser vi metoden til at generere zip-arkivet:
$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',
});
};
alt er relativt simpelt, ved hjælp af biblioteket oprettes en zip, hvori konfigurationsfilerne placeres. Efter oprettelse af zip-arkivet leveres det til browseren af JS ved hjælp af biblioteket :
saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');
hvor content er den opnåede blob-objekt fra zip-arkivet.
Okay, alt hvad jeg skulle gøre, var at tilføje en knap ved siden af, og når man trykker på den, lagrer den ikke det opnåede zip-arkiv i browseren, men henter base64-koden fra det. Efter lidt manipulation fik jeg 2 metoder i stedet for én 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,/, '');
// i variablen base64 er den ønskede zip-arkiv i form af en base64-streng
}
});
gtag('event', $scope.getDomains().join(','), {
event_category: 'download_base64',
});
};
Som du måske har bemærket, har jeg flyttet genereringen af selve zip-arkivet til en privat metode generateZip, og da det er AngularJS, og forfatteren også holder sig til callbacks, implementerede jeg det ikke gennem promises. downloadZip laver stadig saveAs, mens downloadBase64 gør noget lidt anderledes. Vi skaber et FileReader-objekt, som kom til os i html5 og er helt til brug. Som i sin tid kan lave en base64-streng fra en blob, mere præcist laver det en DataURL-streng, men det er ikke så vigtigt for os, da DataURL indeholder præcis det, vi har brug for. Bingo, der ventede mig en lille udfordring, da jeg forsøgte at lægge alt dette i udklipsholderen. Forfatteren brugte et bibliotek i projektet , som gør det muligt at arbejde med udklipsholderen uden flash-objekter, baseret på den markerede tekst. Oprindeligt besluttede jeg at lægge min base64 i et element med display:none;, men i så fald kunne jeg ikke få det ind i udklipsholderen, da der ikke sker nogen markering. Så i stedet for display:none; lavede jeg
position: absolute;
z-index: -1;
opacity: 0;
hvilket gjorde det muligt for mig at skjule elementet for øjnene, men alligevel lade det være på siden. Voilà, opgaven var løst; når jeg trykkede på min knap, blev en streng af følgende format lagt i udklipsholderen:
echo 'base64string' | base64 --decode > config.zipsom jeg bare indsatte i konsollen på serveren og straks fik zip-arkivet med alle konfigurationer.
Og selvfølgelig sendte jeg et pull request til forfatteren, da projektet er aktivt og levende, jeg ønsker både at se opdateringer fra forfatteren og have min knap :) Hvis nogen er interesseret, her er af projektet og selve , hvor man kan se, hvad jeg har rettet/tilføjet.
God udvikling til alle :)
Kilde: habr.com
