feat: implementation of proposed NIP-11 (server metadata)

This commit is contained in:
Greg Heartsfield
2022-01-03 18:42:24 -05:00
parent 19637d612e
commit d3da4eb009
7 changed files with 118 additions and 5 deletions
+17
View File
@@ -8,6 +8,16 @@ lazy_static! {
pub static ref SETTINGS: RwLock<Settings> = RwLock::new(Settings::default());
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(unused)]
pub struct Info {
pub name: Option<String>,
#[serde(rename = "description")]
pub descr: Option<String>,
pub pubkey: Option<String>,
pub email: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(unused)]
pub struct Database {
@@ -52,6 +62,7 @@ pub struct Limits {
#[derive(Debug, Serialize, Deserialize)]
#[allow(unused)]
pub struct Settings {
pub info: Info,
pub database: Database,
pub network: Network,
pub limits: Limits,
@@ -89,6 +100,12 @@ impl Settings {
impl Default for Settings {
fn default() -> Self {
Settings {
info: Info {
name: Some("Unnamed nostr-rs-relay".to_owned()),
descr: None,
pubkey: None,
email: None,
},
database: Database {
data_directory: ".".to_owned(),
},
+61
View File
@@ -0,0 +1,61 @@
use crate::config;
/// Relay Info
use serde::{Deserialize, Serialize};
use serde_json::value::Value;
const CARGO_PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
#[derive(Debug, Serialize, Deserialize)]
#[allow(unused)]
pub struct RelayInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub descr: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pubkey: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supported_nips: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub software: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
impl Default for RelayInfo {
fn default() -> Self {
RelayInfo {
name: None,
descr: None,
pubkey: None,
email: None,
supported_nips: Some(vec!["NIP-01".to_owned()]),
software: Some("https://git.sr.ht/~gheartsfield/nostr-rs-relay".to_owned()),
version: CARGO_PKG_VERSION.map(|x| x.to_owned()),
}
}
}
/// Convert an Info struct into Relay Info json string
pub fn relay_info_json(info: &config::Info) -> String {
// get a default RelayInfo
let mut r = RelayInfo::default();
// update fields from Info, if present
r.name = info.name.clone();
r.descr = info.descr.clone();
r.pubkey = info.pubkey.clone();
r.email = info.email.clone();
r.to_json()
}
impl RelayInfo {
pub fn to_json(self) -> String {
// create the info ARRAY
let mut info_arr: Vec<Value> = vec![];
info_arr.push(Value::String("NOSTR_SERVER_INFO".to_owned()));
info_arr.push(serde_json::to_value(&self).unwrap());
serde_json::to_string_pretty(&info_arr).unwrap()
}
}
+1
View File
@@ -4,5 +4,6 @@ pub mod conn;
pub mod db;
pub mod error;
pub mod event;
pub mod info;
pub mod protostream;
pub mod subscription;
+25 -4
View File
@@ -1,6 +1,7 @@
//! Server process
use futures::SinkExt;
use futures::StreamExt;
use hyper::header::ACCEPT;
use hyper::service::{make_service_fn, service_fn};
use hyper::upgrade::Upgraded;
use hyper::{
@@ -13,6 +14,7 @@ use nostr_rs_relay::conn;
use nostr_rs_relay::db;
use nostr_rs_relay::error::{Error, Result};
use nostr_rs_relay::event::Event;
use nostr_rs_relay::info::relay_info_json;
use nostr_rs_relay::protostream;
use nostr_rs_relay::protostream::NostrMessage::*;
use nostr_rs_relay::protostream::NostrResponse::*;
@@ -47,7 +49,7 @@ async fn handle_web_request(
request.uri().path(),
request.headers().contains_key(header::UPGRADE),
) {
//if the request is ws_echo and the request headers contains an Upgrade key
// Request for / as websocket
("/", true) => {
debug!("websocket with upgrade request");
//assume request is a handshake, so create the handshake response
@@ -96,11 +98,30 @@ async fn handle_web_request(
};
Ok::<_, Infallible>(response)
}
// Request for Relay info
("/", false) => {
// handle request at root with no upgrade header
Ok(Response::new(Body::from(
"This is a Nostr relay.\n".to_string(),
)))
// Check if this is a nostr server info request
let accept_header = &request.headers().get(ACCEPT);
// check if application/nostr+json is included
if let Some(media_types) = accept_header {
if let Ok(mt_str) = media_types.to_str() {
if mt_str.contains("application/nostr+json") {
let config = config::SETTINGS.read().unwrap();
// build a relay info response
debug!("Responding to server info request");
let b = Body::from(relay_info_json(&config.info));
return Ok(Response::builder()
.status(200)
.header("Content-Type", "application/nostr+json")
.body(b)
.unwrap());
}
}
}
return Ok(Response::new(Body::from(
"Please use a Nostr client to connect.",
)));
}
(_, _) => {
//handle any other url