Generowanie konfiguracji dla nginx, historia jednego żądania ściągnięcia

Pozdrawiam, towarzysze. Piękne na moich serwerach bojowych nginx działa od 2006 roku i przez lata jego administrowania zgromadziłem wiele konfiguracji i szablonów. Bardzo chwaliłem nginx i jakimś cudem okazało się, że nawet uruchomiłem hub nginx na hubie, pochwalcie się m/
Znajomi poprosili mnie o założenie dla nich farmy deweloperskiej i zamiast przeciągać im moje konkretne szablony, przypomniał mi się ciekawy projekt nginxconfig.io, który rozrzuca konfiguracje na półkach i przygotowuje wszystko do szyfrowania itp. Pomyślałem: dlaczego nie? Jednak rozwścieczył mnie fakt, że nginxconfig oferuje mi pobranie archiwum zip do przeglądarki, nie pozwalając mi na przesłanie go bezpośrednio na serwer za pomocą wget/fetch/curl. Co za bzdury, po co mi to w przeglądarce, potrzebuję tego na serwerze z konsoli. Wściekły poszedłem na githuba, żeby zobaczyć, co kryje się za projektem, co doprowadziło do rozwidlenia, a w rezultacie żądania ściągnięcia. O czym bym nie pisała, gdyby nie było ciekawe 😉

Generowanie konfiguracji dla nginx, historia jednego żądania ściągnięcia

Oczywiście przed zagłębieniem się w źródła zajrzałem gdzie Chrome pobiera wygenerowane archiwum zip z konfiguracjami i tam czekał na mnie adres zaczynający się od „blob:”, ups. Stało się już jasne, że usługa po drodze nic nie generuje, tak naprawdę wszystko robi js. Rzeczywiście, archiwum zip jest generowane przez klienta, przeglądarkę i sam JavaScript. Te. piękno polega na tym, że projekt nginxconfig.io można po prostu zapisać jako stronę HTML i przesłać do niektórych narod.ru i to zadziała) To bardzo zabawne i ciekawe rozwiązanie, jednak jest strasznie niewygodne przy ustawianiu serwerów, w rzeczywistości dokładnie pod to, po co ten projekt został stworzony. Pobierz wygenerowane archiwum za pomocą przeglądarki, a następnie prześlij je na serwer za pomocą nc... w 2019 roku? Postawiłem sobie za zadanie znalezienie sposobu na pobranie powstałej konfiguracji bezpośrednio na serwer.
Po rozwidleniu projektu zacząłem zastanawiać się, jakie mam opcje. Zadanie komplikuje fakt, że nie chciałem odstąpić od warunku, że projekt powinien pozostać czystym frontendem, bez żadnego backendu. Oczywiście najprostszym rozwiązaniem byłoby pobranie nodejs i zmuszenie go do wygenerowania archiwum z konfiguracjami przy użyciu bezpośrednich linków.
Właściwie nie było zbyt wielu opcji. Dokładniej, przyszedł mi do głowy tylko jeden. Musimy skonfigurować konfiguracje i uzyskać link, który możemy skopiować na konsolę serwera, aby uzyskać archiwum ZIP.
Kilka plików tekstowych w powstałym archiwum zip ważyło sporo, dosłownie kilka kilobajtów. Oczywistym rozwiązaniem było pobranie ciągu base64 z wygenerowanego archiwum zip i wrzucenie go do bufora, będąc na serwerze za pomocą polecenia w konsoli

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

moglibyśmy utworzyć ten sam plik ZIP.

nginxconfig.io został napisany w AngularJS, nie potrafię sobie nawet wyobrazić, ile kilometrów kodu musiałoby być potrzebnych, gdyby autor nie wybrał reaktywnego frameworku js. Ale doskonale sobie wyobrażam o ile prościej i piękniej to wszystko można by zaimplementować w VueJS, chociaż to już zupełnie inny temat.
W zasobach projektu widzimy metodę generowania archiwum 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',
	});
};

wszystko jest dość proste, korzystając z biblioteki jszip W miejscu umieszczenia plików konfiguracyjnych tworzony jest plik zip. Po utworzeniu archiwum zip js przesyła je do przeglądarki za pomocą biblioteki FileSaver.js:

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

gdzie treść jest wynikowym obiektem blob archiwum zip.

Ok, wystarczyło, że dodałem obok niego kolejny przycisk, a kiedy go kliknąłem, nie zapisałem wynikowego archiwum zip w przeglądarce, ale pobrałem z niego kod base64. Po trochę kombinowaniu dostałem 2 metody zamiast jednego 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 zapewne zauważyłeś, przeniosłem generowanie samego archiwum zip do prywatnej metody generateZip i tak dalej. To jest AngularJS, a sam autor trzyma się wywołań zwrotnych i nie zaimplementował go poprzez obietnice. downloadZip nadal wykonywał saveAs jako wynik, podczas gdy downloadBase64 zrobił coś nieco innego. Tworzymy obiekt FileReader, który przyszedł do nas w HTML5 i jest już całkiem dostępny do użycia. Który w pewnym momencie może utworzyć ciąg znaków base64 z obiektu typu blob, a raczej tworzy ciąg DataURL, ale dla nas nie jest to tak ważne, ponieważ DataURL zawiera dokładnie to, czego potrzebujemy. Bingo, czekał mnie mały szkopuł, gdy próbowałem umieścić to wszystko w buforze. Autor wykorzystał w projekcie bibliotekę schowekjs, który umożliwia pracę ze schowkiem bez obiektów flash, na podstawie zaznaczonego tekstu. Początkowo zdecydowałem się umieścić mój base64 w elemencie z display:none;, ale w tym przypadku nie mogłem go umieścić w schowku, ponieważ nie następuje separacja. Dlatego zamiast display:none; Zrobiłem

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

co pozwoliło mi zarówno ukryć element, jak i pozostawić go na stronie. Voila, zadanie zostało zakończone, kiedy kliknąłem na mój przycisk, w buforze została umieszczona taka linia:

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

który po prostu wkleiłem do konsoli na serwerze i natychmiast otrzymałem archiwum zip ze wszystkimi konfiguracjami.
I oczywiście wysłałem prośbę o ściągnięcie do autora, ponieważ... projekt jest aktywny i żywy, chciałbym widzieć aktualizacje od autora i mieć własny przycisk) Dla zainteresowanych, tutaj jest mój widelec projekt i ja pociągnij żądanie, gdzie możesz zobaczyć, co poprawiłem/dodałem.
Życzę wszystkim miłego rozwoju)

Generowanie konfiguracji dla nginx, historia jednego żądania ściągnięcia

Źródło: www.habr.com

Dodaj komentarz