diff --git a/api/tacticalrmm/ee/reporting/markdown/reportasset_ext.py b/api/tacticalrmm/ee/reporting/markdown/reportasset_ext.py index ef4c674d..a9c9177a 100644 --- a/api/tacticalrmm/ee/reporting/markdown/reportasset_ext.py +++ b/api/tacticalrmm/ee/reporting/markdown/reportasset_ext.py @@ -45,7 +45,7 @@ class ReportAssetPreprocessor(Preprocessor): asset: "ReportAssetType" = ReportAsset.objects.get(id=asset_id) - new_line = line.replace(f"asset://{asset_id}", asset.file.url) + new_line = line.replace(f"asset://{asset_id}", f"{asset.file.url}?id={asset_id}") new_lines.append(new_line) else: diff --git a/api/tacticalrmm/ee/reporting/urls.py b/api/tacticalrmm/ee/reporting/urls.py index 574b18b9..dad290c1 100644 --- a/api/tacticalrmm/ee/reporting/urls.py +++ b/api/tacticalrmm/ee/reporting/urls.py @@ -15,6 +15,7 @@ urlpatterns = [ path("templates/preview/", views.GenerateReportPreview.as_view()), # report assets path("assets/", views.GetReportAssets.as_view()), + path("assets/all/", views.GetAllAssets.as_view()), path("assets/rename/", views.RenameReportAsset.as_view()), path("assets/newfolder/", views.CreateAssetFolder.as_view()), path("assets/delete/", views.DeleteAssets.as_view()), @@ -27,5 +28,5 @@ urlpatterns = [ path("dataqueries/", views.GetAddReportDataQuery.as_view()), path("dataqueries//", views.GetEditDeleteReportDataQuery.as_view()), # serving assets - path("assets/", views.redirect_assets_to_nginx_if_authenticated), + path("assets/", views.NginxRedirect.as_view()), ] diff --git a/api/tacticalrmm/ee/reporting/views.py b/api/tacticalrmm/ee/reporting/views.py index fe8dbe10..32aa08ae 100644 --- a/api/tacticalrmm/ee/reporting/views.py +++ b/api/tacticalrmm/ee/reporting/views.py @@ -14,6 +14,7 @@ from rest_framework.serializers import ( ListField, ValidationError, ) +from rest_framework.permissions import AllowAny from typing import Union, List from django.core.exceptions import ( SuspiciousFileOperation, @@ -119,8 +120,8 @@ class GenerateReportPreview(APIView): ) html_report = generate_html( - template=request.data["template_md"] if template_type == "markdown" else request.data["template_html"], - template_type=request.data["type"], + template=template_md, + template_type=template_type, css=template_css, html_template=template_html, variables=request.data["template_variables"], @@ -181,39 +182,53 @@ class GetAllAssets(APIView): only_folders = request.query_params.get("OnlyFolders", None) only_folders = True if only_folders and only_folders == "true" else False - response = {} + # pull report assets from the database so we can pair with the file system assets + assets = ReportAsset.objects.all() - # recursively loop over report assets and add them to response - try: - os.chdir(report_assets_fs.base_location) - except FileNotFoundError: - return notify_error("Unable to process request") - - for current_dir, subdirs, files in os.walk("."): - nodes = list() - - for dirname in subdirs: - nodes.append( - { - "type": "folder", - "name": dirname, - "path": f"{current_dir}/{dirname}", - } - ) - - if not only_folders: - for filename in files: - nodes.append( - { - "type": "file", - "name": filename, - "path": f"{current_dir}/{filename}", - } + def walk_folder_and_return_node(path: str): + for current_dir, subdirs, files in os.walk(path): + current_dir = "Report Assets" if current_dir == "." else current_dir + node = { + "type": "folder", + "name": current_dir.replace("./", ""), + "path": path.replace("./", ""), + "children": [], + "selectable": False, + "icon": "folder", + "iconColor": "yellow-9" + } + for dirname in subdirs: + dirpath = f"{path}/{dirname}" + node["children"].append( + walk_folder_and_return_node(dirpath) # recursively call ) - response[current_dir] = nodes + if not only_folders: + for filename in files: + path = f"{current_dir.replace('./', '')}/{filename}" + try: + # need to remove the relative path + id = assets.get(file=path).id + node["children"].append( + { + "id": id, + "type": "file", + "name": filename, + "path": path, + "icon": "description" + } + ) + except ReportAsset.DoesNotExist: + pass + + return node - return Response(response) + try: + os.chdir(report_assets_fs.base_location) + response = [walk_folder_and_return_node(".")] + return Response(response) + except FileNotFoundError: + return notify_error("Unable to process request") class RenameReportAsset(APIView): @@ -485,11 +500,17 @@ class GetEditDeleteReportDataQuery(APIView): return Response() +class NginxRedirect(APIView): + permission_classes = (AllowAny,) -def redirect_assets_to_nginx_if_authenticated(request: Request, path: str) -> HttpResponse: - if request.user.is_authenticated: - response = HttpResponse() - response["X-Accel-Redirect"] = "/assets/" + path - return response - else: - raise PermissionDenied() + def get(self, request: Request, path: str) -> HttpResponse: + + id = request.query_params.get("id", "") + asset = ReportAsset.objects.get(id=id) + new_path = path.split("?")[0] + if asset.file.name == new_path: + response = HttpResponse(status=200) + response["X-Accel-Redirect"] = "/assets/" + new_path + return response + else: + raise PermissionDenied() diff --git a/docker/containers/tactical-nginx/entrypoint.sh b/docker/containers/tactical-nginx/entrypoint.sh index fc5f6911..912f04d2 100644 --- a/docker/containers/tactical-nginx/entrypoint.sh +++ b/docker/containers/tactical-nginx/entrypoint.sh @@ -114,7 +114,8 @@ server { location /assets/ { internal; - alias /opt/tactical/reporting/; + add_header "Access-Control-Allow-Origin" "https://${APP_HOST}"; + alias /opt/tactical/reporting/assets/; } location ~ ^/natsws { diff --git a/update.sh b/update.sh index 37d27ea6..6f0b50be 100644 --- a/update.sh +++ b/update.sh @@ -131,7 +131,8 @@ if ! [[ $CHECK_ASSETS_NGINX ]]; then /location \/ {/ { print " location /assets/ {" print " internal;" - print " alias /opt/tactical/reporting/;" + print " add_header 'Access-Control-Allow-Origin' 'https://${APP_HOST}';" + print " alias /opt/tactical/reporting/assets/;" print " }" print "\n" }