forked from fediversity/fediversity
		
	Progress Indicator (#259)
closes #74 Show progress indicator to track deployment - Disable deploy button when deployment is in progress. Co-authored-by: kevin <kevin@procolix.com> Reviewed-on: Fediversity/Fediversity#259 Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
This commit is contained in:
		
							parent
							
								
									d78995b34c
								
							
						
					
					
						commit
						b4fbc457a6
					
				
					 6 changed files with 91 additions and 45 deletions
				
			
		
							
								
								
									
										2
									
								
								panel/src/panel/static/htmx.min.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								panel/src/panel/static/htmx.min.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | |||
| /home/vg/src/Fediversity/nix/store/mwqqk0qmldzvv4xj9kq2lbah2flhc44z-source/dist/htmx.js | ||||
| /nix/store/mwqqk0qmldzvv4xj9kq2lbah2flhc44z-source/dist/htmx.js | ||||
|  | @ -3,3 +3,24 @@ body | |||
|   margin: 0 | ||||
|   font-family: sans-serif | ||||
|   box-sizing: border-box | ||||
| 
 | ||||
| .loader | ||||
|   width: 48px | ||||
|   height: 48px | ||||
|   border: 5px solid #000 | ||||
|   border-bottom-color: #F34508 | ||||
|   border-radius: 50% | ||||
|   box-sizing: border-box | ||||
|   animation: rotation 1s linear infinite | ||||
|   display: inline-block | ||||
| 
 | ||||
| @keyframes rotation | ||||
|   0% { transform: rotate(0deg) } | ||||
|   100% { transform: rotate(360deg) } | ||||
| 
 | ||||
| #spinner-container | ||||
|   position: absolute | ||||
|   top: 50% | ||||
|   left: 50% | ||||
|   transform: translate(-50%, -50%) | ||||
|   display: block | ||||
|  | @ -1,11 +1,23 @@ | |||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <form method="post" enctype="multipart/form-data" action="{% url 'configuration_form' %}"> | ||||
| <form method="post" enctype="multipart/form-data" action="{% url 'save' %}"> | ||||
|   {% csrf_token %} | ||||
| 
 | ||||
|   {{ form.as_p }} | ||||
|   <button id="deploy-button" class="button" | ||||
|           hx-post="{% url 'deployment_status' %}" | ||||
|           hx-trigger="click" | ||||
|           hx-indicator="#spinner-container" | ||||
|           hx-disabled-elt="this" | ||||
|           hx-swap="none" | ||||
|           name="deploy"> | ||||
|       Deploy | ||||
|   </button> | ||||
| 
 | ||||
|   <button class="button" type="submit" name="deploy">Deploy</button> | ||||
|   <button class="button" type="submit" name="save">Save</button> | ||||
| 
 | ||||
|   <div id="spinner-container" class="htmx-indicator"> | ||||
|     <span class="loader"></span> | ||||
|   </div> | ||||
| </form> | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ class ConfigurationForm(TestCase): | |||
|             password=self.password | ||||
|         ) | ||||
| 
 | ||||
|         self.config_url = reverse('configuration_form') | ||||
|         self.config_url = reverse('save') | ||||
| 
 | ||||
|     def test_configuration_form_submission(self): | ||||
|         config = Configuration.objects.create( | ||||
|  | @ -36,12 +36,13 @@ class ConfigurationForm(TestCase): | |||
|             enable=True, | ||||
|             mastodon_enable=True, | ||||
|         ) | ||||
| 
 | ||||
|         print(form_data) | ||||
|         response = self.client.post(self.config_url, data=form_data) | ||||
| 
 | ||||
|         self.assertEqual(response.status_code, 302) | ||||
|         config.refresh_from_db() | ||||
| 
 | ||||
|         print(config.parsed_value) | ||||
|         self.assertTrue(config.parsed_value.enable) | ||||
|         self.assertTrue(config.parsed_value.mastodon.enable) | ||||
|         # this should not have changed | ||||
|  |  | |||
|  | @ -26,4 +26,6 @@ urlpatterns = [ | |||
|     path("account/", views.AccountDetail.as_view(), name='account_detail'), | ||||
|     path("services/", views.ServiceList.as_view(), name='service_list'), | ||||
|     path("configuration/", views.ConfigurationForm.as_view(), name='configuration_form'), | ||||
|     path("deployment/status/", views.DeploymentStatus.as_view(), name='deployment_status'), | ||||
|     path("save/", views.Save.as_view(), name='save'), | ||||
| ] | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| from enum import Enum | ||||
| import json | ||||
| import subprocess | ||||
| import os | ||||
| 
 | ||||
| from django.urls import reverse_lazy | ||||
| from django.contrib.auth.mixins import LoginRequiredMixin | ||||
|  | @ -9,9 +10,9 @@ from django.views.generic import TemplateView, DetailView | |||
| from django.views.generic.edit import FormView | ||||
| 
 | ||||
| from panel import models, settings | ||||
| from panel import models | ||||
| from panel.configuration import forms | ||||
| 
 | ||||
| 
 | ||||
| class Index(TemplateView): | ||||
|     template_name = 'index.html' | ||||
| 
 | ||||
|  | @ -33,51 +34,12 @@ class ConfigurationForm(LoginRequiredMixin, FormView): | |||
|     success_url = reverse_lazy('configuration_form') | ||||
|     form_class = forms.Form | ||||
| 
 | ||||
|     def get_context_data(self, **kwargs): | ||||
|         context = super().get_context_data(**kwargs) | ||||
|         return context | ||||
| 
 | ||||
|     def get_object(self): | ||||
|         """Get or create the configuration object for the current user""" | ||||
|         obj, created = models.Configuration.objects.get_or_create( | ||||
|             operator=self.request.user, | ||||
|         ) | ||||
| 
 | ||||
|         # Check for deploy button | ||||
|         if "deploy" in self.request.POST.keys(): | ||||
|             submission = obj.parsed_value.model_dump_json() | ||||
|             # FIXME: let the user specify these from the form (#190) | ||||
|             dummy_user = { | ||||
|               "initialUser": { | ||||
|                 "displayName": "Testy McTestface", | ||||
|                 "username": "test", | ||||
|                 "password": "testtest", | ||||
|                 "email": "test@test.com", | ||||
|               }, | ||||
|             } | ||||
|             # serialize back and forth now we still need to manually inject the dummy user | ||||
|             deployment = json.dumps(dummy_user | json.loads(submission)) | ||||
|             env = { | ||||
|                 "PATH": settings.bin_path, | ||||
|                 # pass in form info to our deployment | ||||
|                 "DEPLOYMENT": deployment, | ||||
|             } | ||||
|             cmd = [ | ||||
|                 "nix", | ||||
|                 "develop", | ||||
|                 # workaround to pass in info to nixops4 thru env vars, tho impure :( | ||||
|                 "--extra-experimental-features", | ||||
|                 "configurable-impure-env", | ||||
|                 "--command", | ||||
|                 "nixops4", | ||||
|                 "apply", | ||||
|                 "test", | ||||
|             ] | ||||
|             subprocess.run( | ||||
|                cmd, | ||||
|                cwd=settings.repo_dir, | ||||
|                env=env, | ||||
|            ) | ||||
|         return obj | ||||
| 
 | ||||
|     # TODO(@fricklerhandwerk): | ||||
|  | @ -120,9 +82,57 @@ class ConfigurationForm(LoginRequiredMixin, FormView): | |||
|         initial.update(self.convert_enums_to_names(config_dict)) | ||||
|         return initial | ||||
| 
 | ||||
| class Save(ConfigurationForm): | ||||
|     def form_valid(self, form): | ||||
|         obj = self.get_object() | ||||
|         obj.value = form.to_python().model_dump_json() | ||||
|         obj.save() | ||||
| 
 | ||||
|         return super().form_valid(form) | ||||
| 
 | ||||
| class DeploymentStatus(ConfigurationForm): | ||||
|     def form_valid(self, form): | ||||
|         obj = self.get_object() | ||||
|         obj.save() | ||||
| 
 | ||||
|         # Check for deploy button | ||||
|         if "deploy" in self.request.POST.keys(): | ||||
|             self.deployment(obj) | ||||
| 
 | ||||
|         return super().form_valid(form) | ||||
| 
 | ||||
|     def deployment(self, obj): | ||||
|         submission = obj.parsed_value.model_dump_json() | ||||
|         # FIXME: let the user specify these from the form (#190) | ||||
|         dummy_user = { | ||||
|             "initialUser": { | ||||
|             "displayName": "Testy McTestface", | ||||
|             "username": "test", | ||||
|             "password": "testtest", | ||||
|             "email": "test@test.com", | ||||
|             }, | ||||
|         } | ||||
|         # serialize back and forth now we still need to manually inject the dummy user | ||||
|         deployment = json.dumps(dummy_user | json.loads(submission)) | ||||
|         env = { | ||||
|             "PATH": settings.bin_path, | ||||
|             # pass in form info to our deployment | ||||
|             "DEPLOYMENT": deployment, | ||||
|         } | ||||
|         cmd = [ | ||||
|             "nix", | ||||
|             "develop", | ||||
|             "--extra-experimental-features", | ||||
|             "configurable-impure-env", | ||||
|             "--command", | ||||
|             "nixops4", | ||||
|             "apply", | ||||
|             "test", | ||||
|         ] | ||||
|         deployment_result = subprocess.run( | ||||
|             cmd, | ||||
|             cwd=settings.repo_dir, | ||||
|             env=env, | ||||
|         ) | ||||
|         print(deployment_result) | ||||
|         return deployment_result | ||||
		Loading…
	
	Add table
		
		Reference in a new issue