mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-09-12 19:50:57 +05:00
115 lines
4.6 KiB
Python
115 lines
4.6 KiB
Python
__package__ = "archivebox.api"
|
|
|
|
|
|
from io import StringIO
|
|
from traceback import format_exception
|
|
from contextlib import redirect_stdout, redirect_stderr
|
|
|
|
from django.http import HttpRequest, HttpResponse
|
|
from django.core.exceptions import ObjectDoesNotExist, EmptyResultSet, PermissionDenied
|
|
from django.contrib.auth.models import User
|
|
|
|
from ninja import NinjaAPI, Swagger
|
|
|
|
# TODO: explore adding https://eadwincode.github.io/django-ninja-extra/
|
|
|
|
from archivebox.config import VERSION
|
|
from archivebox.config.version import get_COMMIT_HASH
|
|
|
|
from archivebox.api.auth import API_AUTH_METHODS
|
|
from archivebox.api.models import APIToken
|
|
|
|
|
|
COMMIT_HASH = get_COMMIT_HASH() or "unknown"
|
|
|
|
html_description = f"""
|
|
<h3>Welcome to your ArchiveBox server's REST API <code>[v1 ALPHA]</code> homepage!</h3>
|
|
<br/>
|
|
<i><b>WARNING: This API is still in an early development stage and may change!</b></i>
|
|
<br/>
|
|
<ul>
|
|
<li>⬅️ Manage your server: <a href="/admin/api/"><b>Setup API Keys</b></a>, <a href="/admin/">Go to your Server Admin UI</a>, <a href="/">Go to your Snapshots list</a>
|
|
<li>💬 Ask questions and get help here: <a href="https://zulip.archivebox.io">ArchiveBox Chat Forum</a></li>
|
|
<li>🐞 Report API bugs here: <a href="https://github.com/ArchiveBox/ArchiveBox/issues">Github Issues</a></li>
|
|
<li>📚 ArchiveBox Documentation: <a href="https://github.com/ArchiveBox/ArchiveBox/wiki">Github Wiki</a></li>
|
|
<li>📜 See the API source code: <a href="https://github.com/ArchiveBox/ArchiveBox/blob/dev/archivebox/api"><code>archivebox/api/</code></a></li>
|
|
</ul>
|
|
<small>Served by ArchiveBox v{VERSION} (<a href="https://github.com/ArchiveBox/ArchiveBox/commit/{COMMIT_HASH}"><code>{COMMIT_HASH[:8]}</code></a>), API powered by <a href="https://django-ninja.dev/"><code>django-ninja</code></a>.</small>
|
|
"""
|
|
|
|
|
|
def register_urls(api: NinjaAPI) -> NinjaAPI:
|
|
api.add_router("/auth/", "archivebox.api.v1_auth.router")
|
|
api.add_router("/core/", "archivebox.api.v1_core.router")
|
|
api.add_router("/crawls/", "archivebox.api.v1_crawls.router")
|
|
api.add_router("/cli/", "archivebox.api.v1_cli.router")
|
|
api.add_router("/machine/", "archivebox.api.v1_machine.router")
|
|
api.add_router("/personas/", "archivebox.api.v1_personas.router")
|
|
return api
|
|
|
|
|
|
class NinjaAPIWithIOCapture(NinjaAPI):
|
|
def create_temporal_response(self, request: HttpRequest) -> HttpResponse:
|
|
stdout, stderr = StringIO(), StringIO()
|
|
|
|
with redirect_stderr(stderr):
|
|
with redirect_stdout(stdout):
|
|
setattr(request, "stdout", stdout)
|
|
setattr(request, "stderr", stderr)
|
|
|
|
response = super().create_temporal_response(request)
|
|
|
|
# Disable caching of API responses entirely
|
|
response["Cache-Control"] = "no-store"
|
|
|
|
# Add debug stdout and stderr headers to response
|
|
response["X-ArchiveBox-Stdout"] = stdout.getvalue().replace("\n", "\\n")[:200]
|
|
response["X-ArchiveBox-Stderr"] = stderr.getvalue().replace("\n", "\\n")[:200]
|
|
# response['X-ArchiveBox-View'] = self.get_openapi_operation_id(request) or 'Unknown'
|
|
|
|
# Add Auth Headers to response
|
|
api_token_attr = request.__dict__.get("_api_token")
|
|
api_token = api_token_attr if isinstance(api_token_attr, APIToken) else None
|
|
token_expiry = api_token.expires.isoformat() if api_token and api_token.expires else "Never"
|
|
|
|
response["X-ArchiveBox-Auth-Method"] = str(request.__dict__.get("_api_auth_method", "None"))
|
|
response["X-ArchiveBox-Auth-Expires"] = token_expiry
|
|
response["X-ArchiveBox-Auth-Token-Id"] = str(api_token.id) if api_token else "None"
|
|
response["X-ArchiveBox-Auth-User-Id"] = str(request.user.pk) if request.user.pk else "None"
|
|
response["X-ArchiveBox-Auth-User-Username"] = request.user.username if isinstance(request.user, User) else "None"
|
|
|
|
return response
|
|
|
|
|
|
api = NinjaAPIWithIOCapture(
|
|
title="ArchiveBox API",
|
|
description=html_description,
|
|
version=VERSION,
|
|
auth=API_AUTH_METHODS,
|
|
urls_namespace="api-1",
|
|
docs=Swagger(settings={"persistAuthorization": True}),
|
|
)
|
|
api = register_urls(api)
|
|
urls = api.urls
|
|
|
|
|
|
@api.exception_handler(Exception)
|
|
def generic_exception_handler(request, err):
|
|
status = 503
|
|
if isinstance(err, (ObjectDoesNotExist, EmptyResultSet, PermissionDenied)):
|
|
status = 404
|
|
|
|
print("".join(format_exception(err)))
|
|
|
|
return api.create_response(
|
|
request,
|
|
{
|
|
"succeeded": False,
|
|
"message": f"{err.__class__.__name__}: {err}",
|
|
"errors": [
|
|
"".join(format_exception(err)),
|
|
],
|
|
},
|
|
status=status,
|
|
)
|