Add support for querying NIP-05 on /.well-known/nostr.json?name=username
This commit is contained in:
		
							parent
							
								
									e1306608ef
								
							
						
					
					
						commit
						377da44eed
					
				
					 12 changed files with 278 additions and 101 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::{
 | 
			
		||||
    noose::sled::BanInfo,
 | 
			
		||||
    noose::user::{User, UserRow},
 | 
			
		||||
    noose::user::{User, UserRow, Nip05Profile},
 | 
			
		||||
    utils::{error::Error, structs::Subscription},
 | 
			
		||||
};
 | 
			
		||||
use nostr::secp256k1::XOnlyPublicKey;
 | 
			
		||||
| 
						 | 
				
			
			@ -25,10 +25,14 @@ pub enum Command {
 | 
			
		|||
 | 
			
		||||
    // Old messages
 | 
			
		||||
    DbReqInsertUser(UserRow),
 | 
			
		||||
    DbReqGetUser(User),
 | 
			
		||||
    DbReqCreateAccount(XOnlyPublicKey, String, String),
 | 
			
		||||
    DbReqGetAccount(String),
 | 
			
		||||
    DbReqClear,
 | 
			
		||||
    // NIP-05 related messages
 | 
			
		||||
    DbReqGetUser(String),
 | 
			
		||||
    DbResUser(Nip05Profile),
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    // DbResponse
 | 
			
		||||
    DbResRelayMessages(
 | 
			
		||||
        /* client_id*/ uuid::Uuid,
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +42,6 @@ pub enum Command {
 | 
			
		|||
    DbResOk,
 | 
			
		||||
    DbResOkWithStatus(/* client_id */ uuid::Uuid, nostr::RelayMessage),
 | 
			
		||||
    DbResAccount, // TODO: Add Account DTO as a param
 | 
			
		||||
    DbResUser(UserRow),
 | 
			
		||||
    DbResEventCounts(/* client_id */ uuid::Uuid, nostr::RelayMessage),
 | 
			
		||||
    // Event Pipeline
 | 
			
		||||
    PipelineReqEvent(/* client_id */ uuid::Uuid, Box<nostr::Event>),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@ use crate::{
 | 
			
		|||
use nostr::{Event, RelayMessage};
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use super::user::Nip05Profile;
 | 
			
		||||
 | 
			
		||||
pub trait Noose: Send + Sync {
 | 
			
		||||
    async fn start(&mut self, pubsub: Arc<PubSub>) -> Result<(), Error>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,4 +16,6 @@ pub trait Noose: Send + Sync {
 | 
			
		|||
    async fn find_event(&self, subscription: Subscription) -> Result<Vec<RelayMessage>, Error>;
 | 
			
		||||
 | 
			
		||||
    async fn counts(&self, subscription: Subscription) -> Result<RelayMessage, Error>;
 | 
			
		||||
 | 
			
		||||
    async fn get_nip05(&self, username: String) -> Result<Nip05Profile, Error>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ impl MigrationRunner {
 | 
			
		|||
        let m_events_fts = include_str!("./1697410223576_events_fts.sql");
 | 
			
		||||
        let m_users = include_str!("./1697410294265_users.sql");
 | 
			
		||||
        let m_unattached_media = include_str!("./1697410480767_unattached_media.sql");
 | 
			
		||||
        let m_pragma = include_str!("./1697410424624_pragma.sql");
 | 
			
		||||
        let m_nip05 = include_str!("./1706575155557_nip05.sql");
 | 
			
		||||
 | 
			
		||||
        let migrations = Migrations::new(vec![
 | 
			
		||||
            M::up(m_create_events),
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ impl MigrationRunner {
 | 
			
		|||
            M::up(m_events_fts),
 | 
			
		||||
            M::up(m_users),
 | 
			
		||||
            M::up(m_unattached_media),
 | 
			
		||||
            M::up(m_pragma),
 | 
			
		||||
            M::up(m_nip05),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        match migrations.to_latest(connection) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,8 @@
 | 
			
		|||
use super::{db::Noose, migrations::MigrationRunner};
 | 
			
		||||
use super::{
 | 
			
		||||
    db::Noose,
 | 
			
		||||
    migrations::MigrationRunner,
 | 
			
		||||
    user::{Nip05Profile, Nip05Table, UserRow},
 | 
			
		||||
};
 | 
			
		||||
use crate::{
 | 
			
		||||
    bussy::{channels, Command, Message, PubSub},
 | 
			
		||||
    utils::{config::Config as ServiceConfig, error::Error, structs::Subscription},
 | 
			
		||||
| 
						 | 
				
			
			@ -149,27 +153,6 @@ impl sea_query::Iden for TagsTable {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// enum DeletedCoordinatesTable {
 | 
			
		||||
//     Table,
 | 
			
		||||
//     Coordinate,
 | 
			
		||||
//     CreatedAt,
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// impl sea_query::Iden for DeletedCoordinatesTable {
 | 
			
		||||
//     fn unquoted(&self, s: &mut dyn std::fmt::Write) {
 | 
			
		||||
//         write!(
 | 
			
		||||
//             s,
 | 
			
		||||
//             "{}",
 | 
			
		||||
//             match self {
 | 
			
		||||
//                 Self::Table => "deleted_coordinates",
 | 
			
		||||
//                 Self::Coordinate => "coordinate",
 | 
			
		||||
//                 Self::CreatedAt => "created_at",
 | 
			
		||||
//             }
 | 
			
		||||
//         )
 | 
			
		||||
//         .unwrap()
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
enum EventSeenByRelaysTable {
 | 
			
		||||
    Table,
 | 
			
		||||
    Id,
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +195,22 @@ impl NostrSqlite {
 | 
			
		|||
 | 
			
		||||
    async fn run_migrations(pool: &Pool) -> bool {
 | 
			
		||||
        let connection = pool.get().await.unwrap();
 | 
			
		||||
        connection.interact(MigrationRunner::up).await.unwrap()
 | 
			
		||||
        connection.interact(MigrationRunner::up).await.unwrap();
 | 
			
		||||
        connection
 | 
			
		||||
            .interact(|conn| {
 | 
			
		||||
                conn.pragma_update(None, "encoding", "UTF-8").unwrap();
 | 
			
		||||
                conn.pragma_update(None, "journal_mode", "WAL").unwrap();
 | 
			
		||||
                conn.pragma_update(None, "foreign_keys", "ON").unwrap();
 | 
			
		||||
                conn.pragma_update(None, "auto_vacuum", "FULL").unwrap();
 | 
			
		||||
                conn.pragma_update(None, "journal_size_limit", "32768")
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
                conn.pragma_update(None, "mmap_size", "17179869184")
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
            })
 | 
			
		||||
            .await
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_connection(&self) -> Result<Object, Error> {
 | 
			
		||||
| 
						 | 
				
			
			@ -1225,6 +1223,48 @@ impl NostrSqlite {
 | 
			
		|||
 | 
			
		||||
        query_result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_nip05_profile(&self, username: String) -> Result<Nip05Profile, Error> {
 | 
			
		||||
        let Ok(connection) = self.get_connection().await else {
 | 
			
		||||
            return Err(Error::internal_with_message("Unable to get DB connection"));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let Ok(query_result) = connection
 | 
			
		||||
            .interact(
 | 
			
		||||
                move |conn: &mut rusqlite::Connection| -> Result<Nip05Profile, Error> {
 | 
			
		||||
                    let (sql, value) = Query::select()
 | 
			
		||||
                        .from(Nip05Table::Table)
 | 
			
		||||
                        .columns([
 | 
			
		||||
                            Nip05Table::Pubkey,
 | 
			
		||||
                            Nip05Table::Username,
 | 
			
		||||
                            Nip05Table::Relays,
 | 
			
		||||
                            Nip05Table::JoinedAt,
 | 
			
		||||
                        ])
 | 
			
		||||
                        .and_where(sea_query::Expr::col(Nip05Table::Username).eq(&username))
 | 
			
		||||
                        .limit(1)
 | 
			
		||||
                        .build_rusqlite(SqliteQueryBuilder);
 | 
			
		||||
 | 
			
		||||
                    let Ok(res) = conn.query_row(sql.as_str(), &*value.as_params(), |row| {
 | 
			
		||||
                        let nip05_row: UserRow = row.into();
 | 
			
		||||
                        let nip05 = Nip05Profile::from(nip05_row);
 | 
			
		||||
 | 
			
		||||
                        Ok(nip05)
 | 
			
		||||
                    }) else {
 | 
			
		||||
                        return Err(Error::not_found("user", username));
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    Ok(res)
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
            .await
 | 
			
		||||
        else {
 | 
			
		||||
            return Err(Error::internal_with_message(
 | 
			
		||||
                "Failed to execute query 'get_nip05_profile'",
 | 
			
		||||
            ));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        query_result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<nostr_database::DatabaseError> for Error {
 | 
			
		||||
| 
						 | 
				
			
			@ -1351,6 +1391,7 @@ impl Noose for NostrSqlite {
 | 
			
		|||
        while let Ok(message) = subscriber.recv().await {
 | 
			
		||||
            log::info!("[Noose] received message: {:?}", message);
 | 
			
		||||
            let command = match message.content {
 | 
			
		||||
                // Relay Events
 | 
			
		||||
                Command::DbReqWriteEvent(client_id, event) => match self.write_event(event).await {
 | 
			
		||||
                    Ok(status) => Command::DbResOkWithStatus(client_id, status),
 | 
			
		||||
                    Err(e) => Command::ServiceError(e),
 | 
			
		||||
| 
						 | 
				
			
			@ -1375,6 +1416,11 @@ impl Noose for NostrSqlite {
 | 
			
		|||
                        Err(e) => Command::ServiceError(e),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // NIP-05
 | 
			
		||||
                Command::DbReqGetUser(username) => match self.get_nip05(username).await {
 | 
			
		||||
                    Ok(user) => Command::DbResUser(user),
 | 
			
		||||
                    Err(e) => Command::ServiceError(e),
 | 
			
		||||
                },
 | 
			
		||||
                _ => Command::Noop,
 | 
			
		||||
            };
 | 
			
		||||
            if command != Command::Noop {
 | 
			
		||||
| 
						 | 
				
			
			@ -1439,6 +1485,10 @@ impl Noose for NostrSqlite {
 | 
			
		|||
            Err(err) => Err(Error::internal_with_message(err.to_string())),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_nip05(&self, username: String) -> Result<Nip05Profile, Error> {
 | 
			
		||||
        self.get_nip05_profile(username).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
| 
						 | 
				
			
			@ -1708,4 +1758,14 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
        dbg!(res);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // #[tokio::test]
 | 
			
		||||
    // async fn get_nip05() {
 | 
			
		||||
    //     let config = Arc::new(ServiceConfig::new());
 | 
			
		||||
    //     let db = NostrSqlite::new(config).await;
 | 
			
		||||
 | 
			
		||||
    //     let res = db.get_nip05("test".to_string()).await.unwrap();
 | 
			
		||||
 | 
			
		||||
    //     dbg!(serde_json::to_value(res).unwrap().to_string());
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,27 +1,103 @@
 | 
			
		|||
use chrono::Utc;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use rusqlite::Row;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use validator::{Validate, ValidationError};
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref VALID_CHARACTERS: Regex = Regex::new(r"^[a-zA-Z0-9\_]+$").unwrap();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Validate)]
 | 
			
		||||
pub enum Nip05Table {
 | 
			
		||||
    Table,
 | 
			
		||||
    Pubkey,
 | 
			
		||||
    Username,
 | 
			
		||||
    Relays,
 | 
			
		||||
    JoinedAt,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl sea_query::Iden for Nip05Table {
 | 
			
		||||
    fn unquoted(&self, s: &mut dyn std::fmt::Write) {
 | 
			
		||||
        write!(
 | 
			
		||||
            s,
 | 
			
		||||
            "{}",
 | 
			
		||||
            match self {
 | 
			
		||||
                Self::Table => "nip05",
 | 
			
		||||
                Self::Pubkey => "pubkey",
 | 
			
		||||
                Self::Username => "username",
 | 
			
		||||
                Self::Relays => "relays",
 | 
			
		||||
                Self::JoinedAt => "joined_at",
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
 | 
			
		||||
pub struct UserRow {
 | 
			
		||||
    pub pubkey: String,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    inserted_at: i64,
 | 
			
		||||
    admin: bool,
 | 
			
		||||
    relays: Vec<String>,
 | 
			
		||||
    joined_at: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UserRow {
 | 
			
		||||
    pub fn new(pubkey: String, username: String, admin: bool) -> Self {
 | 
			
		||||
    pub fn new(pubkey: String, username: String, relays: Vec<String>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            pubkey,
 | 
			
		||||
            username,
 | 
			
		||||
            inserted_at: Utc::now().timestamp(),
 | 
			
		||||
            admin,
 | 
			
		||||
            relays,
 | 
			
		||||
            joined_at: nostr::Timestamp::now().as_i64(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Row<'_>> for UserRow {
 | 
			
		||||
    fn from(row: &Row) -> Self {
 | 
			
		||||
        let pubkey: String = row.get("pubkey").unwrap();
 | 
			
		||||
        let username: String = row.get("username").unwrap();
 | 
			
		||||
        let relays_raw: String = row.get("relays").unwrap_or_default();
 | 
			
		||||
        let joined_at: i64 = row.get("joined_at").unwrap();
 | 
			
		||||
 | 
			
		||||
        let relays: Vec<String> = match serde_json::from_str(&relays_raw) {
 | 
			
		||||
            Ok(val) => val,
 | 
			
		||||
            Err(_) => vec![],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            pubkey,
 | 
			
		||||
            username,
 | 
			
		||||
            relays,
 | 
			
		||||
            joined_at,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
 | 
			
		||||
pub struct Nip05Profile {
 | 
			
		||||
    names: HashMap<String, String>,
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    relays: Option<HashMap<String, Vec<String>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UserRow> for Nip05Profile {
 | 
			
		||||
    fn from(value: UserRow) -> Self {
 | 
			
		||||
        let mut name: HashMap<String, String> = HashMap::new();
 | 
			
		||||
        name.insert(value.username, value.pubkey.clone());
 | 
			
		||||
 | 
			
		||||
        let relay = match value.relays.is_empty() {
 | 
			
		||||
            true => None,
 | 
			
		||||
            false => {
 | 
			
		||||
                let mut relay: HashMap<String, Vec<String>> = HashMap::new();
 | 
			
		||||
                relay.insert(value.pubkey.clone(), value.relays);
 | 
			
		||||
 | 
			
		||||
                Some(relay)
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            names: name,
 | 
			
		||||
            relays: relay,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,3 +117,33 @@ pub fn validate_pubkey(value: &str) -> Result<(), ValidationError> {
 | 
			
		|||
        Err(_) => Err(ValidationError::new("Unable to parse pubkey")),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::{Nip05Profile, UserRow};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn make_nip05() {
 | 
			
		||||
        let user_row = UserRow::new(
 | 
			
		||||
            "npub1m2w0ckmkgj4wtvl8muwjynh56j3qd4nddca4exdg4mdrkepvfnhsmusy54".to_string(),
 | 
			
		||||
            "test_user".to_string(),
 | 
			
		||||
            vec![],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let nip05 = Nip05Profile::from(user_row);
 | 
			
		||||
 | 
			
		||||
        dbg!(&nip05);
 | 
			
		||||
        dbg!(serde_json::to_string(&nip05).unwrap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn parse_relay_vec() {
 | 
			
		||||
        let relays_raw = "";
 | 
			
		||||
        let relays: Vec<String> = match serde_json::from_str(relays_raw) {
 | 
			
		||||
            Ok(val) => val,
 | 
			
		||||
            Err(_) => vec![],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        dbg!(relays);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,19 +6,24 @@ use warp::{Filter, Rejection, Reply};
 | 
			
		|||
pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    let cors = warp::cors().allow_any_origin();
 | 
			
		||||
 | 
			
		||||
    static_files().or(index(context)).with(cors)
 | 
			
		||||
    static_files().or(index(context)).with(&cors)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn index(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    // let real_client_ip = warp::header::optional::<std::net::SocketAddr>("X-Real-IP");
 | 
			
		||||
    let real_client_ip = warp::addr::remote();
 | 
			
		||||
    let cors = warp::cors().allow_any_origin();
 | 
			
		||||
 | 
			
		||||
    let relay_information_document_path = warp::path::end().and(warp::header::header("Accept").and(with_context(context.clone())).and_then(handler::relay_config)).with(&cors);
 | 
			
		||||
    let nostr_relay_path = warp::path::end().and(warp::ws().and(with_context(context.clone()))
 | 
			
		||||
    let relay_information_document_path = warp::path::end().and(
 | 
			
		||||
        warp::header::header("Accept")
 | 
			
		||||
            .and(with_context(context.clone()))
 | 
			
		||||
            .and_then(handler::relay_config),
 | 
			
		||||
    );
 | 
			
		||||
    let nostr_relay_path = warp::path::end().and(
 | 
			
		||||
        warp::ws()
 | 
			
		||||
            .and(with_context(context.clone()))
 | 
			
		||||
            .and(real_client_ip)
 | 
			
		||||
                .and_then(handler::ws_handler)
 | 
			
		||||
        .with(&cors));
 | 
			
		||||
            .and_then(handler::ws_handler),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    relay_information_document_path.or(nostr_relay_path)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ pub struct Nip05 {
 | 
			
		|||
#[derive(Serialize, Deserialize, Debug, Validate, Clone)]
 | 
			
		||||
pub struct UserQuery {
 | 
			
		||||
    #[validate(length(min = 1))]
 | 
			
		||||
    pub user: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, Clone, Validate)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,14 +45,9 @@ pub async fn get_account(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_user(user_query: UserQuery, context: Context) -> Result<impl Reply, Rejection> {
 | 
			
		||||
    let name = user_query.user;
 | 
			
		||||
    let mut subscriber = context.pubsub.subscribe(channels::MSG_NIP05).await;
 | 
			
		||||
 | 
			
		||||
    let user = User {
 | 
			
		||||
        name: Some(name),
 | 
			
		||||
        pubkey: None,
 | 
			
		||||
    };
 | 
			
		||||
    let command = Command::DbReqGetUser(user);
 | 
			
		||||
    let command = Command::DbReqGetUser(user_query.name);
 | 
			
		||||
    context
 | 
			
		||||
        .pubsub
 | 
			
		||||
        .publish(
 | 
			
		||||
| 
						 | 
				
			
			@ -65,18 +60,12 @@ pub async fn get_user(user_query: UserQuery, context: Context) -> Result<impl Re
 | 
			
		|||
        .await;
 | 
			
		||||
 | 
			
		||||
    if let Ok(message) = subscriber.recv().await {
 | 
			
		||||
        let mut response = json!({"names": {}, "relays": {}});
 | 
			
		||||
        match message.content {
 | 
			
		||||
            Command::DbResUser(user) => {
 | 
			
		||||
                response = json!({
 | 
			
		||||
                    "names": {
 | 
			
		||||
                        user.username: user.pubkey
 | 
			
		||||
                    },
 | 
			
		||||
                    "relays": {}
 | 
			
		||||
                });
 | 
			
		||||
            Command::DbResUser(profile) => {
 | 
			
		||||
                let response = serde_json::to_value(profile).unwrap();
 | 
			
		||||
                Ok(warp::reply::json(&response))
 | 
			
		||||
            }
 | 
			
		||||
            Command::ServiceError(e) => Ok(warp::reply::json(&response)),
 | 
			
		||||
            Command::ServiceError(e) => Err(warp::reject::custom(e)),
 | 
			
		||||
            _ => Err(warp::reject::custom(Error::internal_with_message(
 | 
			
		||||
                "Unhandeled message type",
 | 
			
		||||
            ))),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
mod accounts;
 | 
			
		||||
// mod accounts;
 | 
			
		||||
pub mod dto;
 | 
			
		||||
mod filter;
 | 
			
		||||
mod handler;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use crate::noose::user::User;
 | 
			
		||||
 | 
			
		||||
use super::accounts::create_account;
 | 
			
		||||
// use super::accounts::create_account;
 | 
			
		||||
use super::dto::{Account, UserQuery};
 | 
			
		||||
use super::filter::{validate_body_filter, validate_query_filter};
 | 
			
		||||
use super::handler::{get_account, get_user};
 | 
			
		||||
| 
						 | 
				
			
			@ -9,41 +9,49 @@ use crate::utils::structs::Context;
 | 
			
		|||
use warp::{Filter, Rejection, Reply};
 | 
			
		||||
 | 
			
		||||
pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    let cors = warp::cors().allow_any_origin();
 | 
			
		||||
    let index = warp::path::end().map(|| warp::reply::html("<h1>SNEED!</h1>"));
 | 
			
		||||
 | 
			
		||||
    index
 | 
			
		||||
        .or(nip05_get(context.clone()))
 | 
			
		||||
        .or(account_create(context.clone()))
 | 
			
		||||
        // .or(account_create(context.clone()))
 | 
			
		||||
        .with(&cors)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn well_known() -> impl Filter<Extract = (), Error = Rejection> + Clone {
 | 
			
		||||
    warp::get().and(warp::path(".well-known"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn nostr_well_known() -> impl Filter<Extract = (), Error = Rejection> + Clone {
 | 
			
		||||
    well_known().and(warp::path("nostr.json"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn nip05_get(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    warp::get()
 | 
			
		||||
        .and(warp::path(".well-known"))
 | 
			
		||||
        .and(warp::path("nostr.json"))
 | 
			
		||||
    nostr_well_known()
 | 
			
		||||
        .and(validate_query_filter::<UserQuery>())
 | 
			
		||||
        .and(with_context(context))
 | 
			
		||||
        .and(with_context(context.clone()))
 | 
			
		||||
        .and_then(get_user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn account_create(
 | 
			
		||||
    context: Context,
 | 
			
		||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    warp::path("account")
 | 
			
		||||
        .and(warp::post())
 | 
			
		||||
        .and(validate_body_filter::<User>())
 | 
			
		||||
        .and(with_context(context))
 | 
			
		||||
        .and_then(create_account)
 | 
			
		||||
}
 | 
			
		||||
// pub fn account_create(
 | 
			
		||||
//     context: Context,
 | 
			
		||||
// ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
//     warp::path("account")
 | 
			
		||||
//         .and(warp::post())
 | 
			
		||||
//         .and(validate_body_filter::<User>())
 | 
			
		||||
//         .and(with_context(context))
 | 
			
		||||
//         .and_then(create_account)
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
pub fn account_get(
 | 
			
		||||
    context: Context,
 | 
			
		||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
    warp::path("account")
 | 
			
		||||
        .and(warp::get())
 | 
			
		||||
        .and(validate_body_filter::<Account>())
 | 
			
		||||
        .and(with_context(context))
 | 
			
		||||
        .and_then(get_account)
 | 
			
		||||
}
 | 
			
		||||
// pub fn account_get(
 | 
			
		||||
//     context: Context,
 | 
			
		||||
// ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
//     warp::path("account")
 | 
			
		||||
//         .and(warp::get())
 | 
			
		||||
//         .and(validate_body_filter::<Account>())
 | 
			
		||||
//         .and(with_context(context))
 | 
			
		||||
//         .and_then(get_account)
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// pub fn account_update(
 | 
			
		||||
//     context: Context,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ impl Config {
 | 
			
		|||
            "contact": "klink@zhitno.st",
 | 
			
		||||
            "name": "zhitno.st",
 | 
			
		||||
            "description": "Very *special* nostr relay",
 | 
			
		||||
            "supported_nips": [ 1, 2, 9, 11, 12, 15, 16, 20, 22, 28, 33, 40, 45 ],
 | 
			
		||||
            "supported_nips": [ 1, 2, 9, 11, 12, 15, 16, 20, 22, 28, 33, 40, 45, 50 ],
 | 
			
		||||
            "software": "git+https://git.zhitno.st/Klink/sneedstr.git",
 | 
			
		||||
            "version": "0.1.1"
 | 
			
		||||
        })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,13 +8,13 @@ use std::{
 | 
			
		|||
use validator::ValidationErrors;
 | 
			
		||||
use warp::{http::StatusCode, reject::Reject};
 | 
			
		||||
 | 
			
		||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
 | 
			
		||||
pub struct Error {
 | 
			
		||||
    pub code: u16,
 | 
			
		||||
    pub message: String,
 | 
			
		||||
    /// Sneedstr version.
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub sneedstr_version: Option<u16>,
 | 
			
		||||
    pub sneedstr_version: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StdError for Error {
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ impl Error {
 | 
			
		|||
        Self {
 | 
			
		||||
            code: code.as_u16(),
 | 
			
		||||
            message,
 | 
			
		||||
            sneedstr_version: None,
 | 
			
		||||
            sneedstr_version: VERSION.to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,12 +54,11 @@ impl Error {
 | 
			
		|||
        Self::new(StatusCode::BAD_REQUEST, message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn not_found<S: Display>(resource: &str, identifier: S, service_version: u16) -> Self {
 | 
			
		||||
    pub fn not_found<S: Display>(resource: &str, identifier: S) -> Self {
 | 
			
		||||
        Self::new(
 | 
			
		||||
            StatusCode::NOT_FOUND,
 | 
			
		||||
            format!("{} not found by {}", resource, identifier),
 | 
			
		||||
        )
 | 
			
		||||
        .sneedstr_version(service_version)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn invalid_param<S: Display>(name: &str, value: S) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -77,11 +76,6 @@ impl Error {
 | 
			
		|||
    pub fn status_code(&self) -> StatusCode {
 | 
			
		||||
        StatusCode::from_u16(self.code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sneedstr_version(mut self, service_version: u16) -> Self {
 | 
			
		||||
        self.sneedstr_version = Some(service_version);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
| 
						 | 
				
			
			@ -116,28 +110,36 @@ mod tests {
 | 
			
		|||
    #[test]
 | 
			
		||||
    fn test_to_string() {
 | 
			
		||||
        let err = Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned());
 | 
			
		||||
        assert_eq!(err.to_string(), "400 Bad Request: invalid address")
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            "Error { code: 400, message: 'invalid address', sneedstr_version: \"0.1.1\" }"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_from_anyhow_error_as_internal_error() {
 | 
			
		||||
        let err = Error::from(anyhow::format_err!("hello"));
 | 
			
		||||
        assert_eq!(err.to_string(), "500 Internal Server Error: hello")
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            "Error { code: 500, message: 'hello', sneedstr_version: \"0.1.1\" }"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_to_string_with_sneedstr_version() {
 | 
			
		||||
        let err =
 | 
			
		||||
            Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned()).sneedstr_version(123);
 | 
			
		||||
        let err = Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            "400 Bad Request: invalid address\ndiem ledger version: 123"
 | 
			
		||||
            "Error { code: 400, message: 'invalid address', sneedstr_version: \"0.1.1\" }"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_internal_error() {
 | 
			
		||||
        let err = Error::internal(anyhow::format_err!("hello"));
 | 
			
		||||
        assert_eq!(err.to_string(), "500 Internal Server Error: hello")
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            "Error { code: 500, message: 'hello', sneedstr_version: \"0.1.1\" }"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue