Compare commits

...

2 commits

Author SHA1 Message Date
8363125108
Lexicons, woach to spoor, anilist to external
THESE LEXICONS ARE NOT FINAL, she screamed into the wind.

converts all references to woach.me to spoor.my to reflect the official
domain name.

Rename "feed" 3ld to "log" since the content here is separate from what
an activity feed would look like. Log has more in common with records.

I maintained the name activity over "spoor" here, because I want the
items to be easily digested by other services. I think "spoor" is
actually a more apt term than "activity" since "activity" is more
generic, than the specific mediaspoor it is, but it will do.

Changes the "anilist" entry to a more generic "external" entry, which
will be able to handle tvdb, or similar.
2025-05-01 15:48:13 -07:00
db33099405
Atproto, Router, add atproto api mod with Nsid
Sets up a generic atproto module to store atproto implementation. Mainly
doing this so that I can switch between rsky/atrium as well as add my
own layers on top.

This also switches the old Xrpc/Router use of Nsid to the atproto api
implementation of it. Next up is the DB where I'll need a bunch of
these.
2025-04-25 13:52:02 -07:00
12 changed files with 911 additions and 62 deletions

View file

@ -1,14 +0,0 @@
{
"lexicon": 1,
"id": "me.woach.content.anilist",
"defs": {
"main": {
"type": "object",
"required": ["id"],
"key": "nsid",
"properties": {
"id": { "type": "integer" }
}
}
}
}

View file

@ -0,0 +1,30 @@
{
"lexicon": 1,
"id": "my.spoor.content.external",
"defs": {
"main": {
"type": "object",
"required": [ "source", "queryable" ],
"key": "nsid",
"properties": {
"source": {
"type": "string",
"description": "An nsid for a specific data source. The domain authority governs how to process the queryable",
"format": "nsid",
"knownValues": [
"my.spoor.content.external#tvdb"
]
},
"queryable": {
"type": "union",
"description": "All the data needed to query the content from the source"
},
"overrides": {
"type": "object",
"description": "User defined overrides for the returned content",
"properties": {}
}
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"lexicon": 1,
"id": "me.woach.content.media",
"id": "my.spoor.content.media",
"defs": {
"main": {
"type": "object",
@ -12,14 +12,14 @@
"minLength": 1,
"items": {
"type": "ref",
"ref": "me.woach.content.title"
"ref": "my.spoor.content.title"
}
},
"durationData": {
"type": "union",
"refs": [
"me.woach.content.media#television",
"me.woach.content.media#book"
"my.spoor.content.media#television",
"my.spoor.content.media#book"
]
},
"posterImage": {

View file

@ -1,16 +1,17 @@
{
"lexicon": 1,
"id": "me.woach.content.title",
"id": "my.spoor.content.title",
"defs": {
"main": {
"type": "object",
"properties": {
"language": {
"type": "string",
"format": "nsid",
"knownValues": [
"me.woach.content.title#romanization",
"me.woach.content.title#english",
"me.woach.content.title#native"
"my.spoor.content.title#romanization",
"my.spoor.content.title#english",
"my.spoor.content.title#native"
]
},
"value": { "type": "string", "minLength": 1 }

View file

@ -1,10 +1,10 @@
{
"lexicon": 1,
"id": "me.woach.feed.activity",
"id": "my.spoor.log.activity",
"defs": {
"main": {
"type": "record",
"description": "A single activity log for a specific show",
"description": "A single activity (spoor) for a specific session",
"key": "tid",
"record": {
"type": "object",
@ -13,13 +13,15 @@
"properties": {
"session": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
"progress": {
"type": "integer",
"description": "The episode/chapter number for the content consumed."
"type": "union",
"refs": [
"my.spoor.log.activity#indexProgress"
]
},
"performedAt": {
"type": "string",
"format": "datetime",
"description": "User-declared timestamp for when they performed the activity."
"description": "User-declared timestamp for when they performed the activity. Null implies unknown time."
},
"createdAt": {
"type": "string",
@ -28,6 +30,10 @@
}
}
}
},
"indexProgress": {
"type": "integer",
"description": "The index of the content consumed. Content must be indexable"
}
}
}

View file

@ -1,6 +1,6 @@
{
"lexicon": 1,
"id": "me.woach.feed.session",
"id": "my.spoor.log.session",
"defs": {
"main": {
"type": "record",
@ -16,9 +16,9 @@
"maxGraphemes": 64,
"maxLength": 640
},
"participants": {
"otherParticipants": {
"type": "array",
"items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
"items": { "type": "string", "format": "did" }
},
"createdAt": {
"type": "string",

830
rust/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,9 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
atrium-api = { version = "0.25.2", default-features = false }
axum = { version = "0.8.3", features = ["json"] }
axum-macros = "0.5.0"
http = "1.3.1"
serde = "1.0.219"
serde_json = "1.0.140"
sqlx = { version = "0.8.5", features = ["runtime-tokio"] }
sqlx = { version = "0.8.5", features = ["postgres", "runtime-tokio"] }
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }

1
rust/src/atproto.rs Normal file
View file

@ -0,0 +1 @@
pub use atrium_api::types::string::Nsid;

View file

@ -1,4 +1,6 @@
use crate::router::{
use crate::{
atproto::Nsid,
router::{
Router,
Endpoint,
xrpc::{
@ -7,18 +9,21 @@ use crate::router::{
Response,
error,
},
},
};
use axum::http::StatusCode;
use http::status::StatusCode;
mod atproto;
mod router;
mod db;
#[tokio::main]
async fn main() {
let mut router = Router::new();
router = router.add_endpoint(Endpoint::new_xrpc_query(String::from("me.woach.get"), test));
router = router.add_endpoint(Endpoint::new_xrpc_procedure(String::from("me.woach.post"), test2));
let get_nsid = Nsid::new(String::from("me.woach.get")).expect("me.woach.get is a valid nsid");
let post_nsid = Nsid::new(String::from("me.woach.post")).expect("me.woach.post is a valid nsid");
router = router.add_endpoint(Endpoint::new_xrpc_query(get_nsid, test));
router = router.add_endpoint(Endpoint::new_xrpc_procedure(post_nsid, test2));
router.serve().await;
}

View file

@ -1,8 +1,11 @@
use crate::router::xrpc::{
use crate::{
atproto::Nsid,
router::xrpc::{
XrpcEndpoint,
XrpcHandler,
QueryInput,
ProcedureInput,
}
};
use axum::Router as AxumRouter;
use core::net::SocketAddr;
@ -19,13 +22,13 @@ pub enum Endpoint {
Xrpc(XrpcEndpoint),
}
impl Endpoint {
pub fn new_xrpc_query<Q>(nsid: String, query: Q) -> Self
pub fn new_xrpc_query<Q>(nsid: Nsid, query: Q) -> Self
where
Q: XrpcHandler<QueryInput> + Clone
{
Endpoint::Xrpc(XrpcEndpoint::new_query(nsid,query))
}
pub fn new_xrpc_procedure<P>(nsid: String, procedure: P) -> Self
pub fn new_xrpc_procedure<P>(nsid: Nsid, procedure: P) -> Self
where
P: XrpcHandler<ProcedureInput> + Clone
{

View file

@ -1,3 +1,4 @@
use crate::atproto::Nsid;
use std::{
collections::HashMap,
pin::Pin,
@ -26,13 +27,13 @@ use axum::{
};
use serde_json::{Value, json};
enum Nsid {
Nsid(String),
enum Path {
Nsid(Nsid),
NotImplemented,
}
pub struct XrpcEndpoint {
nsid: Nsid,
path: Path,
resolver: MethodRouter,
}
@ -123,12 +124,12 @@ where
}
impl XrpcEndpoint {
pub fn new_query<Q>(nsid: String, query: Q) -> Self
pub fn new_query<Q>(nsid: Nsid, query: Q) -> Self
where
Q: XrpcHandler<QueryInput> + Clone
{
XrpcEndpoint {
nsid: Nsid::Nsid(nsid),
path: Path::Nsid(nsid),
resolver: get(async move | mut parts: Parts | -> Response {
match QueryInput::from_request_parts(&mut parts, &()).await {
Ok(qi) => query.call(qi).await,
@ -138,12 +139,12 @@ impl XrpcEndpoint {
}
}
pub fn new_procedure<P>(nsid: String, procedure: P) -> Self
pub fn new_procedure<P>(nsid: Nsid, procedure: P) -> Self
where
P: XrpcHandler<ProcedureInput> + Clone
{
XrpcEndpoint {
nsid: Nsid::Nsid(nsid),
path: Path::Nsid(nsid),
resolver: post(async move | req: Request | -> Response {
match ProcedureInput::from_request(req, &()).await {
Ok(pi) => procedure.call(pi).await,
@ -154,9 +155,9 @@ impl XrpcEndpoint {
}
pub fn add_to_router(self, router: axumRouter) -> axumRouter {
let path = match self.nsid {
Nsid::Nsid(s) => &("/xrpc/".to_owned() + &s),
Nsid::NotImplemented => "/xrpc/{*nsid}",
let path = match self.path {
Path::Nsid(nsid) => &("/xrpc/".to_owned() + nsid.as_str()),
Path::NotImplemented => "/xrpc/{*nsid}",
};
router.route(path, self.resolver)
@ -172,7 +173,7 @@ impl XrpcEndpoint {
);
XrpcEndpoint {
nsid: Nsid::NotImplemented,
path: Path::NotImplemented,
resolver: get(resolver.clone()).post(resolver),
}
}