Add support for querying NIP-05 on /.well-known/nostr.json?name=username
This commit is contained in:
parent
e1306608ef
commit
377da44eed
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
noose::sled::BanInfo,
|
noose::sled::BanInfo,
|
||||||
noose::user::{User, UserRow},
|
noose::user::{User, UserRow, Nip05Profile},
|
||||||
utils::{error::Error, structs::Subscription},
|
utils::{error::Error, structs::Subscription},
|
||||||
};
|
};
|
||||||
use nostr::secp256k1::XOnlyPublicKey;
|
use nostr::secp256k1::XOnlyPublicKey;
|
||||||
|
@ -25,10 +25,14 @@ pub enum Command {
|
||||||
|
|
||||||
// Old messages
|
// Old messages
|
||||||
DbReqInsertUser(UserRow),
|
DbReqInsertUser(UserRow),
|
||||||
DbReqGetUser(User),
|
|
||||||
DbReqCreateAccount(XOnlyPublicKey, String, String),
|
DbReqCreateAccount(XOnlyPublicKey, String, String),
|
||||||
DbReqGetAccount(String),
|
DbReqGetAccount(String),
|
||||||
DbReqClear,
|
DbReqClear,
|
||||||
|
// NIP-05 related messages
|
||||||
|
DbReqGetUser(String),
|
||||||
|
DbResUser(Nip05Profile),
|
||||||
|
|
||||||
|
|
||||||
// DbResponse
|
// DbResponse
|
||||||
DbResRelayMessages(
|
DbResRelayMessages(
|
||||||
/* client_id*/ uuid::Uuid,
|
/* client_id*/ uuid::Uuid,
|
||||||
|
@ -38,7 +42,6 @@ pub enum Command {
|
||||||
DbResOk,
|
DbResOk,
|
||||||
DbResOkWithStatus(/* client_id */ uuid::Uuid, nostr::RelayMessage),
|
DbResOkWithStatus(/* client_id */ uuid::Uuid, nostr::RelayMessage),
|
||||||
DbResAccount, // TODO: Add Account DTO as a param
|
DbResAccount, // TODO: Add Account DTO as a param
|
||||||
DbResUser(UserRow),
|
|
||||||
DbResEventCounts(/* client_id */ uuid::Uuid, nostr::RelayMessage),
|
DbResEventCounts(/* client_id */ uuid::Uuid, nostr::RelayMessage),
|
||||||
// Event Pipeline
|
// Event Pipeline
|
||||||
PipelineReqEvent(/* client_id */ uuid::Uuid, Box<nostr::Event>),
|
PipelineReqEvent(/* client_id */ uuid::Uuid, Box<nostr::Event>),
|
||||||
|
|
|
@ -6,6 +6,8 @@ use crate::{
|
||||||
use nostr::{Event, RelayMessage};
|
use nostr::{Event, RelayMessage};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::user::Nip05Profile;
|
||||||
|
|
||||||
pub trait Noose: Send + Sync {
|
pub trait Noose: Send + Sync {
|
||||||
async fn start(&mut self, pubsub: Arc<PubSub>) -> Result<(), Error>;
|
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 find_event(&self, subscription: Subscription) -> Result<Vec<RelayMessage>, Error>;
|
||||||
|
|
||||||
async fn counts(&self, subscription: Subscription) -> Result<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_events_fts = include_str!("./1697410223576_events_fts.sql");
|
||||||
let m_users = include_str!("./1697410294265_users.sql");
|
let m_users = include_str!("./1697410294265_users.sql");
|
||||||
let m_unattached_media = include_str!("./1697410480767_unattached_media.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![
|
let migrations = Migrations::new(vec![
|
||||||
M::up(m_create_events),
|
M::up(m_create_events),
|
||||||
|
@ -20,7 +20,7 @@ impl MigrationRunner {
|
||||||
M::up(m_events_fts),
|
M::up(m_events_fts),
|
||||||
M::up(m_users),
|
M::up(m_users),
|
||||||
M::up(m_unattached_media),
|
M::up(m_unattached_media),
|
||||||
M::up(m_pragma),
|
M::up(m_nip05),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
match migrations.to_latest(connection) {
|
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::{
|
use crate::{
|
||||||
bussy::{channels, Command, Message, PubSub},
|
bussy::{channels, Command, Message, PubSub},
|
||||||
utils::{config::Config as ServiceConfig, error::Error, structs::Subscription},
|
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 {
|
enum EventSeenByRelaysTable {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
|
@ -212,7 +195,22 @@ impl NostrSqlite {
|
||||||
|
|
||||||
async fn run_migrations(pool: &Pool) -> bool {
|
async fn run_migrations(pool: &Pool) -> bool {
|
||||||
let connection = pool.get().await.unwrap();
|
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> {
|
async fn get_connection(&self) -> Result<Object, Error> {
|
||||||
|
@ -1225,6 +1223,48 @@ impl NostrSqlite {
|
||||||
|
|
||||||
query_result
|
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 {
|
impl From<nostr_database::DatabaseError> for Error {
|
||||||
|
@ -1351,6 +1391,7 @@ impl Noose for NostrSqlite {
|
||||||
while let Ok(message) = subscriber.recv().await {
|
while let Ok(message) = subscriber.recv().await {
|
||||||
log::info!("[Noose] received message: {:?}", message);
|
log::info!("[Noose] received message: {:?}", message);
|
||||||
let command = match message.content {
|
let command = match message.content {
|
||||||
|
// Relay Events
|
||||||
Command::DbReqWriteEvent(client_id, event) => match self.write_event(event).await {
|
Command::DbReqWriteEvent(client_id, event) => match self.write_event(event).await {
|
||||||
Ok(status) => Command::DbResOkWithStatus(client_id, status),
|
Ok(status) => Command::DbResOkWithStatus(client_id, status),
|
||||||
Err(e) => Command::ServiceError(e),
|
Err(e) => Command::ServiceError(e),
|
||||||
|
@ -1375,6 +1416,11 @@ impl Noose for NostrSqlite {
|
||||||
Err(e) => Command::ServiceError(e),
|
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,
|
_ => Command::Noop,
|
||||||
};
|
};
|
||||||
if command != Command::Noop {
|
if command != Command::Noop {
|
||||||
|
@ -1439,6 +1485,10 @@ impl Noose for NostrSqlite {
|
||||||
Err(err) => Err(Error::internal_with_message(err.to_string())),
|
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)]
|
#[cfg(test)]
|
||||||
|
@ -1708,4 +1758,14 @@ mod tests {
|
||||||
|
|
||||||
dbg!(res);
|
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 regex::Regex;
|
||||||
|
use rusqlite::Row;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use validator::{Validate, ValidationError};
|
use validator::{Validate, ValidationError};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref VALID_CHARACTERS: Regex = Regex::new(r"^[a-zA-Z0-9\_]+$").unwrap();
|
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 struct UserRow {
|
||||||
pub pubkey: String,
|
pub pubkey: String,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
inserted_at: i64,
|
relays: Vec<String>,
|
||||||
admin: bool,
|
joined_at: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserRow {
|
impl UserRow {
|
||||||
pub fn new(pubkey: String, username: String, admin: bool) -> Self {
|
pub fn new(pubkey: String, username: String, relays: Vec<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pubkey,
|
pubkey,
|
||||||
username,
|
username,
|
||||||
inserted_at: Utc::now().timestamp(),
|
relays,
|
||||||
admin,
|
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")),
|
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 {
|
pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
let cors = warp::cors().allow_any_origin();
|
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 {
|
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::header::optional::<std::net::SocketAddr>("X-Real-IP");
|
||||||
let real_client_ip = warp::addr::remote();
|
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 relay_information_document_path = warp::path::end().and(
|
||||||
let nostr_relay_path = warp::path::end().and(warp::ws().and(with_context(context.clone()))
|
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(real_client_ip)
|
||||||
.and_then(handler::ws_handler)
|
.and_then(handler::ws_handler),
|
||||||
.with(&cors));
|
);
|
||||||
|
|
||||||
relay_information_document_path.or(nostr_relay_path)
|
relay_information_document_path.or(nostr_relay_path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub struct Nip05 {
|
||||||
#[derive(Serialize, Deserialize, Debug, Validate, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Validate, Clone)]
|
||||||
pub struct UserQuery {
|
pub struct UserQuery {
|
||||||
#[validate(length(min = 1))]
|
#[validate(length(min = 1))]
|
||||||
pub user: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Validate)]
|
#[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> {
|
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 mut subscriber = context.pubsub.subscribe(channels::MSG_NIP05).await;
|
||||||
|
|
||||||
let user = User {
|
let command = Command::DbReqGetUser(user_query.name);
|
||||||
name: Some(name),
|
|
||||||
pubkey: None,
|
|
||||||
};
|
|
||||||
let command = Command::DbReqGetUser(user);
|
|
||||||
context
|
context
|
||||||
.pubsub
|
.pubsub
|
||||||
.publish(
|
.publish(
|
||||||
|
@ -65,18 +60,12 @@ pub async fn get_user(user_query: UserQuery, context: Context) -> Result<impl Re
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Ok(message) = subscriber.recv().await {
|
if let Ok(message) = subscriber.recv().await {
|
||||||
let mut response = json!({"names": {}, "relays": {}});
|
|
||||||
match message.content {
|
match message.content {
|
||||||
Command::DbResUser(user) => {
|
Command::DbResUser(profile) => {
|
||||||
response = json!({
|
let response = serde_json::to_value(profile).unwrap();
|
||||||
"names": {
|
|
||||||
user.username: user.pubkey
|
|
||||||
},
|
|
||||||
"relays": {}
|
|
||||||
});
|
|
||||||
Ok(warp::reply::json(&response))
|
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(
|
_ => Err(warp::reject::custom(Error::internal_with_message(
|
||||||
"Unhandeled message type",
|
"Unhandeled message type",
|
||||||
))),
|
))),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod accounts;
|
// mod accounts;
|
||||||
pub mod dto;
|
pub mod dto;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::noose::user::User;
|
use crate::noose::user::User;
|
||||||
|
|
||||||
use super::accounts::create_account;
|
// use super::accounts::create_account;
|
||||||
use super::dto::{Account, UserQuery};
|
use super::dto::{Account, UserQuery};
|
||||||
use super::filter::{validate_body_filter, validate_query_filter};
|
use super::filter::{validate_body_filter, validate_query_filter};
|
||||||
use super::handler::{get_account, get_user};
|
use super::handler::{get_account, get_user};
|
||||||
|
@ -9,41 +9,49 @@ use crate::utils::structs::Context;
|
||||||
use warp::{Filter, Rejection, Reply};
|
use warp::{Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
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>"));
|
let index = warp::path::end().map(|| warp::reply::html("<h1>SNEED!</h1>"));
|
||||||
|
|
||||||
index
|
index
|
||||||
.or(nip05_get(context.clone()))
|
.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 {
|
pub fn nip05_get(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
warp::get()
|
nostr_well_known()
|
||||||
.and(warp::path(".well-known"))
|
|
||||||
.and(warp::path("nostr.json"))
|
|
||||||
.and(validate_query_filter::<UserQuery>())
|
.and(validate_query_filter::<UserQuery>())
|
||||||
.and(with_context(context))
|
.and(with_context(context.clone()))
|
||||||
.and_then(get_user)
|
.and_then(get_user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_create(
|
// pub fn account_create(
|
||||||
context: Context,
|
// context: Context,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
// ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
warp::path("account")
|
// warp::path("account")
|
||||||
.and(warp::post())
|
// .and(warp::post())
|
||||||
.and(validate_body_filter::<User>())
|
// .and(validate_body_filter::<User>())
|
||||||
.and(with_context(context))
|
// .and(with_context(context))
|
||||||
.and_then(create_account)
|
// .and_then(create_account)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn account_get(
|
// pub fn account_get(
|
||||||
context: Context,
|
// context: Context,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
// ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
warp::path("account")
|
// warp::path("account")
|
||||||
.and(warp::get())
|
// .and(warp::get())
|
||||||
.and(validate_body_filter::<Account>())
|
// .and(validate_body_filter::<Account>())
|
||||||
.and(with_context(context))
|
// .and(with_context(context))
|
||||||
.and_then(get_account)
|
// .and_then(get_account)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// pub fn account_update(
|
// pub fn account_update(
|
||||||
// context: Context,
|
// context: Context,
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl Config {
|
||||||
"contact": "klink@zhitno.st",
|
"contact": "klink@zhitno.st",
|
||||||
"name": "zhitno.st",
|
"name": "zhitno.st",
|
||||||
"description": "Very *special* nostr relay",
|
"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",
|
"software": "git+https://git.zhitno.st/Klink/sneedstr.git",
|
||||||
"version": "0.1.1"
|
"version": "0.1.1"
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,13 +8,13 @@ use std::{
|
||||||
use validator::ValidationErrors;
|
use validator::ValidationErrors;
|
||||||
use warp::{http::StatusCode, reject::Reject};
|
use warp::{http::StatusCode, reject::Reject};
|
||||||
|
|
||||||
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
pub code: u16,
|
pub code: u16,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
/// Sneedstr version.
|
pub sneedstr_version: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub sneedstr_version: Option<u16>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {
|
impl StdError for Error {
|
||||||
|
@ -32,7 +32,7 @@ impl Error {
|
||||||
Self {
|
Self {
|
||||||
code: code.as_u16(),
|
code: code.as_u16(),
|
||||||
message,
|
message,
|
||||||
sneedstr_version: None,
|
sneedstr_version: VERSION.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +54,11 @@ impl Error {
|
||||||
Self::new(StatusCode::BAD_REQUEST, message)
|
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(
|
Self::new(
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
format!("{} not found by {}", resource, identifier),
|
format!("{} not found by {}", resource, identifier),
|
||||||
)
|
)
|
||||||
.sneedstr_version(service_version)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_param<S: Display>(name: &str, value: S) -> Self {
|
pub fn invalid_param<S: Display>(name: &str, value: S) -> Self {
|
||||||
|
@ -77,11 +76,6 @@ impl Error {
|
||||||
pub fn status_code(&self) -> StatusCode {
|
pub fn status_code(&self) -> StatusCode {
|
||||||
StatusCode::from_u16(self.code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
|
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 {
|
impl fmt::Display for Error {
|
||||||
|
@ -116,28 +110,36 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_string() {
|
fn test_to_string() {
|
||||||
let err = Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned());
|
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]
|
#[test]
|
||||||
fn test_from_anyhow_error_as_internal_error() {
|
fn test_from_anyhow_error_as_internal_error() {
|
||||||
let err = Error::from(anyhow::format_err!("hello"));
|
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]
|
#[test]
|
||||||
fn test_to_string_with_sneedstr_version() {
|
fn test_to_string_with_sneedstr_version() {
|
||||||
let err =
|
let err = Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned());
|
||||||
Error::new(StatusCode::BAD_REQUEST, "invalid address".to_owned()).sneedstr_version(123);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
"400 Bad Request: invalid address\ndiem ledger version: 123"
|
"Error { code: 400, message: 'invalid address', sneedstr_version: \"0.1.1\" }"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_internal_error() {
|
fn test_internal_error() {
|
||||||
let err = Error::internal(anyhow::format_err!("hello"));
|
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…
Reference in a new issue