diff --git a/boot.sh b/boot.sh index edd81506d..bced70dd5 100755 --- a/boot.sh +++ b/boot.sh @@ -10,6 +10,8 @@ GUNICORN_WORKERS="${GUNICORN_WORKERS:-3}" GUNICORN_THREADS="${GUNICORN_THREADS:-2}" GUNICORN_LOG_LEVEL="${GUNICORN_LOG_LEVEL:-'info'}" +PLUGINS_BUILD="${PLUGINS_BUILD:-0}" + if [ "${TANDOOR_PORT}" -eq 80 ]; then echo "TANDOOR_PORT set to 8080 because 80 is now taken by the integrated nginx" TANDOOR_PORT=8080 @@ -82,9 +84,15 @@ echo "Database is ready" echo "Migrating database" - python manage.py migrate +if [ "${PLUGINS_BUILD}" -eq 1 ]; then + echo "Running yarn build at startup because PLUGINS_BUILD is enabled" + cd vue3 + yarn build + cd .. +fi + echo "Collecting static files, this may take a while..." python manage.py collectstatic --noinput diff --git a/cookbook/templates/system.html b/cookbook/templates/system.html index 7647bfeee..ee673671e 100644 --- a/cookbook/templates/system.html +++ b/cookbook/templates/system.html @@ -53,6 +53,17 @@ {% endblocktrans %} {% endif %} +

{% trans 'Plugins' %}

+ Clone the plugin using git into the recipe/plugin/ folder (Docker mount /opt/recipe/recipes/plugins to the host system and clone into it). + + {% for p in plugins %} + + + + + {% endfor %} +
{{ p.name }}
{{ p.base_path }}
Git Pull
+

{% trans 'Media Serving' %} {% if gunicorn_media %} {% trans 'Warning' %}{% else %}{% trans 'Ok' %}{% endif %}

{% if gunicorn_media %} diff --git a/cookbook/urls.py b/cookbook/urls.py index d91bfecfa..cbc58bbe7 100644 --- a/cookbook/urls.py +++ b/cookbook/urls.py @@ -80,8 +80,9 @@ urlpatterns = [ path('switch-space/', views.switch_space, name='view_switch_space'), path('no-perm/', views.no_perm, name='view_no_perm'), path('invite/', views.invite_link, name='view_invite'), - path('system/', views.system, name='view_system'), + path('system/', views.system, name='view_system'), + path('plugin/update/', views.plugin_update, name='view_plugin_update'), path('abuse/', views.report_share_abuse, name='view_report_share_abuse'), diff --git a/cookbook/views/views.py b/cookbook/views/views.py index 1c8748ad0..04f973704 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -1,5 +1,6 @@ import os import re +import subprocess from datetime import datetime, timedelta from io import StringIO from uuid import UUID @@ -13,7 +14,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.contrib.auth.password_validation import validate_password from django.core.cache import caches -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError, PermissionDenied from django.core.management import call_command from django.db import models from django.http import HttpResponseRedirect, JsonResponse, HttpResponse @@ -24,6 +25,7 @@ from django.utils.datetime_safe import date from django.utils.translation import gettext as _ from django_scopes import scopes_disabled from drf_spectacular.views import SpectacularRedocView, SpectacularSwaggerView +from google.api_core.exceptions import BadRequest from cookbook.forms import Recipe, SpaceCreateForm, SpaceJoinForm, User, UserCreateForm from cookbook.helper.HelperFunctions import str2bool @@ -266,6 +268,22 @@ def system(request): }) +def plugin_update(request): + if not request.user.is_superuser: + raise PermissionDenied + + if not 'module' in request.GET: + raise BadRequest + + for p in PLUGINS: + if p['module'] == request.GET['module']: + update_response = subprocess.check_output(['git', 'pull'], cwd=p['base_path']) + print(update_response) + return HttpResponseRedirect(reverse('view_system')) + + return HttpResponseRedirect(reverse('view_system')) + + def setup(request): with scopes_disabled(): if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS: diff --git a/recipes/settings.py b/recipes/settings.py index d9cd8606b..a98d550e5 100644 --- a/recipes/settings.py +++ b/recipes/settings.py @@ -221,10 +221,7 @@ try: 'module': f'recipes.plugins.{d}', 'base_path': os.path.join(BASE_DIR, 'recipes', 'plugins', d), 'base_url': plugin_class.base_url, - 'bundle_name': plugin_class.bundle_name if hasattr(plugin_class, 'bundle_name') else '', 'api_router_name': plugin_class.api_router_name if hasattr(plugin_class, 'api_router_name') else '', - 'nav_main': plugin_class.nav_main if hasattr(plugin_class, 'nav_main') else '', - 'nav_dropdown': plugin_class.nav_dropdown if hasattr(plugin_class, 'nav_dropdown') else '', } PLUGINS.append(plugin_config) print(f'PLUGIN {d} loaded') @@ -534,28 +531,6 @@ if REDIS_HOST: # Vue webpack settings VUE_DIR = os.path.join(BASE_DIR, 'vue') -WEBPACK_LOADER = { - 'DEFAULT': { - 'CACHE': not DEBUG, - 'BUNDLE_DIR_NAME': 'vue/', # must end with slash - 'STATS_FILE': os.path.join(VUE_DIR, 'webpack-stats.json'), - 'POLL_INTERVAL': 0.1, - 'TIMEOUT': None, - 'IGNORE': [r'.+\.hot-update.js', r'.+\.map'], - }, -} - -for p in PLUGINS: - if p['bundle_name'] != '': - WEBPACK_LOADER[p['bundle_name']] = { - 'CACHE': not DEBUG, - 'BUNDLE_DIR_NAME': 'vue/', # must end with slash - 'STATS_FILE': os.path.join(p["base_path"], 'vue', 'webpack-stats.json'), - 'POLL_INTERVAL': 0.1, - 'TIMEOUT': None, - 'IGNORE': [r'.+\.hot-update.js', r'.+\.map'], - } - DJANGO_VITE = { "default": { "dev_mode": False,