From 0254c8248c2243f29e9507618265d5560755be58 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Fri, 21 Jun 2024 18:55:02 +0200
Subject: [PATCH 1/7] feature: on the fly ssr generation

---
 djangoldp/views.py | 66 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 47 insertions(+), 19 deletions(-)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index 40897690..f4453e42 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -1,13 +1,17 @@
 import json
+import logging
+import os
+
 import validators
 from django.apps import apps
 from django.conf import settings
 from django.contrib.auth import get_user_model
+from django.contrib.auth.models import AnonymousUser
 from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
 from django.db import IntegrityError, transaction
-from django.http import JsonResponse, Http404, HttpResponseNotFound
+from django.http import Http404, HttpResponseNotFound, JsonResponse
 from django.shortcuts import get_object_or_404
-from django.urls import include, re_path, path
+from django.urls import include, path, re_path
 from django.urls.resolvers import get_resolver
 from django.utils.decorators import classonlymethod
 from django.views import View
@@ -23,15 +27,16 @@ from rest_framework.utils import model_meta
 from rest_framework.views import APIView
 from rest_framework.viewsets import ModelViewSet
 
+from djangoldp.activities import (ACTIVITY_SAVING_SETTING, ActivityPubService,
+                                  ActivityQueueService, as_activitystream)
+from djangoldp.activities.errors import (ActivityStreamDecodeError,
+                                         ActivityStreamValidationError)
 from djangoldp.endpoints.webfinger import WebFingerEndpoint, WebFingerError
-from djangoldp.models import LDPSource, Model, Follower, DynamicNestedField
-from djangoldp.filters import LocalObjectOnContainerPathBackend, SearchByQueryParamFilterBackend
+from djangoldp.filters import (LocalObjectOnContainerPathBackend,
+                               SearchByQueryParamFilterBackend)
+from djangoldp.models import DynamicNestedField, Follower, LDPSource, Model
 from djangoldp.related import get_prefetch_fields
 from djangoldp.utils import is_authenticated_user
-from djangoldp.activities import ActivityQueueService, as_activitystream, ACTIVITY_SAVING_SETTING, ActivityPubService
-from djangoldp.activities.errors import ActivityStreamDecodeError, ActivityStreamValidationError
-import logging
-import os
 
 logger = logging.getLogger('djangoldp')
 get_user_model()._meta.rdf_context = {"get_full_name": "rdfs:label"}
@@ -619,20 +624,43 @@ class WebFingerView(View):
 
 
 def serve_static_content(request, path):
-    file_path = os.path.join('ssr', path[:-1])
+
+    output_dir = 'ssr'
+    if not os.path.exists(output_dir):
+        os.makedirs(output_dir, exist_ok=True)
+
+    file_path = os.path.join(output_dir, path[:-1])
     if not file_path.endswith('.jsonld'):
         file_path += '.jsonld'
 
+    if not os.path.exists(file_path):
+
+        resolver = get_resolver()
+        match = resolver.resolve('/' + path)
+        request.user = AnonymousUser()
+        response = match.func(request, *match.args, **match.kwargs)
+        if response.status_code == 200:
+            directory = os.path.dirname(file_path)
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            json_content = JSONRenderer().render(response.data)
+            print(json_content)
+            with open(file_path, 'w', encoding='utf-8') as f:
+                f.write(json_content.decode('utf-8'))
+
     if os.path.exists(file_path):
-        with open(file_path, 'r') as file:
+        with open(file_path, 'r', encoding='utf-8') as file:
             content = file.read()
 
-        json_content = json.loads(content)
-        return JsonResponse(json_content, safe=False, status=200,
-                            content_type='application/ld+json',
-                            headers={
-                              'Access-Control-Allow-Origin': '*',
-                              'Cache-Control': 'public, max-age=3600',
-                            })
-    else:
-        return HttpResponseNotFound('File not found')
+        try:
+            json_content = json.loads(content)
+            return JsonResponse(json_content, safe=False, status=200,
+                                content_type='application/ld+json',
+                                headers={
+                                    'Access-Control-Allow-Origin': '*',
+                                    'Cache-Control': 'public, max-age=3600',
+                                })
+        except json.JSONDecodeError:
+            pass
+
+    return HttpResponseNotFound('File not found')
-- 
GitLab


From 993aa3ee3d46bf7b3b10bb02fdb6951034e0c629 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Fri, 21 Jun 2024 18:55:08 +0200
Subject: [PATCH 2/7] fix: encoding

---
 .../management/commands/generate_static_content.py  | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/djangoldp/management/commands/generate_static_content.py b/djangoldp/management/commands/generate_static_content.py
index 45648582..b7d64d11 100644
--- a/djangoldp/management/commands/generate_static_content.py
+++ b/djangoldp/management/commands/generate_static_content.py
@@ -1,10 +1,11 @@
+import json
 import os
+from urllib.parse import urlparse, urlunparse
+
 import requests
-import json
-from django.core.management.base import BaseCommand
-from django.conf import settings
 from django.apps import apps
-from urllib.parse import urlparse, urlunparse
+from django.conf import settings
+from django.core.management.base import BaseCommand
 
 base_uri = getattr(settings, 'BASE_URL', '')
 max_depth = getattr(settings, 'MAX_RECURSION_DEPTH', 5)
@@ -45,7 +46,7 @@ class Command(BaseCommand):
                       file_path = os.path.join(output_dir, f'{filename}.jsonld')
 
                       print(f"Output file_path: {file_path}")
-                      with open(file_path, 'w') as f:
+                      with open(file_path, 'w', encoding='utf-8') as f:
                           f.write(content)
                       self.stdout.write(self.style.SUCCESS(f'Successfully fetched and saved content for {model._meta.model_name} from {url}'))
                       regenerated_urls.append(url)
@@ -96,7 +97,7 @@ class Command(BaseCommand):
 
                     if not os.path.exists(associated_file_dir):
                         os.makedirs(associated_file_dir)
-                    with open(associated_file_path, 'w') as f:
+                    with open(associated_file_path, 'w', encoding='utf-8') as f:
                         f.write(associated_content)
                     regenerated_urls.append(associated_url)
                     self.stdout.write(self.style.SUCCESS(f'Successfully fetched and saved associated content for {associated_url}'))
-- 
GitLab


From b832338f0e2a0615a11005a7b7b948bffb7961d2 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Fri, 21 Jun 2024 19:00:54 +0200
Subject: [PATCH 3/7] feature: remove file older than 24h

---
 djangoldp/views.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index f4453e42..0325411d 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -1,6 +1,7 @@
 import json
 import logging
 import os
+import time
 
 import validators
 from django.apps import apps
@@ -633,6 +634,13 @@ def serve_static_content(request, path):
     if not file_path.endswith('.jsonld'):
         file_path += '.jsonld'
 
+    if os.path.exists(file_path):
+        current_time = time.time()
+        file_mod_time = os.path.getmtime(file_path)
+        time_difference = current_time - file_mod_time
+        if time_difference > 24 * 60 * 60:
+            os.remove(file_path)
+
     if not os.path.exists(file_path):
 
         resolver = get_resolver()
-- 
GitLab


From f2c6480dcb67c2f8639235e2e430f1fee160cebc Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 24 Jun 2024 12:18:32 +0200
Subject: [PATCH 4/7] fix: ids, context, ssr on the fly

---
 djangoldp/views.py | 49 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 34 insertions(+), 15 deletions(-)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index 0325411d..1ffd3733 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -626,13 +626,15 @@ class WebFingerView(View):
 
 def serve_static_content(request, path):
 
-    output_dir = 'ssr'
+    server_url = getattr(settings, "BASE_URL", "http://localhost")
+
+    output_dir = "ssr"
     if not os.path.exists(output_dir):
         os.makedirs(output_dir, exist_ok=True)
 
     file_path = os.path.join(output_dir, path[:-1])
-    if not file_path.endswith('.jsonld'):
-        file_path += '.jsonld'
+    if not file_path.endswith(".jsonld"):
+        file_path += ".jsonld"
 
     if os.path.exists(file_path):
         current_time = time.time()
@@ -644,7 +646,7 @@ def serve_static_content(request, path):
     if not os.path.exists(file_path):
 
         resolver = get_resolver()
-        match = resolver.resolve('/' + path)
+        match = resolver.resolve("/" + path)
         request.user = AnonymousUser()
         response = match.func(request, *match.args, **match.kwargs)
         if response.status_code == 200:
@@ -652,23 +654,40 @@ def serve_static_content(request, path):
             if not os.path.exists(directory):
                 os.makedirs(directory)
             json_content = JSONRenderer().render(response.data)
-            print(json_content)
-            with open(file_path, 'w', encoding='utf-8') as f:
-                f.write(json_content.decode('utf-8'))
+            with open(file_path, "w", encoding="utf-8") as f:
+                f.write(
+                    json_content.decode("utf-8")
+                    .replace('"@id":"' + server_url, '"@id":"' + server_url + "/ssr")
+                    .replace(
+                        '"@id":"' + server_url + "/ssr/ssr",
+                        '"@id":"' + server_url + "/ssr",
+                    )[:-1]
+                    + ',"@context": "'
+                    + getattr(
+                        settings,
+                        "LDP_RDF_CONTEXT",
+                        "https://cdn.startinblox.com/owl/context.jsonld",
+                    )
+                    + '"}'
+                )
 
     if os.path.exists(file_path):
-        with open(file_path, 'r', encoding='utf-8') as file:
+        with open(file_path, "r", encoding="utf-8") as file:
             content = file.read()
 
         try:
             json_content = json.loads(content)
-            return JsonResponse(json_content, safe=False, status=200,
-                                content_type='application/ld+json',
-                                headers={
-                                    'Access-Control-Allow-Origin': '*',
-                                    'Cache-Control': 'public, max-age=3600',
-                                })
+            return JsonResponse(
+                json_content,
+                safe=False,
+                status=200,
+                content_type="application/ld+json",
+                headers={
+                    "Access-Control-Allow-Origin": "*",
+                    "Cache-Control": "public, max-age=3600",
+                },
+            )
         except json.JSONDecodeError:
             pass
 
-    return HttpResponseNotFound('File not found')
+    return HttpResponseNotFound("File not found")
-- 
GitLab


From cee6622eb843e1eb53eb219cf1685d67acc3d3f3 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 24 Jun 2024 12:29:26 +0200
Subject: [PATCH 5/7] fix: only cache GET requests

---
 djangoldp/views.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index 1ffd3733..2b402be7 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -643,7 +643,7 @@ def serve_static_content(request, path):
         if time_difference > 24 * 60 * 60:
             os.remove(file_path)
 
-    if not os.path.exists(file_path):
+    if not os.path.exists(file_path) and request.method == "GET":
 
         resolver = get_resolver()
         match = resolver.resolve("/" + path)
-- 
GitLab


From 69746d3ebde6ecb6c74591e19fe6a9e28ea6ffe5 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 24 Jun 2024 12:38:07 +0200
Subject: [PATCH 6/7] fix: allow OPTIONS/POST/PUT requests

---
 djangoldp/views.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index 2b402be7..503e01cd 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -626,6 +626,11 @@ class WebFingerView(View):
 
 def serve_static_content(request, path):
 
+    if request.method != "GET":
+        resolver = get_resolver()
+        match = resolver.resolve("/" + path)
+        return match.func(request, *match.args, **match.kwargs)
+
     server_url = getattr(settings, "BASE_URL", "http://localhost")
 
     output_dir = "ssr"
@@ -643,7 +648,7 @@ def serve_static_content(request, path):
         if time_difference > 24 * 60 * 60:
             os.remove(file_path)
 
-    if not os.path.exists(file_path) and request.method == "GET":
+    if not os.path.exists(file_path):
 
         resolver = get_resolver()
         match = resolver.resolve("/" + path)
-- 
GitLab


From cec625321c58bd2e6004017892c3ee3107773450 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 24 Jun 2024 12:42:04 +0200
Subject: [PATCH 7/7] fix: request on ssr/ should be anonymous even on options

---
 djangoldp/views.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/djangoldp/views.py b/djangoldp/views.py
index 503e01cd..f45e362f 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -629,6 +629,7 @@ def serve_static_content(request, path):
     if request.method != "GET":
         resolver = get_resolver()
         match = resolver.resolve("/" + path)
+        request.user = AnonymousUser()
         return match.func(request, *match.args, **match.kwargs)
 
     server_url = getattr(settings, "BASE_URL", "http://localhost")
-- 
GitLab