mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-25 08:41:00 +00:00
use logging instead of sentry.capture_* (#646)
This commit is contained in:
parent
84ded2f3a5
commit
9368996a5b
13 changed files with 69 additions and 95 deletions
|
@ -1,5 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import ssl
|
import ssl
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
|
@ -27,7 +28,7 @@ from activities.models.post_types import (
|
||||||
PostTypeDataEncoder,
|
PostTypeDataEncoder,
|
||||||
QuestionData,
|
QuestionData,
|
||||||
)
|
)
|
||||||
from core.exceptions import ActivityPubFormatError, capture_message
|
from core.exceptions import ActivityPubFormatError
|
||||||
from core.html import ContentRenderer, FediverseHtmlParser
|
from core.html import ContentRenderer, FediverseHtmlParser
|
||||||
from core.ld import (
|
from core.ld import (
|
||||||
canonicalise,
|
canonicalise,
|
||||||
|
@ -896,7 +897,7 @@ class Post(StatorModel):
|
||||||
# don't have content, but this shouldn't be a total failure
|
# don't have content, but this shouldn't be a total failure
|
||||||
post.content = get_value_or_map(data, "content", "contentMap")
|
post.content = get_value_or_map(data, "content", "contentMap")
|
||||||
except ActivityPubFormatError as err:
|
except ActivityPubFormatError as err:
|
||||||
capture_message(f"{err} on {post.url}")
|
logging.warning(f"{err} on {post.url}")
|
||||||
post.content = None
|
post.content = None
|
||||||
# Document types have names, not summaries
|
# Document types have names, not summaries
|
||||||
post.summary = data.get("summary") or data.get("name")
|
post.summary = data.get("summary") or data.get("name")
|
||||||
|
@ -992,7 +993,7 @@ class Post(StatorModel):
|
||||||
try:
|
try:
|
||||||
cls.ensure_object_uri(post.in_reply_to, reason=post.object_uri)
|
cls.ensure_object_uri(post.in_reply_to, reason=post.object_uri)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
capture_message(
|
logging.warning(
|
||||||
f"Cannot fetch ancestor of Post={post.pk}, ancestor_uri={post.in_reply_to}"
|
f"Cannot fetch ancestor of Post={post.pk}, ancestor_uri={post.in_reply_to}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from activities.models import (
|
from activities.models import (
|
||||||
Post,
|
Post,
|
||||||
PostInteraction,
|
PostInteraction,
|
||||||
|
@ -5,7 +7,6 @@ from activities.models import (
|
||||||
PostStates,
|
PostStates,
|
||||||
TimelineEvent,
|
TimelineEvent,
|
||||||
)
|
)
|
||||||
from core.exceptions import capture_message
|
|
||||||
from users.models import Identity
|
from users.models import Identity
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ class PostService:
|
||||||
try:
|
try:
|
||||||
Post.ensure_object_uri(object_uri, reason=reason)
|
Post.ensure_object_uri(object_uri, reason=reason)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
capture_message(
|
logging.error(
|
||||||
f"Cannot fetch ancestor Post={self.post.pk}, ancestor_uri={object_uri}"
|
f"Cannot fetch ancestor Post={self.post.pk}, ancestor_uri={object_uri}"
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
import traceback
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class ActivityPubError(BaseException):
|
class ActivityPubError(BaseException):
|
||||||
"""
|
"""
|
||||||
A problem with an ActivityPub message
|
A problem with an ActivityPub message
|
||||||
|
@ -19,29 +14,3 @@ class ActorMismatchError(ActivityPubError):
|
||||||
"""
|
"""
|
||||||
The actor is not authorised to do the action we saw
|
The actor is not authorised to do the action we saw
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def capture_message(message: str, level: str | None = None, scope=None, **scope_args):
|
|
||||||
"""
|
|
||||||
Sends the informational message to Sentry if it's configured
|
|
||||||
"""
|
|
||||||
if settings.SETUP.SENTRY_DSN and settings.SETUP.SENTRY_CAPTURE_MESSAGES:
|
|
||||||
from sentry_sdk import capture_message
|
|
||||||
|
|
||||||
capture_message(message, level, scope, **scope_args)
|
|
||||||
elif settings.DEBUG:
|
|
||||||
if scope or scope_args:
|
|
||||||
message += f"; {scope=}, {scope_args=}"
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
|
|
||||||
def capture_exception(exception: BaseException, scope=None, **scope_args):
|
|
||||||
"""
|
|
||||||
Sends the exception to Sentry if it's configured
|
|
||||||
"""
|
|
||||||
if settings.SETUP.SENTRY_DSN:
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
capture_exception(exception, scope, **scope_args)
|
|
||||||
elif settings.DEBUG:
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import urllib.parse as urllib_parse
|
import urllib.parse as urllib_parse
|
||||||
|
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from pyld import jsonld
|
from pyld import jsonld
|
||||||
|
|
||||||
from core.exceptions import ActivityPubFormatError, capture_message
|
from core.exceptions import ActivityPubFormatError
|
||||||
|
|
||||||
schemas = {
|
schemas = {
|
||||||
"unknown": {
|
"unknown": {
|
||||||
|
@ -629,7 +630,7 @@ def builtin_document_loader(url: str, options={}):
|
||||||
# Get URL without scheme
|
# Get URL without scheme
|
||||||
pieces = urllib_parse.urlparse(url)
|
pieces = urllib_parse.urlparse(url)
|
||||||
if pieces.hostname is None:
|
if pieces.hostname is None:
|
||||||
capture_message(f"No host name for json-ld schema: {url!r}")
|
logging.info(f"No host name for json-ld schema: {url!r}")
|
||||||
return schemas["unknown"]
|
return schemas["unknown"]
|
||||||
key = pieces.hostname + pieces.path.rstrip("/")
|
key = pieces.hostname + pieces.path.rstrip("/")
|
||||||
try:
|
try:
|
||||||
|
@ -640,7 +641,7 @@ def builtin_document_loader(url: str, options={}):
|
||||||
return schemas[key]
|
return schemas[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# return an empty context instead of throwing an error
|
# return an empty context instead of throwing an error
|
||||||
capture_message(f"Ignoring unknown json-ld schema: {url!r}")
|
logging.info(f"Ignoring unknown json-ld schema: {url!r}")
|
||||||
return schemas["unknown"]
|
return schemas["unknown"]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
from ssl import SSLCertVerificationError, SSLError
|
from ssl import SSLCertVerificationError, SSLError
|
||||||
from typing import Literal, TypedDict, cast
|
from typing import Literal, TypedDict, cast
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
@ -258,7 +259,7 @@ class HttpSignature:
|
||||||
)
|
)
|
||||||
except SSLError as invalid_cert:
|
except SSLError as invalid_cert:
|
||||||
# Not our problem if the other end doesn't have proper SSL
|
# Not our problem if the other end doesn't have proper SSL
|
||||||
print(f"{uri} {invalid_cert}")
|
logging.info(f"{uri} {invalid_cert}")
|
||||||
raise SSLCertVerificationError(invalid_cert) from invalid_cert
|
raise SSLCertVerificationError(invalid_cert) from invalid_cert
|
||||||
except InvalidCodepoint as ex:
|
except InvalidCodepoint as ex:
|
||||||
# Convert to a more generic error we handle
|
# Convert to a more generic error we handle
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
@ -73,7 +74,9 @@ class Command(BaseCommand):
|
||||||
if not models:
|
if not models:
|
||||||
models = StatorModel.subclasses
|
models = StatorModel.subclasses
|
||||||
models = [model for model in models if model not in excluded]
|
models = [model for model in models if model not in excluded]
|
||||||
print("Running for models: " + " ".join(m._meta.label_lower for m in models))
|
logging.info(
|
||||||
|
"Running for models: " + " ".join(m._meta.label_lower for m in models)
|
||||||
|
)
|
||||||
# Run a runner
|
# Run a runner
|
||||||
runner = StatorRunner(
|
runner = StatorRunner(
|
||||||
models,
|
models,
|
||||||
|
@ -85,4 +88,4 @@ class Command(BaseCommand):
|
||||||
try:
|
try:
|
||||||
runner.run()
|
runner.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Ctrl-C received")
|
logging.critical("Ctrl-C received")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import datetime
|
import datetime
|
||||||
import traceback
|
import logging
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
|
|
||||||
from asgiref.sync import async_to_sync, iscoroutinefunction
|
from asgiref.sync import async_to_sync, iscoroutinefunction
|
||||||
|
@ -8,7 +8,6 @@ from django.db.models.signals import class_prepared
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import classproperty
|
from django.utils.functional import classproperty
|
||||||
|
|
||||||
from core import exceptions
|
|
||||||
from stator.exceptions import TryAgainLater
|
from stator.exceptions import TryAgainLater
|
||||||
from stator.graph import State, StateGraph
|
from stator.graph import State, StateGraph
|
||||||
|
|
||||||
|
@ -190,7 +189,7 @@ class StatorModel(models.Model):
|
||||||
# If it's a manual progression state don't even try
|
# If it's a manual progression state don't even try
|
||||||
# We shouldn't really be here in this case, but it could be a race condition
|
# We shouldn't really be here in this case, but it could be a race condition
|
||||||
if current_state.externally_progressed:
|
if current_state.externally_progressed:
|
||||||
print(
|
logging.warning(
|
||||||
f"Warning: trying to progress externally progressed state {self.state}!"
|
f"Warning: trying to progress externally progressed state {self.state}!"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
@ -204,8 +203,7 @@ class StatorModel(models.Model):
|
||||||
except TryAgainLater:
|
except TryAgainLater:
|
||||||
pass
|
pass
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
exceptions.capture_exception(e)
|
logging.exception(e)
|
||||||
traceback.print_exc()
|
|
||||||
else:
|
else:
|
||||||
if next_state:
|
if next_state:
|
||||||
# Ensure it's a State object
|
# Ensure it's a State object
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
import traceback
|
|
||||||
import uuid
|
import uuid
|
||||||
from concurrent.futures import Future, ThreadPoolExecutor
|
from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from django.conf import settings
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from core import exceptions, sentry
|
from core import sentry
|
||||||
from core.models import Config
|
from core.models import Config
|
||||||
from stator.models import StatorModel, Stats
|
from stator.models import StatorModel, Stats
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class StatorRunner:
|
||||||
self.scheduling_timer = LoopingTimer(self.schedule_interval)
|
self.scheduling_timer = LoopingTimer(self.schedule_interval)
|
||||||
self.deletion_timer = LoopingTimer(self.delete_interval)
|
self.deletion_timer = LoopingTimer(self.delete_interval)
|
||||||
# For the first time period, launch tasks
|
# For the first time period, launch tasks
|
||||||
print("Running main task loop")
|
logging.info("Running main task loop")
|
||||||
try:
|
try:
|
||||||
with sentry.configure_scope() as scope:
|
with sentry.configure_scope() as scope:
|
||||||
while True:
|
while True:
|
||||||
|
@ -137,18 +137,18 @@ class StatorRunner:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Wait for tasks to finish
|
# Wait for tasks to finish
|
||||||
print("Waiting for tasks to complete")
|
logging.info("Waiting for tasks to complete")
|
||||||
self.executor.shutdown()
|
self.executor.shutdown()
|
||||||
|
|
||||||
# We're done
|
# We're done
|
||||||
print("Complete")
|
logging.info("Complete")
|
||||||
|
|
||||||
def alarm_handler(self, signum, frame):
|
def alarm_handler(self, signum, frame):
|
||||||
"""
|
"""
|
||||||
Called when SIGALRM fires, which means we missed a schedule loop.
|
Called when SIGALRM fires, which means we missed a schedule loop.
|
||||||
Just exit as we're likely deadlocked.
|
Just exit as we're likely deadlocked.
|
||||||
"""
|
"""
|
||||||
print("Watchdog timeout exceeded")
|
logging.warning("Watchdog timeout exceeded")
|
||||||
os._exit(2)
|
os._exit(2)
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
|
@ -165,7 +165,9 @@ class StatorRunner:
|
||||||
for model in self.models:
|
for model in self.models:
|
||||||
num = self.handled.get(model._meta.label_lower, 0)
|
num = self.handled.get(model._meta.label_lower, 0)
|
||||||
if num or settings.DEBUG:
|
if num or settings.DEBUG:
|
||||||
print(f"{model._meta.label_lower}: Scheduling ({num} handled)")
|
logging.info(
|
||||||
|
f"{model._meta.label_lower}: Scheduling ({num} handled)"
|
||||||
|
)
|
||||||
self.submit_stats(model)
|
self.submit_stats(model)
|
||||||
model.transition_clean_locks()
|
model.transition_clean_locks()
|
||||||
|
|
||||||
|
@ -237,8 +239,7 @@ class StatorRunner:
|
||||||
try:
|
try:
|
||||||
task.result()
|
task.result()
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
exceptions.capture_exception(e)
|
logging.exception(e)
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
def run_single_cycle(self):
|
def run_single_cycle(self):
|
||||||
"""
|
"""
|
||||||
|
@ -268,11 +269,11 @@ def task_transition(instance: StatorModel, in_thread: bool = True):
|
||||||
result = instance.transition_attempt()
|
result = instance.transition_attempt()
|
||||||
duration = time.monotonic() - started
|
duration = time.monotonic() - started
|
||||||
if result:
|
if result:
|
||||||
print(
|
logging.info(
|
||||||
f"{instance._meta.label_lower}: {instance.pk}: {instance.state} -> {result} ({duration:.2f}s)"
|
f"{instance._meta.label_lower}: {instance.pk}: {instance.state} -> {result} ({duration:.2f}s)"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print(
|
logging.info(
|
||||||
f"{instance._meta.label_lower}: {instance.pk}: {instance.state} unchanged ({duration:.2f}s)"
|
f"{instance._meta.label_lower}: {instance.pk}: {instance.state} unchanged ({duration:.2f}s)"
|
||||||
)
|
)
|
||||||
if in_thread:
|
if in_thread:
|
||||||
|
@ -288,7 +289,7 @@ def task_deletion(model: type[StatorModel], in_thread: bool = True):
|
||||||
deleted = model.transition_delete_due()
|
deleted = model.transition_delete_due()
|
||||||
if not deleted:
|
if not deleted:
|
||||||
break
|
break
|
||||||
print(f"{model._meta.label_lower}: Deleted {deleted} stale items")
|
logging.info(f"{model._meta.label_lower}: Deleted {deleted} stale items")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if in_thread:
|
if in_thread:
|
||||||
close_old_connections()
|
close_old_connections()
|
||||||
|
|
|
@ -11,7 +11,6 @@ import httpx
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
from corsheaders.defaults import default_headers
|
from corsheaders.defaults import default_headers
|
||||||
from pydantic import AnyUrl, BaseSettings, EmailStr, Field, validator
|
from pydantic import AnyUrl, BaseSettings, EmailStr, Field, validator
|
||||||
from sentry_sdk.integrations.django import DjangoIntegration
|
|
||||||
|
|
||||||
from takahe import __version__
|
from takahe import __version__
|
||||||
|
|
||||||
|
@ -368,7 +367,9 @@ if SETUP.USE_PROXY_HEADERS:
|
||||||
|
|
||||||
|
|
||||||
if SETUP.SENTRY_DSN:
|
if SETUP.SENTRY_DSN:
|
||||||
|
from sentry_sdk.integrations.django import DjangoIntegration
|
||||||
from sentry_sdk.integrations.httpx import HttpxIntegration
|
from sentry_sdk.integrations.httpx import HttpxIntegration
|
||||||
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||||
|
|
||||||
sentry_experiments = {}
|
sentry_experiments = {}
|
||||||
|
|
||||||
|
@ -382,6 +383,7 @@ if SETUP.SENTRY_DSN:
|
||||||
integrations=[
|
integrations=[
|
||||||
DjangoIntegration(),
|
DjangoIntegration(),
|
||||||
HttpxIntegration(),
|
HttpxIntegration(),
|
||||||
|
LoggingIntegration(),
|
||||||
],
|
],
|
||||||
traces_sample_rate=SETUP.SENTRY_TRACES_SAMPLE_RATE,
|
traces_sample_rate=SETUP.SENTRY_TRACES_SAMPLE_RATE,
|
||||||
sample_rate=SETUP.SENTRY_SAMPLE_RATE,
|
sample_rate=SETUP.SENTRY_SAMPLE_RATE,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -9,7 +10,6 @@ import urlman
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from core.exceptions import capture_message
|
|
||||||
from core.models import Config
|
from core.models import Config
|
||||||
from stator.models import State, StateField, StateGraph, StatorModel
|
from stator.models import State, StateField, StateGraph, StatorModel
|
||||||
from users.schemas import NodeInfo
|
from users.schemas import NodeInfo
|
||||||
|
@ -209,9 +209,9 @@ class Domain(StatorModel):
|
||||||
and response.status_code < 500
|
and response.status_code < 500
|
||||||
and response.status_code not in [401, 403, 404, 406, 410]
|
and response.status_code not in [401, 403, 404, 406, 410]
|
||||||
):
|
):
|
||||||
capture_message(
|
logging.warning(
|
||||||
f"Client error fetching nodeinfo: {str(ex)}",
|
f"Client error fetching nodeinfo: {str(ex)}",
|
||||||
extras={
|
extra={
|
||||||
"code": response.status_code,
|
"code": response.status_code,
|
||||||
"content": response.content,
|
"content": response.content,
|
||||||
"domain": self.domain,
|
"domain": self.domain,
|
||||||
|
@ -223,9 +223,9 @@ class Domain(StatorModel):
|
||||||
try:
|
try:
|
||||||
info = NodeInfo(**response.json())
|
info = NodeInfo(**response.json())
|
||||||
except (json.JSONDecodeError, pydantic.ValidationError) as ex:
|
except (json.JSONDecodeError, pydantic.ValidationError) as ex:
|
||||||
capture_message(
|
logging.warning(
|
||||||
f"Client error decoding nodeinfo: {str(ex)}",
|
f"Client error decoding nodeinfo: {str(ex)}",
|
||||||
extras={
|
extra={
|
||||||
"domain": self.domain,
|
"domain": self.domain,
|
||||||
"nodeinfo20_url": nodeinfo20_url,
|
"nodeinfo20_url": nodeinfo20_url,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
|
|
||||||
from core.exceptions import capture_message
|
|
||||||
from core.ld import canonicalise, get_str_or_id
|
from core.ld import canonicalise, get_str_or_id
|
||||||
from core.snowflake import Snowflake
|
from core.snowflake import Snowflake
|
||||||
from stator.models import State, StateField, StateGraph, StatorModel
|
from stator.models import State, StateField, StateGraph, StatorModel
|
||||||
|
@ -350,8 +350,8 @@ class Follow(StatorModel):
|
||||||
try:
|
try:
|
||||||
follow = cls.by_ap(data, create=True)
|
follow = cls.by_ap(data, create=True)
|
||||||
except Identity.DoesNotExist:
|
except Identity.DoesNotExist:
|
||||||
capture_message(
|
logging.info(
|
||||||
"Identity not found for incoming Follow", extras={"data": data}
|
"Identity not found for incoming Follow", extra={"data": data}
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if follow.state == FollowStates.accepted:
|
if follow.state == FollowStates.accepted:
|
||||||
|
@ -367,9 +367,9 @@ class Follow(StatorModel):
|
||||||
try:
|
try:
|
||||||
follow = cls.by_ap(data["object"])
|
follow = cls.by_ap(data["object"])
|
||||||
except (cls.DoesNotExist, Identity.DoesNotExist):
|
except (cls.DoesNotExist, Identity.DoesNotExist):
|
||||||
capture_message(
|
logging.info(
|
||||||
"Follow or Identity not found for incoming Accept",
|
"Follow or Identity not found for incoming Accept",
|
||||||
extras={"data": data},
|
extra={"data": data},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -389,9 +389,9 @@ class Follow(StatorModel):
|
||||||
try:
|
try:
|
||||||
follow = cls.by_ap(data["object"])
|
follow = cls.by_ap(data["object"])
|
||||||
except (cls.DoesNotExist, Identity.DoesNotExist):
|
except (cls.DoesNotExist, Identity.DoesNotExist):
|
||||||
capture_message(
|
logging.info(
|
||||||
"Follow or Identity not found for incoming Reject",
|
"Follow or Identity not found for incoming Reject",
|
||||||
extras={"data": data},
|
extra={"data": data},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -419,8 +419,8 @@ class Follow(StatorModel):
|
||||||
try:
|
try:
|
||||||
follow = cls.by_ap(data["object"])
|
follow = cls.by_ap(data["object"])
|
||||||
except (cls.DoesNotExist, Identity.DoesNotExist):
|
except (cls.DoesNotExist, Identity.DoesNotExist):
|
||||||
capture_message(
|
logging.info(
|
||||||
"Follow or Identity not found for incoming Undo", extras={"data": data}
|
"Follow or Identity not found for incoming Undo", extra={"data": data}
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
from functools import cached_property, partial
|
from functools import cached_property, partial
|
||||||
from typing import Literal, Optional
|
from typing import Literal, Optional
|
||||||
|
@ -11,7 +12,7 @@ from django.utils import timezone
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from core.exceptions import ActorMismatchError, capture_message
|
from core.exceptions import ActorMismatchError
|
||||||
from core.html import ContentRenderer, FediverseHtmlParser
|
from core.html import ContentRenderer, FediverseHtmlParser
|
||||||
from core.ld import (
|
from core.ld import (
|
||||||
canonicalise,
|
canonicalise,
|
||||||
|
@ -871,9 +872,9 @@ class Identity(StatorModel):
|
||||||
# Their account got deleted, so let's do the same.
|
# Their account got deleted, so let's do the same.
|
||||||
Identity.objects.filter(pk=self.pk).delete()
|
Identity.objects.filter(pk=self.pk).delete()
|
||||||
if status_code < 500 and status_code not in [401, 403, 404, 406, 410]:
|
if status_code < 500 and status_code not in [401, 403, 404, 406, 410]:
|
||||||
capture_message(
|
logging.info(
|
||||||
f"Client error fetching actor at {self.actor_uri}: {status_code}",
|
f"Client error fetching actor at {self.actor_uri}: {status_code}",
|
||||||
extras={
|
extra={
|
||||||
"identity": self.pk,
|
"identity": self.pk,
|
||||||
"domain": self.domain_id,
|
"domain": self.domain_id,
|
||||||
"content": response.content,
|
"content": response.content,
|
||||||
|
@ -884,9 +885,9 @@ class Identity(StatorModel):
|
||||||
document = canonicalise(response.json(), include_security=True)
|
document = canonicalise(response.json(), include_security=True)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# servers with empty or invalid responses are inevitable
|
# servers with empty or invalid responses are inevitable
|
||||||
capture_message(
|
logging.info(
|
||||||
f"Invalid response fetching actor at {self.actor_uri}",
|
f"Invalid response fetching actor at {self.actor_uri}",
|
||||||
extras={
|
extra={
|
||||||
"identity": self.pk,
|
"identity": self.pk,
|
||||||
"domain": self.domain_id,
|
"domain": self.domain_id,
|
||||||
"content": response.content,
|
"content": response.content,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest, JsonResponse
|
from django.http import Http404, HttpResponse, HttpResponseBadRequest, JsonResponse
|
||||||
|
@ -9,7 +10,6 @@ from django.views.generic import View
|
||||||
|
|
||||||
from activities.models import Post
|
from activities.models import Post
|
||||||
from activities.services import TimelineService
|
from activities.services import TimelineService
|
||||||
from core import exceptions
|
|
||||||
from core.decorators import cache_page
|
from core.decorators import cache_page
|
||||||
from core.ld import canonicalise
|
from core.ld import canonicalise
|
||||||
from core.models import Config
|
from core.models import Config
|
||||||
|
@ -140,6 +140,10 @@ class Inbox(View):
|
||||||
# Find the Identity by the actor on the incoming item
|
# Find the Identity by the actor on the incoming item
|
||||||
# This ensures that the signature used for the headers matches the actor
|
# This ensures that the signature used for the headers matches the actor
|
||||||
# described in the payload.
|
# described in the payload.
|
||||||
|
if "actor" not in document:
|
||||||
|
logging.warning("Inbox error: unspecified actor")
|
||||||
|
return HttpResponseBadRequest("Unspecified actor")
|
||||||
|
|
||||||
identity = Identity.by_actor_uri(document["actor"], create=True, transient=True)
|
identity = Identity.by_actor_uri(document["actor"], create=True, transient=True)
|
||||||
if (
|
if (
|
||||||
document["type"] == "Delete"
|
document["type"] == "Delete"
|
||||||
|
@ -154,23 +158,19 @@ class Inbox(View):
|
||||||
try:
|
try:
|
||||||
identity.fetch_actor()
|
identity.fetch_actor()
|
||||||
except TryAgainLater:
|
except TryAgainLater:
|
||||||
exceptions.capture_message(
|
logging.warning(
|
||||||
f"Inbox error: timed out fetching actor {document['actor']}"
|
f"Inbox error: timed out fetching actor {document['actor']}"
|
||||||
)
|
)
|
||||||
return HttpResponse(status=504)
|
return HttpResponse(status=504)
|
||||||
|
|
||||||
if not identity.public_key:
|
if not identity.public_key:
|
||||||
exceptions.capture_message(
|
logging.warning(f"Inbox error: cannot fetch actor {document['actor']}")
|
||||||
f"Inbox error: cannot fetch actor {document['actor']}"
|
|
||||||
)
|
|
||||||
return HttpResponseBadRequest("Cannot retrieve actor")
|
return HttpResponseBadRequest("Cannot retrieve actor")
|
||||||
|
|
||||||
# See if it's from a blocked user or domain
|
# See if it's from a blocked user or domain
|
||||||
if identity.blocked or identity.domain.recursively_blocked():
|
if identity.blocked or identity.domain.recursively_blocked():
|
||||||
# I love to lie! Throw it away!
|
# I love to lie! Throw it away!
|
||||||
exceptions.capture_message(
|
logging.warning(f"Inbox: Discarded message from {identity.actor_uri}")
|
||||||
f"Inbox: Discarded message from {identity.actor_uri}"
|
|
||||||
)
|
|
||||||
return HttpResponse(status=202)
|
return HttpResponse(status=202)
|
||||||
|
|
||||||
# If there's a "signature" payload, verify against that
|
# If there's a "signature" payload, verify against that
|
||||||
|
@ -178,12 +178,10 @@ class Inbox(View):
|
||||||
try:
|
try:
|
||||||
LDSignature.verify_signature(document, identity.public_key)
|
LDSignature.verify_signature(document, identity.public_key)
|
||||||
except VerificationFormatError as e:
|
except VerificationFormatError as e:
|
||||||
exceptions.capture_message(
|
logging.warning(f"Inbox error: Bad LD signature format: {e.args[0]}")
|
||||||
f"Inbox error: Bad LD signature format: {e.args[0]}"
|
|
||||||
)
|
|
||||||
return HttpResponseBadRequest(e.args[0])
|
return HttpResponseBadRequest(e.args[0])
|
||||||
except VerificationError:
|
except VerificationError:
|
||||||
exceptions.capture_message("Inbox error: Bad LD signature")
|
logging.warning("Inbox error: Bad LD signature")
|
||||||
return HttpResponseUnauthorized("Bad signature")
|
return HttpResponseUnauthorized("Bad signature")
|
||||||
|
|
||||||
# Otherwise, verify against the header (assuming it's the same actor)
|
# Otherwise, verify against the header (assuming it's the same actor)
|
||||||
|
@ -194,12 +192,10 @@ class Inbox(View):
|
||||||
identity.public_key,
|
identity.public_key,
|
||||||
)
|
)
|
||||||
except VerificationFormatError as e:
|
except VerificationFormatError as e:
|
||||||
exceptions.capture_message(
|
logging.warning(f"Inbox error: Bad HTTP signature format: {e.args[0]}")
|
||||||
f"Inbox error: Bad HTTP signature format: {e.args[0]}"
|
|
||||||
)
|
|
||||||
return HttpResponseBadRequest(e.args[0])
|
return HttpResponseBadRequest(e.args[0])
|
||||||
except VerificationError:
|
except VerificationError:
|
||||||
exceptions.capture_message("Inbox error: Bad HTTP signature")
|
logging.warning("Inbox error: Bad HTTP signature")
|
||||||
return HttpResponseUnauthorized("Bad signature")
|
return HttpResponseUnauthorized("Bad signature")
|
||||||
|
|
||||||
# Don't allow injection of internal messages
|
# Don't allow injection of internal messages
|
||||||
|
|
Loading…
Reference in a new issue