diff --git a/.env.example b/.env.example index 79f9f93..ba25f2a 100644 --- a/.env.example +++ b/.env.example @@ -53,7 +53,7 @@ EVENT_BUS_BACKEND=db # EVENT_BUS_BACKEND=nats # NATS_URL=nats://localhost:4222 # NATS_MODE=jetstream # "jetstream" (default, at-least-once) or "core" (fire-and-forget) -# NATS_SUBJECT_PREFIX=movies-diary.events +# NATS_SUBJECT_PREFIX=screened.events # NATS_STREAM_NAME=MOVIES_DIARY_EVENTS # NATS_CONSUMER_NAME=worker diff --git a/README.md b/README.md index a8c06c6..ff5f9a9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Movies Diary +# Screened A self-hosted, server-side rendered movie logging system with a full REST API. Built in Rust — no JavaScript in the HTML interface, just HTML forms and an RSS feed. Designed to run as a lightweight widget embedded on a personal site or as a backend for third-party clients. @@ -10,7 +10,7 @@ A self-hosted, server-side rendered movie logging system with a full REST API. B - Movie enrichment via TMDb — full cast, crew, genres, keywords, runtime, budget/revenue, ratings; fetched automatically on movie discovery and refreshed every 30 days; exposed via `GET /api/v1/movies/{id}/profile` - RSS/Atom feed for public subscription (global and per-user) - JWT authentication via cookie (HTML) or Bearer token (REST API) -- ActivityPub federation — follow/unfollow remote users, accept/reject/remove followers, federated reviews broadcast as `Note` objects with `#MoviesDiary` + `#MovieTitle` hashtags, paginated outbox, boost/Announce tracking, NodeInfo discovery endpoint, shared inbox delivery, actor profile sync (bio, avatar, discoverable) +- ActivityPub federation — follow/unfollow remote users, accept/reject/remove followers, federated reviews broadcast as `Note` objects with `#Screened` + `#MovieTitle` hashtags, paginated outbox, boost/Announce tracking, NodeInfo discovery endpoint, shared inbox delivery, actor profile sync (bio, avatar, discoverable) - Federation moderation — instance-level domain blocking (admin-managed), per-user actor blocking with `Block` activity, delivery filter excludes blocked actors and blocked-domain inboxes - CSV and JSON diary export - File importer: upload CSV, TSV, JSON, or XLSX from any source (Letterboxd, IMDb, etc.), map columns to domain fields via a step-by-step wizard or REST API, save mapping profiles for repeat imports diff --git a/crates/adapters/activitypub/src/lib.rs b/crates/adapters/activitypub/src/lib.rs index c6484c6..be0ca77 100644 --- a/crates/adapters/activitypub/src/lib.rs +++ b/crates/adapters/activitypub/src/lib.rs @@ -6,6 +6,8 @@ pub mod review_handler; pub(crate) mod urls; pub mod user_adapter; +use domain::PRODUCT_NAME; + // Re-export the generic base types that callers need pub use activitypub_base::{ ActivityPubService, ApFederationConfig, ApObjectHandler, ApUser, ApUserRepository, @@ -46,7 +48,7 @@ pub async fn wire( }), base_url.clone(), allow_registration, - "movies-diary".to_string(), + PRODUCT_NAME.to_lowercase(), cfg!(debug_assertions), ) .await?, diff --git a/crates/adapters/activitypub/src/objects.rs b/crates/adapters/activitypub/src/objects.rs index 3f04a53..bac5a01 100644 --- a/crates/adapters/activitypub/src/objects.rs +++ b/crates/adapters/activitypub/src/objects.rs @@ -69,9 +69,9 @@ pub fn review_to_ap_object( let tag = vec![ ApHashtag { kind: "Hashtag".to_string(), - href: Url::parse(&format!("{}/tags/moviesdiary", base_url)) + href: Url::parse(&format!("{}/tags/screened", base_url)) .expect("valid base_url"), - name: "#MoviesDiary".to_string(), + name: "#Screened".to_string(), }, ApHashtag { kind: "Hashtag".to_string(), @@ -137,7 +137,7 @@ mod tests { ); assert_eq!(obj.tag.len(), 2); let names: Vec<&str> = obj.tag.iter().map(|t| t.name.as_str()).collect(); - assert!(names.contains(&"#MoviesDiary")); + assert!(names.contains(&"#Screened")); assert!(names.contains(&"#Dune")); } } diff --git a/crates/adapters/nats/src/config.rs b/crates/adapters/nats/src/config.rs index 402e63d..1056a93 100644 --- a/crates/adapters/nats/src/config.rs +++ b/crates/adapters/nats/src/config.rs @@ -28,9 +28,9 @@ impl NatsConfig { }; let subject_prefix = std::env::var("NATS_SUBJECT_PREFIX") - .unwrap_or_else(|_| "movies-diary.events".to_string()); + .unwrap_or_else(|_| "screened.events".to_string()); let stream_name = std::env::var("NATS_STREAM_NAME") - .unwrap_or_else(|_| "MOVIES_DIARY_EVENTS".to_string()); + .unwrap_or_else(|_| "SCREENED_EVENTS".to_string()); let consumer_name = std::env::var("NATS_CONSUMER_NAME") .unwrap_or_else(|_| "worker".to_string()); @@ -61,8 +61,8 @@ mod tests { let cfg = NatsConfig::from_env().unwrap(); assert_eq!(cfg.url, "nats://localhost:4222"); assert_eq!(cfg.mode, NatsMode::JetStream); - assert_eq!(cfg.subject_prefix, "movies-diary.events"); - assert_eq!(cfg.stream_name, "MOVIES_DIARY_EVENTS"); + assert_eq!(cfg.subject_prefix, "screened.events"); + assert_eq!(cfg.stream_name, "SCREENED_EVENTS"); assert_eq!(cfg.consumer_name, "worker"); unsafe { std::env::remove_var("NATS_URL"); } diff --git a/crates/adapters/template-askama/templates/base.html b/crates/adapters/template-askama/templates/base.html index 8ea0eac..3b3fd74 100644 --- a/crates/adapters/template-askama/templates/base.html +++ b/crates/adapters/template-askama/templates/base.html @@ -9,7 +9,7 @@ content="A personal movie diary — track what you watch, rate and review films." /> - + @@ -26,7 +26,7 @@
- Movies Diary + Screened