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