Génération de configurations pour nginx, historique d'une pull request

Salutations, camarades. Magnifique sur mes serveurs de combat nginx fonctionne depuis 2006 et au fil des années de son administration, j'ai accumulé de nombreuses configurations et modèles. J'ai beaucoup fait l'éloge de nginx et, d'une manière ou d'une autre, il s'est avéré que j'avais même démarré un hub nginx sur le hub, montrez-moi m/
Des amis m'ont demandé de créer une ferme de développement pour eux, et au lieu de leur glisser mes modèles spécifiques, je me suis souvenu d'un projet intéressant nginxconfig.io, qui disperse les configs sur les étagères et prépare tout pour let crypter, etc. Je me suis dit, pourquoi pas ? Cependant, j'étais furieux du fait que nginxconfig me propose de télécharger l'archive zip dans le navigateur, sans me permettre de la télécharger directement sur le serveur en utilisant wget/fetch/curl. Quelle absurdité, pourquoi en ai-je besoin dans le navigateur, j'en ai besoin sur le serveur depuis la console. En colère, je suis allé sur github pour voir les entrailles du projet, ce qui a conduit à son fork et, par conséquent, à une pull request. Sur lequel je n'écrirais pas si ce n'était pas intéressant 😉

Génération de configurations pour nginx, historique d'une pull request

Bien sûr, avant de fouiller dans les sources, j'ai regardé où Chrome récupérait l'archive zip générée avec les configurations, et là une adresse commençant par « blob : » m'attendait, oups. Il est déjà devenu clair que le service ne génère rien en cours de route, en fait, tout est fait par js. En effet, l'archive zip est générée par le client, le navigateur et le javascript lui-même. Ceux. la beauté c'est que le projet nginxconfig.io peut être simplement enregistré sous forme de page HTML, téléchargé sur certains narod.ru et ça fonctionnera) C'est une solution très amusante et intéressante, cependant, elle est terriblement gênante pour configurer des serveurs, en fait, exactement pour quoi ce projet a été créé. Télécharger l'archive générée avec un navigateur, puis la transférer sur le serveur via nc... en 2019 ? Je me suis donné pour tâche de trouver un moyen de télécharger la configuration résultante directement sur le serveur.
Après avoir lancé le projet, j'ai commencé à réfléchir à mes options. La tâche était compliquée par le fait que je ne voulais pas déroger à la condition selon laquelle le projet devait rester un pur front-end, sans aucun back-end. Bien sûr, la solution la plus simple serait d'extraire nodejs et de le forcer à générer une archive avec des configurations à l'aide de liens directs.
En fait, il n’y avait pas beaucoup d’options. Plus précisément, un seul m'est venu à l'esprit. Nous devons configurer les configurations et obtenir un lien que nous pouvons copier sur la console du serveur pour obtenir une archive zip.
Plusieurs fichiers texte dans l'archive zip résultante pesaient un peu, littéralement quelques kilo-octets. La solution évidente était d'obtenir la chaîne base64 de l'archive zip générée et de la jeter dans le tampon, sur le serveur avec la commande dans la console

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

nous pourrions créer ce même fichier zip.

nginxconfig.io a été écrit en AngularJS, je ne peux même pas imaginer quels kilomètres de code auraient été nécessaires si l'auteur n'avait pas choisi un framework js réactif. Mais je peux parfaitement imaginer à quel point tout cela pourrait être implémenté de manière plus simple et plus belle dans VueJS, bien que ce soit un sujet complètement différent.
Dans les ressources du projet, nous voyons une méthode pour générer une archive 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',
	});
};

tout est assez simple, en utilisant la bibliothèque jszip Un zip est créé où les fichiers de configuration sont placés. Après avoir créé l'archive zip, js la transmet au navigateur à l'aide de la bibliothèque FileSaver.js:

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

où content est l'objet blob résultant de l'archive zip.

Ok, tout ce que j'avais à faire était d'ajouter un autre bouton à côté et lorsque je cliquais dessus, je n'enregistrais pas l'archive zip résultante dans le navigateur, mais j'en récupérais le code base64. Après avoir bidouillé un peu, j'ai eu 2 méthodes, au lieu d'un seul 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',
	});
};

Comme vous l'avez peut-être remarqué, j'ai déplacé la génération de l'archive zip elle-même vers la méthode privée generateZip, et ainsi de suite. Il s'agit d'AngularJS, et l'auteur lui-même s'en tient aux rappels et ne l'a pas mis en œuvre par le biais de promesses. downloadZip faisait toujours saveAs en sortie, tandis que downloadBase64 faisait quelque chose de légèrement différent. Nous créons un objet FileReader qui nous est parvenu en html5 et qui est déjà assez accessible pour utilisation. Ce qui, à un moment donné, peut créer une chaîne base64 à partir d'un blob, ou plutôt, cela crée une chaîne DataURL, mais ce n'est pas si important pour nous, car DataURL contient exactement ce dont nous avons besoin. Bingo, un petit accroc m'attendait lorsque j'ai essayé de mettre tout ça dans le buffer. L'auteur a utilisé la bibliothèque dans le projet presse-papiers, qui vous permet de travailler avec le presse-papiers sans objets Flash, en fonction du texte sélectionné. Au départ, j'ai décidé de mettre mon base64 dans un élément avec display:none;, mais dans ce cas je n'ai pas pu le mettre dans le presse-papiers car aucune séparation ne se produit. Par conséquent, au lieu de display:none; Je l'ai fait

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

ce qui m'a permis à la fois de masquer l'élément et de le laisser sur la page. Voila, la tâche était terminée, lorsque j'ai cliqué sur mon bouton, une ligne comme celle-ci a été placée dans le buffer :

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

que j'ai simplement collé dans la console du serveur et j'ai immédiatement reçu une archive zip avec toutes les configurations.
Et bien sûr, j'ai envoyé une pull request à l'auteur, parce que... le projet est actif et vivant, j'aimerais voir les mises à jour de l'auteur et avoir mon propre bouton) Pour ceux que ça intéresse, le voici ma fourchette projet et moi-même demande de tirage, où vous pouvez voir ce que j'ai corrigé/ajouté.
Bon développement à tous)

Génération de configurations pour nginx, historique d'une pull request

Source: habr.com

Ajouter un commentaire