diff --git a/src/edrnsite.controls/src/edrnsite/controls/migrations/0007_informatics_ip_address.py b/src/edrnsite.controls/src/edrnsite/controls/migrations/0007_informatics_ip_address.py new file mode 100644 index 00000000..d129c3d1 --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/migrations/0007_informatics_ip_address.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.10 on 2024-07-18 15:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("edrnsitecontrols", "0006_rename_socialmedia_socialmedialink"), + ] + + operations = [ + migrations.AddField( + model_name="informatics", + name="ip_address", + field=models.CharField( + default="unknown", + help_text="Last known source IP address of the portal", + max_length=40, + ), + ), + ] diff --git a/src/edrnsite.controls/src/edrnsite/controls/migrations/0008_informatics_ip_address_service.py b/src/edrnsite.controls/src/edrnsite/controls/migrations/0008_informatics_ip_address_service.py new file mode 100644 index 00000000..bdec4067 --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/migrations/0008_informatics_ip_address_service.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.10 on 2024-07-18 16:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("edrnsitecontrols", "0007_informatics_ip_address"), + ] + + operations = [ + migrations.AddField( + model_name="informatics", + name="ip_address_service", + field=models.URLField( + default="https://api.ipify.org", + help_text="API endpoint of IP address service", + ), + ), + ] diff --git a/src/edrnsite.controls/src/edrnsite/controls/models.py b/src/edrnsite.controls/src/edrnsite/controls/models.py index ffadf2b7..af4adbf3 100644 --- a/src/edrnsite.controls/src/edrnsite/controls/models.py +++ b/src/edrnsite.controls/src/edrnsite/controls/models.py @@ -79,6 +79,12 @@ class Informatics(BaseSiteSetting): ) funding_cycle = models.CharField(default='Ⅴ', max_length=8, null=False, blank=False, help_text='EDRN Funding Cycle') site_wide_banner = RichTextField(blank=True, help_text='Banner to display site-wide at the top of every page') + ip_address = models.CharField( + default='unknown', max_length=40, null=False, blank=False, help_text='Last known source IP address of the portal' + ) + ip_address_service = models.URLField( + default='https://api.ipify.org', null=False, blank=False, help_text='API endpoint of IP address service' + ) panels = [ FieldPanel('in_development'), FieldPanel('entrez_email'), @@ -87,6 +93,8 @@ class Informatics(BaseSiteSetting): FieldPanel('dmcc_url'), FieldPanel('funding_cycle'), FieldPanel('site_wide_banner'), + FieldPanel('ip_address'), + FieldPanel('ip_address_service'), ] diff --git a/src/edrnsite.controls/src/edrnsite/controls/tasks.py b/src/edrnsite.controls/src/edrnsite/controls/tasks.py new file mode 100644 index 00000000..f8153008 --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/tasks.py @@ -0,0 +1,30 @@ +# encoding: utf-8 + +'''🎛 EDRN Site Controls: asynchronous tasks.''' + +from .models import Informatics +from celery import shared_task +from django.core.cache import cache +from urllib.request import urlopen +from wagtail.models import Site +import logging + + +_logger = logging.getLogger(__name__) + + +@shared_task +def do_update_my_ip(): + _logger.info('🔓 Getting lock for `update_my_up`') + with cache.lock('update_my_ip', timeout=300): + settings = Informatics.for_site(Site.objects.filter(is_default_site=True).first()) + _logger.info('🤓 Looking up my IP with %s', settings.ip_address_service) + try: + with urlopen(settings.ip_address_service) as io: + my_ip = io.read().decode('utf-8') + _logger.info('🎉 Got %s', my_ip) + except Exception as ex: + my_ip = str(ex) + _logger.exception('😔 Could not get my_ip') + settings.ip_address = my_ip + settings.save() diff --git a/src/edrnsite.controls/src/edrnsite/controls/templates/edrnsite.controls/edrnsite-controls.html b/src/edrnsite.controls/src/edrnsite/controls/templates/edrnsite.controls/edrnsite-controls.html new file mode 100644 index 00000000..0881871f --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/templates/edrnsite.controls/edrnsite-controls.html @@ -0,0 +1,18 @@ +{% load wagtailadmin_tags %} +
+
+
+

EDRN Site Controls

+
+
My IP
+
{{my_ip}}
+
+ +
+
+ Update my IP +
+
+
+{# -*- Django HTML -*- #} diff --git a/src/edrnsite.controls/src/edrnsite/controls/urls.py b/src/edrnsite.controls/src/edrnsite/controls/urls.py new file mode 100644 index 00000000..4e3a8dc8 --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/urls.py @@ -0,0 +1,12 @@ +# encoding: utf-8 + +'''🎛 EDRN Site Controls: URL patterns.''' + + +from .views import update_my_ip +from django.urls import path + + +urlpatterns = [ + path('update_my_ip', update_my_ip, name='update_my_ip'), +] diff --git a/src/edrnsite.controls/src/edrnsite/controls/views.py b/src/edrnsite.controls/src/edrnsite/controls/views.py new file mode 100644 index 00000000..ebab777e --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/views.py @@ -0,0 +1,24 @@ +# encoding: utf-8 + +'''🎛 EDRN Site Controls: views.''' + +from .tasks import do_update_my_ip +from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, HttpResponseForbidden +from edrn.auth.views import logged_in_or_basicauth + + +def _get_referrer(request: HttpRequest) -> str: + try: + return request.META['HTTP_REFERER'] + except KeyError: + return '/' + + +@logged_in_or_basicauth('edrn') +def update_my_ip(request: HttpRequest) -> HttpResponse: + '''Update my IP address.''' + if request.user.is_staff or request.user.is_superuser: + do_update_my_ip.delay() + return HttpResponseRedirect(_get_referrer(request)) + else: + return HttpResponseForbidden() diff --git a/src/edrnsite.controls/src/edrnsite/controls/wagtail_hooks.py b/src/edrnsite.controls/src/edrnsite/controls/wagtail_hooks.py new file mode 100644 index 00000000..453be36d --- /dev/null +++ b/src/edrnsite.controls/src/edrnsite/controls/wagtail_hooks.py @@ -0,0 +1,29 @@ +# encoding: utf-8 + +'''🎛 EDRN Site Controls: Wagtail hooks and interceptors.''' + +from .models import Informatics +from django.http import HttpRequest +from django.template.loader import render_to_string +from wagtail import hooks +from wagtail.admin.ui.components import Component + + +class EDRNSiteControlsControlPanel(Component): + '''Custom control panel for EDRN site controls.''' + + name = 'edrnsite_controls' + order = 210 + + def __init__(self, request: HttpRequest): + self.request = request + + def render_html(self, parent_context: list) -> str: + context = {'my_ip': Informatics.for_request(self.request).ip_address} + return render_to_string('edrnsite.controls/edrnsite-controls.html', context, request=self.request) + + +@hooks.register('construct_homepage_panels') +def add_ingest_controls(request: HttpRequest, panels: list): + '''Add the custom EDRN site controls control panel.''' + panels.append(EDRNSiteControlsControlPanel(request)) diff --git a/src/edrnsite.policy/src/edrnsite/policy/urls.py b/src/edrnsite.policy/src/edrnsite/policy/urls.py index d6ceec15..8613c3ff 100644 --- a/src/edrnsite.policy/src/edrnsite/policy/urls.py +++ b/src/edrnsite.policy/src/edrnsite/policy/urls.py @@ -11,16 +11,17 @@ from django.urls import path, include, re_path from edrn.auth.urls import urlpatterns as edrnAuthURLs from edrn.metrics.urls import urlpatterns as edrn_metrics_urls +from edrnsite.controls.urls import urlpatterns as edrnsite_controls_urlpatterns from edrnsite.search.urls import urlpatterns as edrnSiteSearchURLs from eke.knowledge.urls import urlpatterns as ekeKnowledgeURLs +from wagtail import urls as wagtail_urls from wagtail.admin import urls as wagtailadmin_urls from wagtail.contrib.sitemaps.views import sitemap -from wagtail import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls from wagtail_favicon.urls import urls as favicon_urls -urlpatterns = ekeKnowledgeURLs + edrnSiteSearchURLs + edrnAuthURLs + edrn_metrics_urls + [ +urlpatterns = edrnsite_controls_urlpatterns + ekeKnowledgeURLs + edrnSiteSearchURLs + edrnAuthURLs + edrn_metrics_urls + [ path('clear-caches', clear_caches, name='clear_caches'), path('django-admin/', admin.site.urls), path('admin/', include(wagtailadmin_urls)), diff --git a/src/eke.knowledge/src/eke/knowledge/templates/eke.knowledge/ingest-controls.html b/src/eke.knowledge/src/eke/knowledge/templates/eke.knowledge/ingest-controls.html index 29a4ff89..eb3d066a 100644 --- a/src/eke.knowledge/src/eke/knowledge/templates/eke.knowledge/ingest-controls.html +++ b/src/eke.knowledge/src/eke/knowledge/templates/eke.knowledge/ingest-controls.html @@ -6,8 +6,6 @@

EDRN Ingest Controls

{% if last_ingest_start %}
-
My IP
-
{{my_ip}}
Last started
{{last_ingest_start|naturaltime}}
Duration
diff --git a/src/eke.knowledge/src/eke/knowledge/wagtail_hooks.py b/src/eke.knowledge/src/eke/knowledge/wagtail_hooks.py index ad00116e..81e2cc90 100644 --- a/src/eke.knowledge/src/eke/knowledge/wagtail_hooks.py +++ b/src/eke.knowledge/src/eke/knowledge/wagtail_hooks.py @@ -8,8 +8,6 @@ from django.template.loader import render_to_string from wagtail.admin.ui.components import Component from wagtail import hooks -from urllib.request import urlopen -import os class IngestControlPanel(Component): @@ -27,20 +25,11 @@ def render_html(self, parent_context: list) -> str: settings = RDFIngest.for_request(self.request) folders = KnowledgeFolder.objects.all().order_by('ingest_order') - # ipify.org is having problems - # try: - # with urlopen(self._ip_service) as f: - # my_ip = f.read().decode('utf-8') - # except Exception as ex: - # my_ip = str(ex) - my_ip = 'unknown' - context = { 'last_ingest_start': settings.last_ingest_start, 'last_ingest_duration': settings.last_ingest_duration, 'ingest_running': lock.locked(), 'knowledge_folders': folders, - 'my_ip': my_ip, } return render_to_string('eke.knowledge/ingest-controls.html', context, request=self.request)