Generování konfigurací pro nginx, příběh jednoho pull requestu

Zdravím, přátelé. Na mých bojových serverech je skvělý nginx funguje od roku 2006 a během let správy jsem nasbíral mnoho konfigurací a šablon. Hodně jsem chválil nginx a nějak se stalo, že jsem dokonce založil nginx hub na Habr, což bylo trochu na výšku m/
Přátelé mě požádali, abych jim založil vývojářskou farmu, a místo toho, abych jim nesl své specifické šablony, vzpomněl jsem si na zajímavý projekt nginxconfig.io, který rozděluje konfigurace na místa a vše připravuje pro lets encrypt atd. Pomyslel jsem si, proč ne? Nicméně mě rozčilovalo, že nginxconfig nabízí stáhnout zip archiv v prohlížeči, což mi nedává možnost stáhnout ho přímo na server pomocí wget/fetch/curl. Proč mi to dává v prohlížeči, když to potřebuji na serveru z konzole? Rozčilený jsem se podíval na github, abych zjistil další detaily projektu, což vedlo k jeho forku a v důsledku toho k pull requestu. O kterém bych nepsal, kdyby nebyl zajímavý 😉

Generování konfigurací pro nginx, příběh jednoho pull requestu

Samozřejmě, než jsem se pustil do zdrojových kódů, podíval jsem se, odkud Chrome stáhne vygenerovaný zip archiv s konfiguracemi, a tam jsem našel adresu začínající na "blob:", aha. Už bylo jasné, že služba nic netvoří, ve skutečnosti to dělá js. Opravu, zip archiv generuje sám klient, prohlížeč, javascript. Tj. Krásou je, že projekt nginxconfig.io může být jednoduše uložen jako html stránka, nahraný na nějaký narod.ru a bude fungovat :) To je velmi zábavné a zajímavé řešení, nicméně je to strašně nepohodlné pro konfiguraci serverů, vlastně pro to, pro co byl tento projekt vytvořen. Stahovat vygenerovaný archiv z prohlížeče a pak ho předávat na server pomocí nc... v roce 2019? Stanovil jsem si úkol najít způsob, jak stáhnout získanou konfiguraci přímo na server.
Po vytvoření forku projektu jsem začal přemýšlet, jaké mám možnosti. Úkol se komplikoval tím, že jsem nechtěl, aby projekt opustil podmínku, že musí zůstat čistý front-end, bez jakéhokoli back-endu. Samozřejmě, nejjednodušší řešení by bylo přitáhnout nodejs a přimět ho generovat archiv s konfiguracemi přes přímé odkazy.
Možností nebylo moc. Tedy vlastně mě napadl jen jeden. Musíme nastavit konfigurace a získat odkaz, který můžeme zkopírovat do konzole serveru, abychom získali zip archiv.
Několik textových souborů v získaném zip archivu vážilo docela málo, doslova několik kilobajtů. Očividným řešením bylo získat base64 řetězec z vygenerovaného zip archivu a vložit ho do schránky, zatímco na serveru použijeme příkaz v konzoli.

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

mohli bychom vytvořit tento zip soubor.

nginxconfig.io byl napsán v AngularJS, ani si nedokážu představit, kolik kilometrů kódu by bylo potřeba, kdyby autor nevybral reaktivní js framework. Ale dokonale si představuji, jak by to všechno mohlo být jednodušší a hezčí na VueJS, ačkoli to je už úplně jiná téma.
Ve zdrojích projektu vidíme metodu generování zip archivu:

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

vše je dost jednoduché, s pomocí knihovny jszip se vytváří zip, do kterého se ukládají konfigurační soubory. Po vytvoření zip archivu, js ho posílá prohlížeči pomocí knihovny FileSaver.js:

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

kde content je získaný blob objekt zip archivu.

Dobře, vše, co jsem musel udělat, bylo přidat další tlačítko vedle a při jeho stisknutí neukládat získaný zip archiv do prohlížeče, ale získat z něj base64 kód. Po několika úpravách jsem získal 2 metody místo jedné 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,/, '');
			// v proměnné base64 je přesně potřebný zip archiv ve formě base64 řetězce
		}
	});

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

Jak jste si mohli všimnout, generaci samotného zip archivu jsem přenesl do soukromé metody generateZip, a protože se jedná o AngularJS, a sám autor upřednostňuje callbacky, neimplementoval jsem to přes promisy. downloadZip stále na výstupu prováděl saveAs, zatímco downloadBase64 dělalo něco mírně odlišného. Vytváříme objekt FileReader, který jsme dostali v HTML5 a je již docela dostupný k používání. Ten, který umí v době svého vzniku převádět blob na base64 řetězec, přesněji dělá DataURL řetězec, ale to pro nás není tak důležité, protože DataURL obsahuje přesně to, co potřebujeme. Bingo, drobná intelektuální nástraha na mě čekala při pokusu to všechno vložit do schránky. Autor použil v projektu knihovnu clipboardjs, která umožňuje pracovat se schránkou bez flash objektů, na základě vybraného textu. Původně jsem se rozhodl vložit svůj base64 do elementu s display:none;, ale v takovém případě se mi nedařilo jej vložit do schránky, protože nedocházelo k výběru. Takže místo display:none; jsem udělal

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

, což mi umožnilo skrýt element z očí a fakticky ho ponechat na stránce. Voilà, úkol splněn, při stisknutí mého tlačítka se do schránky umisťoval řetězec ve formátu:

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

, který jsem jednoduše vložil do konzole na serveru a okamžitě jsem obdržel zip archiv se všemi konfiguracemi.
A samozřejmě jsem poslal pull request autorovi, protože projekt je aktivní a živý, chtěl bych vidět aktualizace od autora a mít své tlačítko ) Kdo má zájem, tady je můj fork projekty a samotný pull request, kde si můžete prohlédnout, co jsem opravil/doplnil.
Všem přejí úspěšné programování )

Generování konfigurací pro nginx, příběh jednoho pull requestu

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster