Compare commits
No commits in common. "065fa9c80e30e0b13132685be4e12399eb42b054" and "2d3421e2c37e4639f9cab3bf6f379d683df7c844" have entirely different histories.
065fa9c80e
...
2d3421e2c3
12 changed files with 84 additions and 310 deletions
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<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>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -4,10 +4,10 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
diesel = { version = "2.2.4", features = ["postgres", "uuid"] }
|
diesel = "2.2.4"
|
||||||
diesel-async = { version = "0.5.1", features = ["postgres"] }
|
|
||||||
diesel_migrations = "2.2.0"
|
diesel_migrations = "2.2.0"
|
||||||
uuid = "1.11.0"
|
acrate-hostmeta = { path = "../acrate-hostmeta" }
|
||||||
|
acrate-nodeinfo = { path = "../acrate-nodeinfo" }
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
tabs-in-doc-comments = "allow"
|
tabs-in-doc-comments = "allow"
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
|
@ -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)
|
|
||||||
);
|
|
|
@ -1,8 +1,4 @@
|
||||||
//! Core crate of the `acrate` project.
|
//! Core crate of the `acrate` project.
|
||||||
|
|
||||||
mod schema;
|
pub use acrate_nodeinfo as nodeinfo;
|
||||||
|
pub use acrate_hostmeta as hostmeta;
|
||||||
pub mod meta;
|
|
||||||
|
|
||||||
pub use diesel;
|
|
||||||
pub use diesel_async;
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +1,46 @@
|
||||||
// @generated automatically by Diesel CLI.
|
// @generated automatically by Diesel CLI.
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
meta_aliases (id) {
|
meta_alias (id) {
|
||||||
id -> Uuid,
|
id -> Uuid,
|
||||||
document -> Bpchar,
|
|
||||||
pattern -> Bpchar,
|
pattern -> Bpchar,
|
||||||
alias -> Bpchar,
|
alias -> Bpchar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
meta_link_properties (id) {
|
meta_link (id) {
|
||||||
id -> Uuid,
|
id -> Uuid,
|
||||||
meta_link_id -> Uuid,
|
|
||||||
rel -> Bpchar,
|
|
||||||
value -> Nullable<Bpchar>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
meta_links (id) {
|
|
||||||
id -> Uuid,
|
|
||||||
document -> Bpchar,
|
|
||||||
pattern -> Bpchar,
|
pattern -> Bpchar,
|
||||||
rel -> Bpchar,
|
rel -> Bpchar,
|
||||||
r#type -> Nullable<Bpchar>,
|
#[sql_name = "type"]
|
||||||
|
type_ -> Nullable<Bpchar>,
|
||||||
href -> Nullable<Bpchar>,
|
href -> Nullable<Bpchar>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
meta_properties (id) {
|
meta_link_property (id) {
|
||||||
id -> Uuid,
|
id -> Uuid,
|
||||||
document -> Bpchar,
|
link -> Nullable<Uuid>,
|
||||||
pattern -> Bpchar,
|
|
||||||
rel -> Bpchar,
|
rel -> Bpchar,
|
||||||
value -> Nullable<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!(
|
diesel::allow_tables_to_appear_in_same_query!(
|
||||||
meta_aliases,
|
meta_alias,
|
||||||
meta_link_properties,
|
meta_link,
|
||||||
meta_links,
|
meta_link_property,
|
||||||
meta_properties,
|
meta_property,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,8 +4,6 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acrate-core = { path = "../acrate-core" }
|
|
||||||
acrate-hostmeta = { path = "../acrate-hostmeta" }
|
|
||||||
anyhow = "1.0.93"
|
anyhow = "1.0.93"
|
||||||
axum = { version = "0.7.7", features = ["macros"] }
|
axum = { version = "0.7.7", features = ["macros"] }
|
||||||
axum-extra = { version = "0.9.4", features = ["query"] }
|
axum-extra = { version = "0.9.4", features = ["query"] }
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
micronfig::config!(
|
micronfig::config!(
|
||||||
ACRATE_WEBFINGER_DATABASE_URL: String,
|
|
||||||
ACRATE_WEBFINGER_BIND_ADDRESS: String,
|
ACRATE_WEBFINGER_BIND_ADDRESS: String,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
use axum::body::Body;
|
use axum::http::StatusCode;
|
||||||
use axum::http::{HeaderMap, HeaderValue, StatusCode};
|
|
||||||
use axum_extra::extract::Query;
|
use axum_extra::extract::Query;
|
||||||
use serde::Deserialize;
|
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)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct WebfingerQuery {
|
pub struct WebfingerQuery {
|
||||||
|
@ -15,127 +10,13 @@ pub struct WebfingerQuery {
|
||||||
pub rel: Vec<String>,
|
pub rel: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const WEBFINGER_DOC: &str = "/.well-known/webfinger";
|
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn webfinger_handler(
|
pub async fn webfinger_handler(
|
||||||
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>,
|
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>
|
||||||
headers: HeaderMap,
|
) -> StatusCode {
|
||||||
) -> Result<(Body, HeaderMap), StatusCode> {
|
|
||||||
log::info!("Handling a WebFinger request!");
|
log::info!("Handling a WebFinger request!");
|
||||||
|
|
||||||
log::debug!("Resource is: {resource:#?}");
|
log::debug!("Resource is: {resource:#?}");
|
||||||
|
|
||||||
log::debug!("Rel is: {rel:#?}");
|
log::debug!("Rel is: {rel:#?}");
|
||||||
|
|
||||||
let accept = headers.get("Accept")
|
StatusCode::NO_CONTENT
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue