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}}
+
+
+
+
+
+
+{# -*- 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)