feat: discoverability (NodeInfo, hashtags) and moderation (domain/actor blocking)

- NodeInfo at /.well-known/nodeinfo + /nodeinfo/2.0
- Hashtags #MoviesDiary + #MovieTitle on review posts; /tags/{tag} redirect
- Domain blocking: blocked_domains table, admin API + HTML, inbox enforcement
- Per-actor blocking: blocked_actors table, user API + HTML, BlockActivity send/receive
- Delivery filter excludes blocked actors and blocked-domain inboxes
This commit is contained in:
2026-05-12 00:49:30 +02:00
parent 80f620c840
commit f0620f5aa1
40 changed files with 1410 additions and 543 deletions

View File

@@ -0,0 +1,5 @@
CREATE TABLE blocked_domains (
domain TEXT PRIMARY KEY,
reason TEXT,
blocked_at TEXT NOT NULL
);

View File

@@ -0,0 +1,6 @@
CREATE TABLE blocked_actors (
local_user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
remote_actor_url TEXT NOT NULL,
blocked_at TEXT NOT NULL,
PRIMARY KEY (local_user_id, remote_actor_url)
);

View File

@@ -766,6 +766,16 @@ impl DiaryRepository for PostgresRepository {
offset: page.offset,
})
}
async fn count_local_posts(&self) -> Result<u64, DomainError> {
let count: i64 = sqlx::query_scalar(
"SELECT COUNT(*) FROM reviews WHERE remote_actor_url IS NULL"
)
.fetch_one(&self.pool)
.await
.map_err(Self::map_err)?;
Ok(count as u64)
}
}
#[async_trait]