Generování konfigurací pro nginx, historie jednoho požadavku na stažení

Zdravím vás, soudruzi. Krásné na mých bojových serverech Nginx běží od roku 2006 a za roky jeho správy se mi nashromáždilo spoustu konfigurací a šablon. Hodně jsem chválil nginx a nějak to dopadlo, že jsem na hubu dokonce založil nginx hub, pochlubte se m/
Přátelé mě požádali, abych jim založil vývojovou farmu, a místo toho, abych jim přetahoval své konkrétní šablony, vzpomněl jsem si na zajímavý projekt nginxconfig.io, který rozhází konfigurace po poličkách a připraví vše na lets encrypt atp. Říkal jsem si, proč ne? Rozhořčila mě však skutečnost, že mi nginxconfig nabízí stažení archivu zip do prohlížeče, aniž by mi umožnil nahrát jej přímo na server pomocí wget/fetch/curl. Jaký nesmysl, proč to potřebuji v prohlížeči, potřebuji to na serveru z konzole. Rozzlobený jsem šel na github, abych viděl vnitřnosti projektu, což vedlo k jeho forku a v důsledku toho k požadavku na stažení. O čem bych nepsal, kdyby to nebylo zajímavé 😉

Generování konfigurací pro nginx, historie jednoho požadavku na stažení

Samozřejmě, než jsem se pustil do zdrojů, podíval jsem se, odkud Chrome stahuje vygenerovaný zip archiv s konfiguracemi, a tam na mě čekala adresa začínající na „blob:“, ups. Již se ukázalo, že služba po cestě nic negeneruje, ve skutečnosti to všechno dělá js. Archiv zip je generován samotným klientem, prohlížečem a javascriptem. Tito. krása je v tom projektu nginxconfig.io lze jednoduše uložit jako html stránku, nahrát na některé narod.ru a bude to fungovat) Toto je velmi vtipné a zajímavé řešení, ale je strašně nepohodlné pro nastavení serverů, vlastně přesně pro to, k čemu tento projekt vznikl. Stáhnout vygenerovaný archiv pomocí prohlížeče a poté jej přenést na server pomocí nc... v roce 2019? Dal jsem si za úkol najít způsob, jak stáhnout výslednou konfiguraci přímo na server.
Po rozvětvení projektu jsem začal přemýšlet, jaké mám možnosti. Úkol byl komplikován tím, že jsem nechtěl vybočit z podmínky, že projekt má zůstat čistým front-endem, bez jakéhokoli back-endu. Nejjednodušším řešením by samozřejmě bylo vytáhnout nodejs a donutit jej vygenerovat archiv s konfiguracemi pomocí přímých odkazů.
Vlastně moc možností nebylo. Přesněji mě napadalo jen jedno. Musíme nastavit konfigurace a získat odkaz, který můžeme zkopírovat do konzoly serveru, abychom získali archiv zip.
Několik textových souborů ve výsledném zip archivu vážilo docela dost, doslova pár kilobajtů. Zřejmým řešením bylo získat řetězec base64 z vygenerovaného zip archivu a hodit jej do vyrovnávací paměti, zatímco na serveru pomocí příkazu v konzole

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

mohli bychom vytvořit stejný soubor zip.

nginxconfig.io byl napsán v AngularJS, nedokážu si ani představit, kolik kilometrů kódu by bylo potřeba, kdyby autor nezvolil reaktivní js framework. Dokážu si ale dokonale představit, o kolik jednodušší a krásnější by se to všechno dalo implementovat do VueJS, ačkoli to je úplně jiné téma.
Ve zdrojích projektu vidíme metodu pro 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 docela jednoduché, pomocí knihovny jszip Vytvoří se zip, kam jsou umístěny konfigurační soubory. Po vytvoření archivu zip jej js odešle do prohlížeče pomocí knihovny FileSaver.js:

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

kde obsah je výsledný objekt blob archivu zip.

Ok, stačilo přidat další tlačítko vedle něj a když na něj kliknu, neuložím si výsledný zip archiv do prohlížeče, ale získám z něj kód base64. Po troše šmejdění jsem dostal 2 metody místo jednoho 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,/, '');
			// в переменной base64 как раз нужный мне zip архив в виде base64 строки
		}
	});

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

Jak jste si mohli všimnout, přesunul jsem generování samotného zip archivu na privátní metodu createZip a tak dále. Toto je AngularJS a sám autor se drží zpětných volání a neimplementoval to přes sliby. downloadZip stále dělal saveAs jako výstup, zatímco downloadBase64 dělal něco trochu jiného. Vytvoříme objekt FileReader, který k nám přišel v html5 a už je docela dost k dispozici k použití. Což najednou může z blobu vytvořit řetězec base64, nebo spíše vytvoří řetězec DataURL, ale to pro nás není tak důležité, protože DataURL obsahuje přesně to, co potřebujeme. Bingo, čekal na mě malý zádrhel, když jsem se to všechno pokusil dát do vyrovnávací paměti. Autor v projektu použil knihovnu schránky, 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 prvku s display:none;, ale v tomto případě jsem ho nemohl umístit do schránky, protože nedochází k žádné separaci. Proto místo display:none; udělal jsem

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

což mi umožnilo skrýt prvek ze zobrazení a skutečně jej ponechat na stránce. Voila, úkol byl dokončen, když jsem kliknul na své tlačítko, do vyrovnávací paměti byl umístěn řádek jako tento:

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 autorovi poslal žádost o stažení, protože... projekt je aktivní a živý, rád bych viděl aktualizace od autora a měl vlastní tlačítko) Pro zájemce zde je moje vidlička projekt a já pull request, kde vidíte, co jsem opravil/přidal.
Šťastný vývoj všem)

Generování konfigurací pro nginx, historie jednoho požadavku na stažení

Zdroj: www.habr.com

Přidat komentář