Compare commits

..

No commits in common. "065fa9c80e30e0b13132685be4e12399eb42b054" and "2d3421e2c37e4639f9cab3bf6f379d683df7c844" have entirely different histories.

12 changed files with 84 additions and 310 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/acrate-core/migrations/2024-11-14-031744_meta/up.sql" dialect="GenericSQL" />
<file url="file://$PROJECT_DIR$/acrate-core/migrations/2024-11-14-031744_Add webfinger table/up.sql" dialect="GenericSQL" />
</component>
</project>

View file

@ -4,10 +4,10 @@ version = "0.1.0"
edition = "2021"
[dependencies]
diesel = { version = "2.2.4", features = ["postgres", "uuid"] }
diesel-async = { version = "0.5.1", features = ["postgres"] }
diesel = "2.2.4"
diesel_migrations = "2.2.0"
uuid = "1.11.0"
acrate-hostmeta = { path = "../acrate-hostmeta" }
acrate-nodeinfo = { path = "../acrate-nodeinfo" }
[lints.clippy]
tabs-in-doc-comments = "allow"

View file

@ -0,0 +1,8 @@
DROP FUNCTION get_meta_property;
DROP FUNCTION get_meta_link;
DROP FUNCTION get_meta_aliases;
DROP TABLE meta_property;
DROP TABLE meta_link_property;
DROP TABLE meta_link;
DROP TABLE meta_alias;

View file

@ -0,0 +1,47 @@
CREATE TABLE meta_alias (
id UUID DEFAULT gen_random_uuid(),
pattern BPCHAR NOT NULL,
alias BPCHAR NOT NULL,
CONSTRAINT unique_aliases UNIQUE (alias),
PRIMARY KEY (id)
);
CREATE TABLE meta_link (
id UUID DEFAULT gen_random_uuid(),
pattern BPCHAR NOT NULL,
rel BPCHAR NOT NULL,
type BPCHAR,
href BPCHAR,
PRIMARY KEY (id)
);
CREATE TABLE meta_link_property (
id UUID DEFAULT gen_random_uuid(),
link UUID REFERENCES meta_link (id),
rel BPCHAR NOT NULL,
value BPCHAR,
PRIMARY KEY (id)
);
CREATE TABLE meta_property (
id UUID DEFAULT gen_random_uuid(),
pattern BPCHAR NOT NULL,
value BPCHAR,
PRIMARY KEY (id)
);
CREATE FUNCTION get_meta_aliases(BPCHAR) RETURNS SETOF meta_alias AS $$
SELECT * FROM meta_alias WHERE meta_alias.pattern ILIKE $1;
$$ LANGUAGE SQL;
CREATE FUNCTION get_meta_link(BPCHAR) RETURNS SETOF meta_link AS $$
SELECT * FROM meta_link WHERE meta_link.pattern ILIKE $1;
$$ LANGUAGE SQL;
CREATE FUNCTION get_meta_property(BPCHAR) RETURNS SETOF meta_property AS $$
SELECT * FROM meta_property WHERE meta_property.pattern ILIKE $1;
$$ LANGUAGE SQL;

View file

@ -1,4 +0,0 @@
DROP TABLE IF EXISTS meta_properties CASCADE;
DROP TABLE IF EXISTS meta_link_properties CASCADE;
DROP TABLE IF EXISTS meta_links CASCADE;
DROP TABLE IF EXISTS meta_aliases CASCADE;

View file

@ -1,39 +0,0 @@
CREATE TABLE meta_aliases (
id UUID DEFAULT gen_random_uuid(),
document BPCHAR NOT NULL,
pattern BPCHAR NOT NULL,
alias BPCHAR NOT NULL,
CONSTRAINT unique_aliases UNIQUE (alias),
PRIMARY KEY (id)
);
CREATE TABLE meta_links (
id UUID DEFAULT gen_random_uuid(),
document BPCHAR NOT NULL,
pattern BPCHAR NOT NULL,
rel BPCHAR NOT NULL,
type BPCHAR,
href BPCHAR,
PRIMARY KEY (id)
);
CREATE TABLE meta_link_properties (
id UUID DEFAULT gen_random_uuid(),
meta_link_id UUID REFERENCES meta_links (id) NOT NULL,
rel BPCHAR NOT NULL,
value BPCHAR,
PRIMARY KEY (id)
);
CREATE TABLE meta_properties (
id UUID DEFAULT gen_random_uuid(),
document BPCHAR NOT NULL,
pattern BPCHAR NOT NULL,
rel BPCHAR NOT NULL,
value BPCHAR,
PRIMARY KEY (id)
);

View file

@ -1,8 +1,4 @@
//! Core crate of the `acrate` project.
mod schema;
pub mod meta;
pub use diesel;
pub use diesel_async;
pub use acrate_nodeinfo as nodeinfo;
pub use acrate_hostmeta as hostmeta;

View file

@ -1,109 +0,0 @@
use diesel::{Associations, Identifiable, Insertable, QueryResult, Queryable, QueryableByName, Selectable};
use diesel_async::AsyncPgConnection;
use uuid::Uuid;
use super::schema;
#[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Insertable)]
#[diesel(table_name = schema::meta_aliases)]
pub struct MetaAlias {
pub id: Uuid,
pub document: String,
pub pattern: String,
pub alias: String,
}
#[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Insertable)]
#[diesel(table_name = schema::meta_links)]
pub struct MetaLink {
pub id: Uuid,
pub document: String,
pub pattern: String,
pub rel: String,
pub r#type: Option<String>,
pub href: Option<String>,
}
#[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Insertable, Associations)]
#[diesel(belongs_to(MetaLink))]
#[diesel(table_name = schema::meta_link_properties)]
pub struct MetaLinkProperty {
pub id: Uuid,
pub meta_link_id: Uuid,
pub rel: String,
pub value: Option<String>,
}
#[derive(Debug, Queryable, QueryableByName, Identifiable, Selectable, Insertable)]
#[diesel(table_name = schema::meta_properties)]
pub struct MetaProperty {
pub id: Uuid,
pub document: String,
pub pattern: String,
pub rel: String,
pub value: Option<String>,
}
impl MetaAlias {
pub async fn query_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult<Vec<MetaAlias>> {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use schema::meta_aliases::dsl::*;
let document_is_equal = document.eq(doc);
let subject_matches_pattern = subject.into_sql::<diesel::sql_types::Text>().ilike(pattern);
meta_aliases
.filter(document_is_equal)
.filter(subject_matches_pattern)
.select(Self::as_select())
.load(conn)
.await
}
}
impl MetaLink {
pub async fn query_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult<Vec<MetaLink>> {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use schema::meta_links::dsl::*;
let document_is_equal = document.eq(doc);
let subject_matches_pattern = subject.into_sql::<diesel::sql_types::Text>().ilike(pattern);
meta_links
.filter(document_is_equal)
.filter(subject_matches_pattern)
.select(Self::as_select())
.load(conn)
.await
}
pub async fn query_properties(&self, conn: &mut AsyncPgConnection) -> QueryResult<Vec<MetaLinkProperty>> {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
MetaLinkProperty::belonging_to(self)
.load(conn)
.await
}
}
impl MetaProperty {
pub async fn query_matching(conn: &mut AsyncPgConnection, doc: &str, subject: &str) -> QueryResult<Vec<MetaProperty>> {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use schema::meta_properties::dsl::*;
let document_is_equal = document.eq(doc);
let subject_matches_pattern = subject.into_sql::<diesel::sql_types::Text>().ilike(pattern);
meta_properties
.filter(document_is_equal)
.filter(subject_matches_pattern)
.select(Self::as_select())
.load(conn)
.await
}
}

View file

@ -1,49 +1,46 @@
// @generated automatically by Diesel CLI.
diesel::table! {
meta_aliases (id) {
meta_alias (id) {
id -> Uuid,
document -> Bpchar,
pattern -> Bpchar,
alias -> Bpchar,
}
}
diesel::table! {
meta_link_properties (id) {
meta_link (id) {
id -> Uuid,
meta_link_id -> Uuid,
rel -> Bpchar,
value -> Nullable<Bpchar>,
}
}
diesel::table! {
meta_links (id) {
id -> Uuid,
document -> Bpchar,
pattern -> Bpchar,
rel -> Bpchar,
r#type -> Nullable<Bpchar>,
#[sql_name = "type"]
type_ -> Nullable<Bpchar>,
href -> Nullable<Bpchar>,
}
}
diesel::table! {
meta_properties (id) {
meta_link_property (id) {
id -> Uuid,
document -> Bpchar,
pattern -> Bpchar,
link -> Nullable<Uuid>,
rel -> Bpchar,
value -> Nullable<Bpchar>,
}
}
diesel::joinable!(meta_link_properties -> meta_links (meta_link_id));
diesel::table! {
meta_property (id) {
id -> Uuid,
pattern -> Bpchar,
value -> Nullable<Bpchar>,
}
}
diesel::joinable!(meta_link_property -> meta_link (link));
diesel::allow_tables_to_appear_in_same_query!(
meta_aliases,
meta_link_properties,
meta_links,
meta_properties,
meta_alias,
meta_link,
meta_link_property,
meta_property,
);

View file

@ -4,8 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
acrate-core = { path = "../acrate-core" }
acrate-hostmeta = { path = "../acrate-hostmeta" }
anyhow = "1.0.93"
axum = { version = "0.7.7", features = ["macros"] }
axum-extra = { version = "0.9.4", features = ["query"] }

View file

@ -1,4 +1,3 @@
micronfig::config!(
ACRATE_WEBFINGER_DATABASE_URL: String,
ACRATE_WEBFINGER_BIND_ADDRESS: String,
);

View file

@ -1,11 +1,6 @@
use axum::body::Body;
use axum::http::{HeaderMap, HeaderValue, StatusCode};
use axum::http::StatusCode;
use axum_extra::extract::Query;
use serde::Deserialize;
use acrate_core::diesel_async::{AsyncConnection, AsyncPgConnection};
use acrate_hostmeta::jrd::ResourceDescriptorLinkJRD;
use acrate_hostmeta::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD};
use crate::config;
#[derive(Debug, Clone, Deserialize)]
pub struct WebfingerQuery {
@ -15,127 +10,13 @@ pub struct WebfingerQuery {
pub rel: Vec<String>,
}
const WEBFINGER_DOC: &str = "/.well-known/webfinger";
#[axum::debug_handler]
pub async fn webfinger_handler(
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>,
headers: HeaderMap,
) -> Result<(Body, HeaderMap), StatusCode> {
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>
) -> StatusCode {
log::info!("Handling a WebFinger request!");
log::debug!("Resource is: {resource:#?}");
log::debug!("Rel is: {rel:#?}");
let accept = headers.get("Accept")
.map(|v| v.to_str())
.filter(Result::is_ok)
.map(|v| v.unwrap())
.unwrap_or("application/json")
.to_string();
log::debug!("Accept is: {accept:#?}");
let mut response_headers = HeaderMap::new();
let mut conn = AsyncPgConnection::establish(config::ACRATE_WEBFINGER_DATABASE_URL())
.await
.map_err(|_| StatusCode::BAD_GATEWAY)?;
let aliases = acrate_core::meta::MetaAlias::query_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let properties = acrate_core::meta::MetaProperty::query_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let links = acrate_core::meta::MetaLink::query_matching(&mut conn, WEBFINGER_DOC, &resource)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
for mime in accept.split(", ") {
response_headers.insert("Content-Type", mime.parse().unwrap());
match mime {
"application/json" | "application/jrd+json" => {
let subject = Some(resource);
let aliases = aliases
.into_iter()
.map(|alias| alias.alias)
.collect();
let properties = properties
.into_iter()
.map(|prop| (prop.rel, prop.value))
.collect();
let links = links
.into_iter()
.map(|link| ResourceDescriptorLinkJRD {
rel: link.rel,
r#type: link.r#type,
href: link.href,
titles: Default::default(), // TODO: Titles
properties: Default::default(), // TODO: Link properties
template: None, // TODO: Template
})
.collect::<Vec<ResourceDescriptorLinkJRD>>();
let rd = acrate_hostmeta::jrd::ResourceDescriptorJRD {
subject,
aliases,
properties,
links,
};
let body = rd.
return Ok(rd, response_headers)
},
"application/xml" | "application/xrd+xml" => {
let subject = Some(resource);
let aliases = aliases
.into_iter()
.map(|alias| alias.alias)
.collect();
let properties = properties
.into_iter()
.map(|prop| ResourceDescriptorPropertyXRD {
r#type: prop.rel, // TODO: Ah si chiama type?
value: prop.value,
})
.collect();
let links = links
.into_iter()
.map(|link| ResourceDescriptorLinkXRD {
rel: link.rel,
r#type: link.r#type,
href: link.href,
titles: Default::default(), // TODO: Titles
properties: Default::default(), // TODO: Link properties
template: None, // TODO: Template
})
.collect::<Vec<ResourceDescriptorLinkXRD>>();
let rd = acrate_hostmeta::xrd::ResourceDescriptorXRD {
subject,
aliases,
properties,
links,
};
return Ok(StatusCode::OK)
},
_ => {
continue;
}
}
}
Err(StatusCode::NOT_ACCEPTABLE)
StatusCode::NO_CONTENT
}