Commit

Author:

Hash:

Timestamp:

+54 -46 +/-3 browse

Kevin Schoon [me@kevinschoon.com]

d8100641ad9779f5f51b19a20d6afbc2d13df783

Mon, 01 Dec 2025 11:27:01 +0000 (4 days ago)

update finger.rs again
update finger.rs again

The forge-feed specification has been updated to qualify URLs with a ://
making the original Uri package work. I opened up an issue in the webfinger-rs
repository https://github.com/joshka/webfinger-rs/issues/90 but ultimately
discovered this approach could work.
1diff --git a/Cargo.lock b/Cargo.lock
2index 9627085..6891707 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -3718,7 +3718,8 @@ dependencies = [
6 [[package]]
7 name = "webfinger-rs"
8 version = "0.0.20"
9- source = "git+https://ayllu-forge.org/ayllu/webfinger-rs?branch=replace-http-with-url#a087dc92f993d9e2157cebba8e68234e13a19c69"
10+ source = "registry+https://github.com/rust-lang/crates.io-index"
11+ checksum = "6c136a6626cec37738c562cab4ac5ee19c9e26fc9d2de79b54148ca82f8689bf"
12 dependencies = [
13 "async-convert",
14 "axum",
15 @@ -3732,7 +3733,6 @@ dependencies = [
16 "serde_with",
17 "thiserror 2.0.12",
18 "tracing",
19- "url",
20 ]
21
22 [[package]]
23 diff --git a/Cargo.toml b/Cargo.toml
24index c5c1d70..118efe6 100644
25--- a/Cargo.toml
26+++ b/Cargo.toml
27 @@ -42,4 +42,4 @@ tokio = { version = "1.46.1", features = ["full"] }
28 tokio-util = { version = "0.7.15", features = ["io", "compat"] }
29 tokio-stream = "0.1.17"
30 tempfile = "3.21.0"
31- webfinger-rs = { git = "https://ayllu-forge.org/ayllu/webfinger-rs", branch = "replace-http-with-url" }
32+ webfinger-rs = "0.0.20"
33 diff --git a/ayllu/src/web2/routes/finger.rs b/ayllu/src/web2/routes/finger.rs
34index 1a072a5..94ede50 100644
35--- a/ayllu/src/web2/routes/finger.rs
36+++ b/ayllu/src/web2/routes/finger.rs
37 @@ -1,7 +1,7 @@
38 use std::collections::HashMap;
39 use std::path::PathBuf;
40
41- use axum::{extract::Extension, response::Json};
42+ use axum::{extract::Extension, http::Uri, response::Json};
43 use ayllu_identity::Identity;
44 use url::Url;
45 use webfinger_rs::{Link, WebFingerRequest, WebFingerResponse};
46 @@ -12,14 +12,6 @@ use crate::{
47 };
48 use ayllu_git::Scanner;
49
50- // mod forge_feed {
51- // pub enum Relation {}
52- // pub enum Namespace {
53- // Label,
54- // VCS,
55- // }
56- // }
57-
58 const FORGE_FEED_REL_AVATAR: &str = "http://forge-feed.org/rel/avatar";
59 // const FORGE_FEED_REL_TICKETING: &str = "http://forge-feed.org/rel/ticketing-system";
60 const FORGE_FEED_REL_REPOSITORY: &str = "http://forge-feed.org/rel/repository";
61 @@ -71,22 +63,32 @@ impl Resolver {
62
63 /// Determine the type of resource the caller wants to resolve but don't
64 /// resolve it yet
65- fn hint(&self, resource: &Url) -> Result<Resource, Error> {
66- match resource.scheme() {
67- SCHEME_ACCT => {
68+ fn hint(&self, resource: &Uri) -> Result<Resource, Error> {
69+ tracing::debug!("Parsing webfinger query from resource: {resource:?}");
70+ match resource.scheme_str() {
71+ Some(SCHEME_ACCT) => {
72 if let Some(identity) = self.identities.get(resource.path()) {
73 Ok(Resource::Acct(identity.clone()))
74 } else {
75 Err(Error::NotFound(resource.path().to_string()))
76 }
77 }
78- SCHEME_PROJECT => {
79- // project == collection
80+ // Ayllu calls a project a "collection"
81+ Some(SCHEME_PROJECT) => {
82+ let collection_name = resource.host().ok_or(Error::Message(format!(
83+ "Missing collection name: {resource:?}"
84+ )))?;
85+ println!("Path: {}", resource.path());
86+ if resource.path() != "/" {
87+ return Err(Error::Message(format!(
88+ "Collection format is project://<name>, got: {resource}"
89+ )));
90+ }
91 let collection = self
92 .collections
93 .iter()
94 .find(|collection| {
95- collection.name == resource.path()
96+ collection.name == collection_name
97 && collection.hidden.is_none_or(|hidden| !hidden)
98 })
99 .ok_or(Error::NotFound(format!(
100 @@ -102,38 +104,44 @@ impl Resolver {
101 .collect(),
102 )))
103 }
104- SCHEME_REPOSITORY => match resource.path().split_once('/') {
105- Some((collection_name, repository)) => {
106- let collection = self
107- .collections
108- .iter()
109- .find(|collection| {
110- collection.name == collection_name
111- && collection.hidden.is_none_or(|hidden| !hidden)
112- })
113- .ok_or(Error::NotFound(format!(
114- "No collection named {collection_name}"
115- )))?;
116- let scanner: Scanner = collection.path.as_path().try_into()?;
117- let repo_path = scanner
118- .into_iter()
119- .find(|path| ayllu_git::name(path.as_path()) == repository)
120- .ok_or(Error::NotFound(format!(
121- "Collection {} does not contain a repository named {repository}",
122- collection.name
123- )))?;
124- Ok(Resource::Repository((collection.clone(), repo_path)))
125+ Some(SCHEME_REPOSITORY) => {
126+ let collection_name = resource.host().ok_or(Error::Message(format!(
127+ "Missing collection name: {resource:?}"
128+ )))?;
129+
130+ let repository_name = resource.path().trim_start_matches("/");
131+ if repository_name.is_empty() {
132+ return Err(Error::Message(format!(
133+ "Repository format is: repository://<project>/<name>: {resource:?}"
134+ )));
135 }
136- None => Err(Error::Message(format!(
137- "Bad repository specified, should have two parts, got: {}",
138- resource.path()
139- ))),
140- },
141- scheme => Err(Error::Message(format!("Bad URI scheme: {scheme}"))),
142+ let collection = self
143+ .collections
144+ .iter()
145+ .find(|collection| {
146+ collection.name == collection_name
147+ && collection.hidden.is_none_or(|hidden| !hidden)
148+ })
149+ .ok_or(Error::NotFound(format!(
150+ "No collection named {} was found",
151+ resource.path()
152+ )))?;
153+ let scanner: Scanner = collection.path.as_path().try_into()?;
154+ let repo_path = scanner
155+ .into_iter()
156+ .find(|path| ayllu_git::name(path.as_path()) == repository_name)
157+ .ok_or(Error::NotFound(format!(
158+ "Collection {} does not contain a repository named {repository_name}",
159+ collection.name
160+ )))?;
161+ Ok(Resource::Repository((collection.clone(), repo_path)))
162+ }
163+ Some(scheme) => Err(Error::Message(format!("Unsupported scheme: {scheme}"))),
164+ None => Err(Error::Message(String::from("No scheme"))),
165 }
166 }
167
168- pub async fn resolve(&self, resource: &Url) -> Result<WebFingerResponse, Error> {
169+ pub async fn resolve(&self, resource: &Uri) -> Result<WebFingerResponse, Error> {
170 match self.hint(resource)? {
171 Resource::Acct(author) => {
172 let mut links: Vec<Link> = Vec::new();