User fetching and inbox message cleaning
This commit is contained in:
parent
2f443414a7
commit
3b079526a2
@ -213,6 +213,7 @@ class Config(models.Model):
|
||||
identity_min_length: int = 2
|
||||
identity_max_per_user: int = 5
|
||||
identity_max_age: int = 24 * 60 * 60
|
||||
inbox_message_purge_after: int = 24 * 60 * 60
|
||||
|
||||
restricted_usernames: str = "admin\nadmins\nadministrator\nadministrators\nsystem\nroot\nannounce\nannouncement\nannouncements"
|
||||
|
||||
|
@ -87,10 +87,14 @@ class State:
|
||||
try_interval: Optional[float] = None,
|
||||
handler_name: Optional[str] = None,
|
||||
externally_progressed: bool = False,
|
||||
attempt_immediately: bool = True,
|
||||
force_initial: bool = False,
|
||||
):
|
||||
self.try_interval = try_interval
|
||||
self.handler_name = handler_name
|
||||
self.externally_progressed = externally_progressed
|
||||
self.attempt_immediately = attempt_immediately
|
||||
self.force_initial = force_initial
|
||||
self.parents: Set["State"] = set()
|
||||
self.children: Set["State"] = set()
|
||||
|
||||
@ -121,7 +125,7 @@ class State:
|
||||
|
||||
@property
|
||||
def initial(self):
|
||||
return not self.parents
|
||||
return self.force_initial or (not self.parents)
|
||||
|
||||
@property
|
||||
def terminal(self):
|
||||
|
@ -74,6 +74,10 @@ class StatorModel(models.Model):
|
||||
def state_graph(cls) -> Type[StateGraph]:
|
||||
return cls._meta.get_field("state").graph
|
||||
|
||||
@property
|
||||
def state_age(self) -> int:
|
||||
return (timezone.now() - self.state_changed).total_seconds()
|
||||
|
||||
@classmethod
|
||||
async def atransition_schedule_due(cls, now=None) -> models.QuerySet:
|
||||
"""
|
||||
@ -184,13 +188,23 @@ class StatorModel(models.Model):
|
||||
state = state.name
|
||||
if state not in self.state_graph.states:
|
||||
raise ValueError(f"Invalid state {state}")
|
||||
self.__class__.objects.filter(pk=self.pk).update(
|
||||
state=state,
|
||||
state_changed=timezone.now(),
|
||||
state_attempted=None,
|
||||
state_locked_until=None,
|
||||
state_ready=True,
|
||||
)
|
||||
# See if it's ready immediately (if not, delay until first try_interval)
|
||||
if self.state_graph.states[state].attempt_immediately:
|
||||
self.__class__.objects.filter(pk=self.pk).update(
|
||||
state=state,
|
||||
state_changed=timezone.now(),
|
||||
state_attempted=None,
|
||||
state_locked_until=None,
|
||||
state_ready=True,
|
||||
)
|
||||
else:
|
||||
self.__class__.objects.filter(pk=self.pk).update(
|
||||
state=state,
|
||||
state_changed=timezone.now(),
|
||||
state_attempted=timezone.now(),
|
||||
state_locked_until=None,
|
||||
state_ready=False,
|
||||
)
|
||||
|
||||
atransition_perform = sync_to_async(transition_perform)
|
||||
|
||||
|
@ -65,7 +65,7 @@ class PasswordResetAdmin(admin.ModelAdmin):
|
||||
|
||||
@admin.register(InboxMessage)
|
||||
class InboxMessageAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "state", "state_attempted", "message_type", "message_actor"]
|
||||
list_display = ["id", "state", "state_changed", "message_type", "message_actor"]
|
||||
search_fields = ["message"]
|
||||
actions = ["reset_state"]
|
||||
readonly_fields = ["state_changed"]
|
||||
|
38
users/migrations/0003_identity_followers_etc.py
Normal file
38
users/migrations/0003_identity_followers_etc.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Generated by Django 4.1.3 on 2022-11-27 22:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("users", "0002_identity_discoverable"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="identity",
|
||||
name="followers_uri",
|
||||
field=models.CharField(blank=True, max_length=500, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="identity",
|
||||
name="following_uri",
|
||||
field=models.CharField(blank=True, max_length=500, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="identity",
|
||||
name="metadata",
|
||||
field=models.JSONField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="identity",
|
||||
name="pinned",
|
||||
field=models.JSONField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="identity",
|
||||
name="shared_inbox_uri",
|
||||
field=models.CharField(blank=True, max_length=500, null=True),
|
||||
),
|
||||
]
|
@ -23,19 +23,31 @@ from users.models.system_actor import SystemActor
|
||||
|
||||
|
||||
class IdentityStates(StateGraph):
|
||||
outdated = State(try_interval=3600)
|
||||
updated = State()
|
||||
"""
|
||||
There are only two states in a cycle.
|
||||
Identities sit in "updated" for up to system.identity_max_age, and then
|
||||
go back to "outdated" for refetching.
|
||||
"""
|
||||
|
||||
outdated = State(try_interval=3600, force_initial=True)
|
||||
updated = State(try_interval=86400, attempt_immediately=False)
|
||||
|
||||
outdated.transitions_to(updated)
|
||||
updated.transitions_to(outdated)
|
||||
|
||||
@classmethod
|
||||
async def handle_outdated(cls, identity: "Identity"):
|
||||
# Local identities never need fetching
|
||||
if identity.local:
|
||||
return "updated"
|
||||
return cls.updated
|
||||
# Run the actor fetch and progress to updated if it succeeds
|
||||
if await identity.fetch_actor():
|
||||
return "updated"
|
||||
return cls.updated
|
||||
|
||||
@classmethod
|
||||
async def handle_updated(cls, instance: "Identity"):
|
||||
if instance.state_age > Config.system.identity_max_age:
|
||||
return cls.outdated
|
||||
|
||||
|
||||
class Identity(StatorModel):
|
||||
|
@ -1,14 +1,17 @@
|
||||
from asgiref.sync import sync_to_async
|
||||
from django.db import models
|
||||
|
||||
from core.models import Config
|
||||
from stator.models import State, StateField, StateGraph, StatorModel
|
||||
|
||||
|
||||
class InboxMessageStates(StateGraph):
|
||||
received = State(try_interval=300)
|
||||
processed = State()
|
||||
processed = State(try_interval=86400, attempt_immediately=False)
|
||||
purged = State() # This is actually deletion, it will never get here
|
||||
|
||||
received.transitions_to(processed)
|
||||
processed.transitions_to(purged)
|
||||
|
||||
@classmethod
|
||||
async def handle_received(cls, instance: "InboxMessage"):
|
||||
@ -80,6 +83,11 @@ class InboxMessageStates(StateGraph):
|
||||
raise ValueError(f"Cannot handle activity of type {unknown}")
|
||||
return cls.processed
|
||||
|
||||
@classmethod
|
||||
async def handle_processed(cls, instance: "InboxMessage"):
|
||||
if instance.state_age > Config.system.inbox_message_purge_after:
|
||||
await InboxMessage.objects.filter(pk=instance.pk).adelete()
|
||||
|
||||
|
||||
class InboxMessage(StatorModel):
|
||||
"""
|
||||
|
Reference in New Issue
Block a user