diff --git a/app/__init__.py b/app/__init__.py
index ef13ad4..636dc7c 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -263,11 +263,37 @@ def create_app(
@app.errorhandler(500)
def internal_error(error):
- return render_template('500.html'), 500
+ wants_html = request.accept_mimetypes.accept_html
+ path = request.path or ""
+ if include_ui and wants_html and (path.startswith("/ui") or path == "/"):
+ return render_template('500.html'), 500
+ error_xml = (
+ ''
+ ''
+ 'InternalError'
+ 'An internal server error occurred'
+ f'{path}'
+ f'{getattr(g, "request_id", "-")}'
+ ''
+ )
+ return error_xml, 500, {'Content-Type': 'application/xml'}
@app.errorhandler(CSRFError)
def handle_csrf_error(e):
- return render_template('csrf_error.html', reason=e.description), 400
+ wants_html = request.accept_mimetypes.accept_html
+ path = request.path or ""
+ if include_ui and wants_html and (path.startswith("/ui") or path == "/"):
+ return render_template('csrf_error.html', reason=e.description), 400
+ error_xml = (
+ ''
+ ''
+ 'CSRFError'
+ f'{e.description}'
+ f'{path}'
+ f'{getattr(g, "request_id", "-")}'
+ ''
+ )
+ return error_xml, 400, {'Content-Type': 'application/xml'}
@app.template_filter("filesizeformat")
def filesizeformat(value: int) -> str:
diff --git a/app/s3_api.py b/app/s3_api.py
index f2cfa0b..12a72d1 100644
--- a/app/s3_api.py
+++ b/app/s3_api.py
@@ -2776,9 +2776,14 @@ def object_handler(bucket_name: str, object_key: str):
except StorageError as exc:
return _error_response("InternalError", str(exc), 500)
else:
- stat = path.stat()
- file_size = stat.st_size
- etag = storage._compute_etag(path)
+ try:
+ stat = path.stat()
+ file_size = stat.st_size
+ etag = storage._compute_etag(path)
+ except PermissionError:
+ return _error_response("AccessDenied", "Permission denied accessing object", 403)
+ except OSError as exc:
+ return _error_response("InternalError", f"Failed to access object: {exc}", 500)
if range_header:
try:
@@ -2819,13 +2824,22 @@ def object_handler(bucket_name: str, object_key: str):
except StorageError as exc:
return _error_response("InternalError", str(exc), 500)
else:
- stat = path.stat()
- response = Response(status=200)
- etag = storage._compute_etag(path)
+ try:
+ stat = path.stat()
+ response = Response(status=200)
+ etag = storage._compute_etag(path)
+ except PermissionError:
+ return _error_response("AccessDenied", "Permission denied accessing object", 403)
+ except OSError as exc:
+ return _error_response("InternalError", f"Failed to access object: {exc}", 500)
response.headers["Content-Type"] = mimetype
logged_bytes = 0
- _apply_object_headers(response, file_stat=path.stat() if not is_encrypted else None, metadata=metadata, etag=etag)
+ try:
+ file_stat = path.stat() if not is_encrypted else None
+ except (PermissionError, OSError):
+ file_stat = None
+ _apply_object_headers(response, file_stat=file_stat, metadata=metadata, etag=etag)
if request.method == "GET":
response_overrides = {
diff --git a/app/storage.py b/app/storage.py
index 22391be..f102949 100644
--- a/app/storage.py
+++ b/app/storage.py
@@ -522,7 +522,7 @@ class ObjectStorage:
def get_object_path(self, bucket_name: str, object_key: str) -> Path:
path = self._object_path(bucket_name, object_key)
- if not path.exists():
+ if not path.is_file():
raise ObjectNotFoundError("Object not found")
return path
diff --git a/app/ui.py b/app/ui.py
index 9df2131..8287259 100644
--- a/app/ui.py
+++ b/app/ui.py
@@ -594,7 +594,7 @@ def list_bucket_objects(bucket_name: str):
"etag": obj.etag,
})
- return jsonify({
+ response = jsonify({
"objects": objects_data,
"is_truncated": result.is_truncated,
"next_continuation_token": result.next_continuation_token,
@@ -613,6 +613,8 @@ def list_bucket_objects(bucket_name: str):
"metadata": metadata_template,
},
})
+ response.headers["Cache-Control"] = "no-store"
+ return response
@ui_bp.get("/buckets//objects/stream")