Generovanie konfigurácií pre nginx, história jednej žiadosti o stiahnutie

Zdravím, súdruhovia. Na mojich serveroch mám skvelú hru. nginx Funguje to od roku 2006 a za tie roky, čo som to spravoval, som nazhromaždil množstvo konfigurácií a šablón. Veľa som nginx chválil a nejako sa dokonca stalo, že som si na Habr spustil nginx hub, len tak pre parádu.
Priatelia ma požiadali, aby som pre nich založil vývojovú farmu, a namiesto toho, aby som im pretiahol svoje konkrétne šablóny, som si spomenul na zaujímavý projekt. nginxconfig.io, ktorý distribuuje konfigurácie po policiach a pripravuje všetko pre Let's Encrypt atď. Tak som si pomyslel, prečo nie? Otravovalo ma však, že nginxconfig ma vyzval na stiahnutie zip archívu v prehliadači bez toho, aby mi umožnil priamo ho nahrať na server cez wget/fetch/curl. Aký nezmysel! Načo ho potrebujem v prehliadači? Potrebujem ho na serveri z konzoly. Rozzúrený som išiel na GitHub pozrieť sa na vnútornosti projektu, čo viedlo k forku a v dôsledku toho k pull requestu. O čom by som nepísal, keby to nebolo zaujímavé 😉

Generovanie konfigurácií pre nginx, história jednej žiadosti o stiahnutie

Samozrejme, predtým, ako som sa ponoril do zdrojového kódu, som sa pozrel, kde Chrome sťahoval vygenerovaný zip archív s konfiguráciami, a tam na mňa čakala adresa začínajúca na „blob:“ – ups! Už teraz bolo jasné, že služba nič negenerovala; v skutočnosti to všetko robil JavaScript. V skutočnosti zip archív vygeneroval klient, prehliadač a samotný JavaScript. Krása projektu teda spočíva v tom, že nginxconfig.io Dá sa jednoducho uložiť ako HTML stránka a nahrať na nejakú webovú stránku. narod.ru A bude to fungovať.) Toto je veľmi zábavné a zaujímavé riešenie, ale je neuveriteľne nepohodlné na nastavovanie serverov, čo je presne to, na čo bol tento projekt vytvorený. Stiahnutie vygenerovaného archívu pomocou prehliadača a jeho následný prenos na server pomocou nc... v roku 2019? Dal som si za úlohu nájsť spôsob, ako výslednú konfiguráciu priamo nahrať na server.
Po forkovaní projektu som začal zvažovať možnosti. Úlohu komplikovala skutočnosť, že som sa nechcel odchýliť od požiadavky, aby projekt zostal čisto front-endový, bez akéhokoľvek back-endu. Samozrejme, najjednoduchším riešením by bolo stiahnuť nodejs a nechať ich vygenerovať konfiguračný archív prostredníctvom priamych odkazov.
Vlastne nebolo veľa možností. Presnejšie povedané, napadla mi len jedna. Potrebujeme nakonfigurovať konfigurácie a získať odkaz, ktorý môžeme skopírovať do konzoly servera na vytvorenie zip archívu.
Niekoľko textových súborov vo výslednom zip archíve bolo veľmi malých, len niekoľko kilobajtov. Zjavným riešením bolo extrahovať reťazec base64 z vygenerovaného zip archívu a pomocou konzolového príkazu ho uložiť do vyrovnávacej pamäte na serveri.

echo 'base64string' | base64 --decode > config.zip

Mohli by sme vytvoriť tento veľmi podobný zip súbor.

nginxconfig.io Bolo to napísané v AngularJS a ani si neviem predstaviť, koľko kilometrov kódu by bolo potrebných, keby si autor nezvolil reaktívny JS framework. Viem si však jasne predstaviť, o koľko jednoduchšie a krajšie by to všetko mohlo byť implementované vo VueJS, aj keď to je úplne iná téma.
V zdrojoch projektu vidíme metódu na generovanie zip archívu:

$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',
	});
};

Je to všetko celkom jednoduché, s pomocou knižnice jsip Vytvorí sa zip súbor obsahujúci konfiguračné súbory. Po vytvorení zip archívu ho JS pomocou knižnice odošle do prehliadača. FileSaver.js:

saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip');

kde obsah je výsledný objekt blob zip archívu.

Dobre, stačilo pridať ďalšie tlačidlo do blízkosti a keď som naň klikol, namiesto uloženia výsledného zip archívu do prehliadača som z neho získal base64 kód. Po troche experimentovania som namiesto stiahnutiaZip získal dve metódy:

$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,/, '');
			// в переменной base64 как раз нужный мне zip архив в виде base64 строки
		}
	});

	gtag('event', $scope.getDomains().join(','), {
		event_category: 'download_base64',
	});
};

Ako ste si možno všimli, generovanie samotného zip archívu som presunul do súkromnej metódy generateZip. Keďže ide o AngularJS a samotný autor je vývojár s podporou callback, neimplementoval som to pomocou promisov. downloadZip stále používal na výstupe saveAs, zatiaľ čo downloadBase64 robil niečo trochu iné. Vytvárame objekt FileReader, čo je novinka v HTML5 a je celkom užitočná. k dispozícii na použitie. Ktorý vo svojej dobe dokázal previesť blob na reťazec base64, presnejšie povedané, previedol reťazec DataURL, ale to pre nás nie je až také dôležité, keďže DataURL obsahuje presne to, čo potrebujeme. Bingo, pri pokuse o vloženie tohto všetkého do buffera ma čakal malý zádrhel. Autor v projekte použil knižnicu. clipboardjs, čo umožňuje pracovať so schránkou bez flash objektov na základe vybratého textu. Spočiatku som sa rozhodol umiestniť svoj base64 do elementu s display:none;, ale v tomto prípade som ho nemohol umiestniť do schránky, pretože k žiadnemu výberu nedochádza. Preto som namiesto display:none; urobil

position: absolute;
z-index: -1;
opacity: 0;

To mi umožnilo skryť prvok a zároveň ho ponechať na stránke. Voilà, úloha bola dokončená. Keď som klikol na tlačidlo, do vyrovnávacej pamäte sa umiestnil reťazec podobný tomuto:

echo 'base64string' | base64 --decode > config.zip

ktorý som jednoducho vložil do konzoly na serveri a okamžite som dostal zip archív so všetkými konfiguráciami.
Samozrejme, autorovi som poslal pull request, keďže projekt je aktívny a živý, chcem vidieť aktualizácie od autora a mať vlastné tlačidlo) Pre každého, kto má záujem, tu je moja vidlička projekt a ja požiadavka pull, kde si môžete pozrieť, čo som opravil/pridal.
Prajem všetkým šťastný vývoj!

Generovanie konfigurácií pre nginx, história jednej žiadosti o stiahnutie

Zdroj: hab.com

Kúpte si spoľahlivý hosting pre stránky s DDoS ochranou, VPS VDS servery 🔥 Kúpte si spoľahlivý webhosting s ochranou DDoS, VPS VDS servery | ProHoster