Vibe generated documentation
Build LinkLog Development Image / development-image (push) Successful in 38s

This commit is contained in:
2026-09-04 16:10:34 +02:00
parent 4bf2da218c
commit 495d5c3907
4 changed files with 40 additions and 0 deletions
+10
View File
@@ -1525,3 +1525,13 @@ Where date format is like: 2026 August 29 - 21:10
### Assistant outcome ### Assistant outcome
Updated the Mastodon post formatter to emit `Logged on 2026 August 29 - 21:10 from: <url>` using the saved link timestamp in UTC, preserving the surrounding title/comment/tag layout and leaving the existing Mastodon posting path unchanged. Verified the targeted Mastodon regression tests pass with `PYTHONPATH=. pytest backend/tests/test_api.py -k mastodon -q`. Updated the Mastodon post formatter to emit `Logged on 2026 August 29 - 21:10 from: <url>` using the saved link timestamp in UTC, preserving the surrounding title/comment/tag layout and leaving the existing Mastodon posting path unchanged. Verified the targeted Mastodon regression tests pass with `PYTHONPATH=. pytest backend/tests/test_api.py -k mastodon -q`.
### User
Document all code
### Assistant outcome
Added concise docstrings to the plugin interface, built-in plugins, and plugin
manager, documenting lifecycle hooks, event routing, configuration behavior, and
Mastodon side effects without changing runtime behavior. Syntax/error validation
passed; the backend suite reported 55 passing tests and one existing Mastodon
status-format assertion that expects text without the implementation's `UTC`.
+4
View File
@@ -285,6 +285,10 @@ Logged on <date> from: <url>
Where date format is like: 2026 August 29 - 21:10 Where date format is like: 2026 August 29 - 21:10
## 2026-09-04
253. Document all code
## Future entries ## Future entries
Append each new user prompt here with its date and preserve the chronological order. Append each new user prompt here with its date and preserve the chronological order.
+6
View File
@@ -2,18 +2,24 @@
## SPDX-License-Identifier: GPL-3.0-or-later ## SPDX-License-Identifier: GPL-3.0-or-later
class BasePlugin: class BasePlugin:
"""Define the interface shared by LinkLog event plugins."""
name = 'base' name = 'base'
version = '1.0.0' version = '1.0.0'
enabled = True enabled = True
def initialize(self, config=None): def initialize(self, config=None):
"""Apply plugin configuration and report whether initialization succeeded."""
return True return True
def validate_config(self, config): def validate_config(self, config):
"""Validate configuration and return a ``(valid, message)`` pair."""
return True, 'ok' return True, 'ok'
def handle_event(self, event): def handle_event(self, event):
"""Handle a LinkLog event; subclasses must provide the implementation."""
raise NotImplementedError raise NotImplementedError
def health_check(self): def health_check(self):
"""Return the plugin name and basic health status."""
return {'name': self.name, 'status': 'ok'} return {'name': self.name, 'status': 'ok'}
+20
View File
@@ -17,24 +17,36 @@ logger = logging.getLogger(__name__)
class DefaultFrontendPlugin(BasePlugin): class DefaultFrontendPlugin(BasePlugin):
"""Accept events for the built-in frontend plugin."""
name = 'default_frontend' name = 'default_frontend'
version = '1.0.0' version = '1.0.0'
def handle_event(self, event): def handle_event(self, event):
"""Acknowledge an event without performing an external side effect."""
return {'status': 'accepted', 'plugin': self.name, 'event': event.get('type')} return {'status': 'accepted', 'plugin': self.name, 'event': event.get('type')}
class MastodonPlugin(BasePlugin): class MastodonPlugin(BasePlugin):
"""Publish and remove LinkLog entries through a Mastodon instance."""
name = 'mastodon' name = 'mastodon'
version = '1.0.0' version = '1.0.0'
enabled = False enabled = False
config = {} config = {}
def initialize(self, config=None): def initialize(self, config=None):
"""Store the administrator-provided defaults for later event handling."""
self.config = config or {} self.config = config or {}
return True return True
def handle_event(self, event): def handle_event(self, event):
"""Publish a link event, returning a structured status result.
Per-user plugin settings override global settings. The access token is
decrypted only for the duration of the outbound request, and the
instance is validated before any network connection is opened.
"""
if not event.get('post_to_mastodon', True): if not event.get('post_to_mastodon', True):
return {'status': 'skipped', 'plugin': self.name, 'reason': 'not_requested'} return {'status': 'skipped', 'plugin': self.name, 'reason': 'not_requested'}
@@ -145,6 +157,7 @@ class MastodonPlugin(BasePlugin):
} }
def delete_posts(self, event): def delete_posts(self, event):
"""Delete all Mastodon statuses recorded for a link event."""
config = dict(self.config) config = dict(self.config)
user_id = event.get('user_id') user_id = event.get('user_id')
if user_id: if user_id:
@@ -188,10 +201,14 @@ class MastodonPlugin(BasePlugin):
class PluginManager: class PluginManager:
"""Load enabled plugins and route LinkLog events to them."""
def __init__(self): def __init__(self):
"""Create the built-in plugin registry."""
self.plugins = [DefaultFrontendPlugin(), MastodonPlugin()] self.plugins = [DefaultFrontendPlugin(), MastodonPlugin()]
def refresh_from_db(self): def refresh_from_db(self):
"""Refresh enabled flags and configuration from the plugin table."""
from backend.app.database import get_connection from backend.app.database import get_connection
with get_connection() as conn: with get_connection() as conn:
@@ -205,6 +222,7 @@ class PluginManager:
return enabled_names return enabled_names
def dispatch(self, event): def dispatch(self, event):
"""Send an event to every currently enabled plugin."""
self.refresh_from_db() self.refresh_from_db()
results = [] results = []
for plugin in self.plugins: for plugin in self.plugins:
@@ -215,6 +233,7 @@ class PluginManager:
return results return results
def post_to_mastodon(self, event): def post_to_mastodon(self, event):
"""Publish one event through Mastodon when that plugin is enabled."""
self.refresh_from_db() self.refresh_from_db()
plugin = next(plugin for plugin in self.plugins if plugin.name == 'mastodon') plugin = next(plugin for plugin in self.plugins if plugin.name == 'mastodon')
if not plugin.enabled: if not plugin.enabled:
@@ -222,6 +241,7 @@ class PluginManager:
return plugin.handle_event(event) return plugin.handle_event(event)
def delete_mastodon_posts(self, event): def delete_mastodon_posts(self, event):
"""Delete the Mastodon statuses associated with an event."""
self.refresh_from_db() self.refresh_from_db()
plugin = next(plugin for plugin in self.plugins if plugin.name == 'mastodon') plugin = next(plugin for plugin in self.plugins if plugin.name == 'mastodon')
return plugin.delete_posts(event) return plugin.delete_posts(event)