Генерация на конфигурации за nginx, историята на един pull request

Здравейте, колеги. На моите бойни сървъри е чудесно nginx , работи от 2006 година и през годините на администрирането съм събрал много конфигурации и шаблони. Много съм хвалил nginx и така се случи, че дори хъбът nginx на Хабра също съм го създал, хвалби m/
Приятели ми помолиха да им настроя разработческа ферма и вместо да им пренасям моите специфични шаблони, си припомних за един интересен проект nginxconfig.io, който организира конфигурациите и подготвя всичко за lets encrypt и др. Помислих си, защо не? Обаче ме дразнеше фактът, че nginxconfig ми предлага да изтегля zip архив в браузъра, без да ми позволява да го сваля директно на сървъра чрез wget/fetch/curl. Каква глупост, защо ми е в браузъра, нужен ми е на сървъра от конзолата. Раздразнен, влязох в github да погледна какво става в проекта, което доведе до неговия fork и, в следствие, до pull request. За който не бих писал, ако не беше интересен 😉

Генерация на конфигурации за nginx, историята на един pull request

Разбира се, преди да ритуални в източниците, погледнах откъде Chrome изтегля генерирания zip архив с конфигурациите, а там ме очакваше адрес, започващ с 'blob:', оппа. Вече стана ясно, че по всяка вероятност, услугата нищо не генерира, на практика, всичко това прави js. Наистина, zip архивът се генерира от самия клиент, браузъра, javascript. Тоест, хубавото на проекта nginxconfig.io може просто да бъде запазено като html страница, качена на някой narod.ru , и ще работи ) Това е много забавно и интересно решение, обаче, е изключително неудобно за настройка на сървъри, собствено, точно за това, за което е създаден този проект. Да изтеглиш генерирания архив от браузъра, а след това да го прехвърлиш на сървъра с помощта на nc… през 2019 година? Поставих си за задача да намеря начин да свалям получения конфигурационен файл директно на сървъра.
Направих fork на проекта и започнах да мисля какви имам възможности. Задачата се усложняваше от факта, че не исках да излизам от условието проектът да остане чист front-end, без никакъв back-end. Разбира се, най-простото решение щеше да бъде да наложа nodejs и да го накарам да генерира архиви с конфигурации по директни линкове.
Вариантите, всъщност, не бяха много. По-точно, в главата ми дойде само един. Трябва да настроим конфигурациите и да получим линк, който можем да копираме в конзолата на сървъра, за да получим zip архив.
Няколко текстови файла в полученото zip архивче тежаха съвсем малко, буквално няколко килобайта. Очевидното решение беше да получим base64 низ от генерираното zip архивче и да го поставим в буфера, докато на сървъра с команда в консолата.

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

можехме да създадем това zip файлче.

nginxconfig.io беше написан на AngularJS, дори не мога да си представя колко километри код биха били нужни, ако авторът не беше избрал реактивен js фреймуърк. Но прекрасно си представям колко по-лесно и красиво би могло да бъде всичко това реализирано на VueJS, въпреки че това вече е съвсем друга тема.
В източниците на проекта виждаме метод за генериране на zip архив:

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

всичко е достатъчно просто, с помощта на библиотеката jszip се създава zip, в който се поставят файловете с конфигурации. След създаването на zip архива, js го подава на браузъра с помощта на библиотеката FileSaver.js:

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

където content е получен blob обект от zip архива.

Ок, всичко, което трябваше да направя, беше да добавя още един бутон до него и при натискане на него да не запазвам получения zip архив в браузъра, а да получавам base64 код от него. Леко поигравайки, получих 2 метода вместо един 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',
	});
};

Както забелязахте, генерирането на zip архива изнесох в частен метод generateZip, а тъй като това е AngularJS и самият автор следва колбек подхода, не реших да го реализирам с промиси. downloadZip все още извършва saveAs, докато downloadBase64 прави нещо малко по-различно. Създаваме FileReader обект, който получихме от html5 и напълно е налично за използване. Този, който по време на неговото съществуване може да превръща blob в base64 стринг, по-точно той създава DataURL стринг, но това не е толкова важно, тъй като DataURL съдържа точно това, от което се нуждаем. Бинго, малка затруднение ме чакаше при опита да поставя всичко това в буфера. Авторът е използвал в проекта библиотека clipboardjs, която позволява работа с клипборда без flash обекти, на базата на избрания текст. Първоначално реших да поставя моя base64 в елемент с display:none;, но в този случай не успявах да го сложа в буфера, тъй като няма избиране. Затова вместо display:none; направих

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

което ми позволи да скрия елемента от погледа и в същото време да го оставя на страницата. Вуаля, задачата е изпълнена, при натискане на бутона ми, в буфера се поставяше стринг от вида:

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

която просто поставих в конзолата на сървъра и веднага получих zip архив с всички конфигурации.
И разбира се, изпратих pull request на автора, тъй като проектът е активен и жив, и искам да виждам обновления от автора и да имам моят форк на проекта и сам pull request, където можете да видите какво съм поправил/допълнил.
На всички успешна разработка)

Генерация на конфигурации за nginx, историята на един pull request

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster