Compare commits

...

27 commits

Author SHA1 Message Date
065fa9c80e
Eyes are closing, git fire 2024-11-15 06:50:51 +01:00
f5449cd397
core: Fix r#type again 2024-11-15 06:31:49 +01:00
6315c22ad6
core: Add missing rel attribute to meta_properties 2024-11-15 06:30:42 +01:00
affb063508
webfinger: Add acrate-core dependency 2024-11-15 05:23:21 +01:00
94203cdc7b
core: Filter meta queries on document 2024-11-15 05:23:06 +01:00
d0aa551ef4
core: Add document to the meta types 2024-11-15 05:21:05 +01:00
b7924c8776
core: Fix r#type in schema 2024-11-15 05:20:46 +01:00
3906d03653
core: Add document field to meta tables 2024-11-15 05:19:50 +01:00
69392bf7e5
core: DROP tables only IF EXISTS 2024-11-15 05:19:25 +01:00
153dc7d50f
core: Cascade table DROPs 2024-11-15 05:18:52 +01:00
3e2a7fd110
core: Expose diesel and diesel_async 2024-11-15 04:59:13 +01:00
c235467b06
core: Add queryable meta structs 2024-11-15 03:16:06 +01:00
18b3619785
core: Apparently you still need the postgres feature on diesel 2024-11-15 02:57:39 +01:00
6ebf2ddd6d
core: Use diesel_async dep 2024-11-15 02:40:58 +01:00
b5d907bd07
core: Use r#type instead of type_ 2024-11-15 02:35:17 +01:00
5a3d913933
core: Don't define database functions, implement them in Rust 2024-11-15 02:34:53 +01:00
21842d9f80
core: Add base core::meta structs 2024-11-15 02:16:16 +01:00
2ca4bb3662
core: Rename link to meta_link_id 2024-11-15 02:15:29 +01:00
84f7002338
core: Make link not null 2024-11-15 02:12:26 +01:00
c27af2cf53
core: Use plural in table names 2024-11-15 02:05:49 +01:00
e65c85616d
core: Use plural in table names 2024-11-15 02:04:15 +01:00
956bd17ad0
core: Make core::meta public 2024-11-15 01:57:32 +01:00
c9afa95fd6
core: Remove dependency on workspace
(This will be the database crate)
2024-11-15 01:57:13 +01:00
34e1eff855
core: Add uuid feature to diesel 2024-11-15 01:51:30 +01:00
c09edc9b08
core: Attach core::schema 2024-11-15 01:49:26 +01:00
ad2383c56b
core: Add postgres feature to diesel 2024-11-15 01:48:01 +01:00
03aab44ba4
core: Rename migration 2024-11-15 01:37:26 +01:00
12 changed files with 310 additions and 84 deletions

View file

@ -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_Add webfinger table/up.sql" dialect="GenericSQL" /> <file url="file://$PROJECT_DIR$/acrate-core/migrations/2024-11-14-031744_meta/up.sql" dialect="GenericSQL" />
</component> </component>
</project> </project>

View file

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

View file

@ -1,8 +0,0 @@
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

@ -1,47 +0,0 @@
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

@ -0,0 +1,4 @@
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

@ -0,0 +1,39 @@
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,4 +1,8 @@
//! Core crate of the `acrate` project. //! Core crate of the `acrate` project.
pub use acrate_nodeinfo as nodeinfo; mod schema;
pub use acrate_hostmeta as hostmeta;
pub mod meta;
pub use diesel;
pub use diesel_async;

109
acrate-core/src/meta.rs Normal file
View file

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

View file

@ -4,6 +4,8 @@ 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"] }

View file

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

View file

@ -1,6 +1,11 @@
use axum::http::StatusCode; use axum::body::Body;
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 {
@ -10,13 +15,127 @@ 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>,
) -> StatusCode { headers: HeaderMap,
) -> 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:#?}");
StatusCode::NO_CONTENT 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)
} }