Генерацыя канфігаў для nginx, гісторыя аднаго pull request

Вітаю, таварышы. На маіх баявых серверах цудоўны Nginx круціцца з 2006 года і за гады яго адміністравання я назапасіў шмат канфігаў і шаблонаў. Я шмат нахвальваў nginx і неяк так выйшла, што нават хаб nginx на хабры таксама завёў я, панты m/
Сябры папрасілі падняць ім дэвелаперскую ферму і замест таго, каб цягнуць ім свае спецыфічныя шаблоны, я ўспомніў пра цікавы праект nginxconfig.io, які і канфігі раскідае па паліцах і для lets encrypt усё рыхтуе гп. Я і падумаў, чаму б і не? Аднак, мне злаваў той факт, што nginxconfig прапануе мне запампаваць zip архіў у браўзэр, не дазваляючы зліць яго адразу на сервак па сродках wget/fetch/curl. Што за трызненне, навошта ён мне ў браўзэры, мне ён патрэбен на серверы з кансолі. Раззлаваўшыся, я залез на github паглядзець кішкі праекту, што прывяло да яго форку і, як следства, pull request`у. Пра які я б не стаў пісаць, калі б ён не быў цікавым 😉

Генерацыя канфігаў для nginx, гісторыя аднаго pull request

Вядома, перад тым, як калупаць зыходнікі, я паглядзеў адкуль хром цягне згенераваны zip архіў з канфігамі, а там мяне чакаў адрас, які пачынаецца з «blob:», оппа. Ужо стала зразумела, што па ходзе сервіс нічога не генеруе, па факце, што гэта ўсё робіць js. Сапраўды, zip архіў генеруе сам кліент, браўзэр, javascript. Г.зн. хараство ў тым, што праект nginxconfig.io можа быць проста захаваны як html старонка, заліты на які-небудзь narod.ru і ён будзе працаваць) Гэта вельмі пацешнае і цікавае рашэнне, аднак, яно жудасна нязручнае для налады сервераў, уласна, менавіта для таго, для чаго гэты праект і ствараўся. Пампаваць згенераваны архіў браўзэрам, а потым перадаваць яго на сервер з дапамогай nc… у 2019 году? Я паставіў перад сабой задачу знайсці спосаб пампаваць атрыманы канфіг адразу на сервер.
Зрабіўшы форк праекту, я пачаў думаць, якія ў мяне ёсць варыянты. Задача ўскладнялася тым, што я не хацеў адыходзіць ад умовы, што праект мусіць заставацца чыстым front-end, без якога-небудзь back-end. Вядома, самае простае рашэнне было б падцягнуць nodejs і прымусіць яго генераваць архіў з канфігамі па прамых спасылках.
Варыянтаў, уласна, было няшмат. Дакладней, у галаву прыйшоў толькі адзін. Нам трэба наладзіць канфігі і атрымаць спасылку, якую зможам скапіяваць у кансоль сервера, каб атрымаць zip архіў.
Некалькі тэкставых файлаў у атрымоўваным zip архіве важылі зусім няшмат, літаральна некалькі кілабайт. Відавочным рашэннем было атрымаць base64 радок са згенераванага zip архіва і кінуць яе ў буфер, тады як на серверы камандай у кансолі

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

мы маглі б стварыць гэты самы zips файл.

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 ствараецца zips, куды кладуцца файлы канфігурацый. Пасля стварэння 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 аўтару, т.я. праект актыўны і жывы, мне хочацца і абнаўленні ад аўтара бачыць і сваю кнопачку мець ) Каму цікава, вось мой форк праекта і сам запыт цягнуць, дзе можна паглядзець што я выправіў/дапоўніў.
Усім бадзёрай распрацоўкі )

Генерацыя канфігаў для nginx, гісторыя аднаго pull request

Крыніца: habr.com

Дадаць каментар