Fix: Protocol implementation error
Build and Push Docker Image / build_docker_image (push) Has been cancelled
Build and Push Docker Image / build_docker_image (push) Has been cancelled
This commit is contained in:
@@ -22,3 +22,4 @@ target/
|
|||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
.zed/
|
.zed/
|
||||||
|
.env
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT * FROM deleted_events WHERE event_id = ? AND pubkey = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "event_id",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pubkey",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kind",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "d_tag",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deleted_at",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "20cd6a8251512848aaf6b0a5ea721da5d37b0d946f7a0e083afaa9e8fb296e3f"
|
||||||
|
}
|
||||||
+13
-7
@@ -6,15 +6,18 @@ pub const DEFAULT_BIND_ADDR: &str = "0.0.0.0:8080";
|
|||||||
pub const DEFAULT_DB_PATH: &str = "nostr.db";
|
pub const DEFAULT_DB_PATH: &str = "nostr.db";
|
||||||
pub const MAX_EVENT_TAGS: u32 = 5000;
|
pub const MAX_EVENT_TAGS: u32 = 5000;
|
||||||
pub const MAX_LIMIT: u64 = 500;
|
pub const MAX_LIMIT: u64 = 500;
|
||||||
pub const DEFAULT_LIMIT: u64 = 10;
|
pub const DEFAULT_LIMIT: u64 = 50;
|
||||||
pub const MAX_FILTERS_PER_REQ: usize = 100;
|
pub const MAX_FILTERS_PER_REQ: usize = 100;
|
||||||
pub const BROADCAST_CHANNEL_SIZE: usize = 100;
|
pub const BROADCAST_CHANNEL_SIZE: usize = 100;
|
||||||
pub const CLIENT_CHANNEL_SIZE: usize = 32;
|
pub const CLIENT_CHANNEL_SIZE: usize = 32;
|
||||||
pub const MAX_SUBSCRIPTIONS: usize = 20;
|
pub const MAX_SUBSCRIPTIONS: usize = 20;
|
||||||
|
pub static RELAY_URL: Lazy<String> =
|
||||||
|
Lazy::new(|| env::var("RELAY_URL").unwrap_or_else(|_| "".to_string()));
|
||||||
|
|
||||||
pub static SERVER_INFO: Lazy<ServerInfo> = Lazy::new(|| ServerInfo {
|
pub static SERVER_INFO: Lazy<ServerInfo> = Lazy::new(|| ServerInfo {
|
||||||
contact: "https://www.moec.top/",
|
contact: env::var("RELAY_CONTACT").unwrap_or_else(|_| "https://www.moec.top/".to_string()),
|
||||||
description: "Powered by laoXong.",
|
description: env::var("RELAY_DESCRIPTION")
|
||||||
|
.unwrap_or_else(|_| "Powered by laoXong.".to_string()),
|
||||||
limitation: Limitation {
|
limitation: Limitation {
|
||||||
max_event_tags: MAX_EVENT_TAGS,
|
max_event_tags: MAX_EVENT_TAGS,
|
||||||
max_event_time_newer_than_now: 900,
|
max_event_time_newer_than_now: 900,
|
||||||
@@ -26,11 +29,14 @@ pub static SERVER_INFO: Lazy<ServerInfo> = Lazy::new(|| ServerInfo {
|
|||||||
max_subscriptions: MAX_SUBSCRIPTIONS as u32, // usize to u32 cast
|
max_subscriptions: MAX_SUBSCRIPTIONS as u32, // usize to u32 cast
|
||||||
min_prefix: 10,
|
min_prefix: 10,
|
||||||
},
|
},
|
||||||
name: "A rust nostr relay by laoXong",
|
name: env::var("RELAY_NAME").unwrap_or_else(|_| "A rust nostr relay by laoXong".to_string()),
|
||||||
pubkey: "63abd4f817e39cca4e6abb6e6cf3e133bb718cf8ec28b38c1645e84d7a6190c6",
|
pubkey: env::var("RELAY_PUBKEY").unwrap_or_else(|_| {
|
||||||
software: "https://git.moe.gift/laoxong/nostr-relay",
|
"63abd4f817e39cca4e6abb6e6cf3e133bb718cf8ec28b38c1645e84d7a6190c6".to_string()
|
||||||
|
}),
|
||||||
|
software: env::var("RELAY_SOFTWARE")
|
||||||
|
.unwrap_or_else(|_| "https://git.moe.gift/laoxong/nostr-relay".to_string()),
|
||||||
supported_nips: vec![1, 2, 5, 9, 11, 42, 50, 65],
|
supported_nips: vec![1, 2, 5, 9, 11, 42, 50, 65],
|
||||||
version: env!("CARGO_PKG_VERSION"), // 从 Cargo.toml 获取版本
|
version: env!("CARGO_PKG_VERSION").to_string(), // 从 Cargo.toml 获取版本
|
||||||
auth_required: env::var("AUTH_REQUIRED")
|
auth_required: env::var("AUTH_REQUIRED")
|
||||||
.unwrap_or_else(|_| "false".to_string())
|
.unwrap_or_else(|_| "false".to_string())
|
||||||
.to_lowercase()
|
.to_lowercase()
|
||||||
|
|||||||
+21
-1
@@ -15,19 +15,39 @@ pub async fn init_database(pool: &SqlitePool) -> Result<()> {
|
|||||||
tags TEXT NOT NULL,
|
tags TEXT NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
sig TEXT NOT NULL,
|
sig TEXT NOT NULL,
|
||||||
|
d_tag TEXT,
|
||||||
indexed_at INTEGER DEFAULT (unixepoch())
|
indexed_at INTEGER DEFAULT (unixepoch())
|
||||||
);
|
);
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
let create_deleted_events_table = r#"
|
||||||
|
CREATE TABLE IF NOT EXISTS deleted_events (
|
||||||
|
event_id TEXT,
|
||||||
|
pubkey TEXT NOT NULL,
|
||||||
|
kind INTEGER,
|
||||||
|
d_tag TEXT,
|
||||||
|
deleted_at INTEGER DEFAULT (unixepoch())
|
||||||
|
);
|
||||||
|
"#;
|
||||||
|
|
||||||
let create_indexes = vec![
|
let create_indexes = vec![
|
||||||
"CREATE INDEX IF NOT EXISTS idx_events_pubkey ON events(pubkey);",
|
"CREATE INDEX IF NOT EXISTS idx_events_pubkey ON events(pubkey);",
|
||||||
"CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);",
|
"CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);",
|
||||||
"CREATE INDEX IF NOT EXISTS idx_events_created_at ON events(created_at);",
|
"CREATE INDEX IF NOT EXISTS idx_events_created_at ON events(created_at);",
|
||||||
"CREATE INDEX IF NOT EXISTS idx_events_pubkey_kind ON events(pubkey, kind);",
|
"CREATE INDEX IF NOT EXISTS idx_events_pubkey_kind ON events(pubkey, kind);",
|
||||||
"CREATE INDEX IF NOT EXISTS idx_events_kind_created_at_desc ON events(kind, created_at DESC);",
|
"CREATE INDEX IF NOT EXISTS idx_events_kind_created_at_desc ON events(kind, created_at DESC);",
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_events_pubkey_kind_d_tag ON events(pubkey, kind, d_tag);",
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_deleted_events_pubkey ON deleted_events(pubkey, event_id);",
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_deleted_events_kind_d_tag ON deleted_events(kind, pubkey, d_tag);",
|
||||||
];
|
];
|
||||||
|
|
||||||
query(create_events_table).execute(pool).await?;
|
query(create_events_table).execute(pool).await?;
|
||||||
|
// 尝试添加 d_tag 列,如果已存在则忽略错误
|
||||||
|
let _ = query("ALTER TABLE events ADD COLUMN d_tag TEXT DEFAULT ''")
|
||||||
|
.execute(pool)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
query(create_deleted_events_table).execute(pool).await?;
|
||||||
|
|
||||||
for index_sql in create_indexes {
|
for index_sql in create_indexes {
|
||||||
query(index_sql).execute(pool).await?;
|
query(index_sql).execute(pool).await?;
|
||||||
@@ -39,7 +59,7 @@ pub async fn init_database(pool: &SqlitePool) -> Result<()> {
|
|||||||
|
|
||||||
// 获取信任账户列表
|
// 获取信任账户列表
|
||||||
pub async fn get_trust_accounts(pool: &SqlitePool) -> Vec<String> {
|
pub async fn get_trust_accounts(pool: &SqlitePool) -> Vec<String> {
|
||||||
let pubkey = SERVER_INFO.pubkey; // 获取服务器公钥
|
let pubkey = &SERVER_INFO.pubkey; // 获取服务器公钥
|
||||||
// 查询最新的 kind 3 事件(联系人列表),获取其中的 p 标签作为信任账户
|
// 查询最新的 kind 3 事件(联系人列表),获取其中的 p 标签作为信任账户
|
||||||
let sql =
|
let sql =
|
||||||
"SELECT tags FROM events WHERE kind = 3 AND pubkey = ? ORDER BY created_at DESC LIMIT 1";
|
"SELECT tags FROM events WHERE kind = 3 AND pubkey = ? ORDER BY created_at DESC LIMIT 1";
|
||||||
|
|||||||
+6
-6
@@ -7,14 +7,14 @@ use uuid::Uuid;
|
|||||||
// 服务器信息结构体(用于 NIP-11)
|
// 服务器信息结构体(用于 NIP-11)
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct ServerInfo {
|
pub struct ServerInfo {
|
||||||
pub contact: &'static str,
|
pub contact: String,
|
||||||
pub description: &'static str,
|
pub description: String,
|
||||||
pub limitation: Limitation,
|
pub limitation: Limitation,
|
||||||
pub name: &'static str,
|
pub name: String,
|
||||||
pub pubkey: &'static str,
|
pub pubkey: String,
|
||||||
pub software: &'static str,
|
pub software: String,
|
||||||
pub supported_nips: Vec<u32>,
|
pub supported_nips: Vec<u32>,
|
||||||
pub version: &'static str,
|
pub version: String,
|
||||||
pub auth_required: bool,
|
pub auth_required: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+225
-35
@@ -7,12 +7,13 @@ use serde_json::json;
|
|||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use std::{
|
use std::{
|
||||||
|
result,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
use tokio::sync::{RwLock, mpsc};
|
use tokio::sync::{RwLock, mpsc};
|
||||||
|
|
||||||
use crate::constants::SERVER_INFO;
|
use crate::constants::{RELAY_URL, SERVER_INFO};
|
||||||
use crate::models::ClientConnection;
|
use crate::models::ClientConnection;
|
||||||
use crate::nostr::NostrEvent;
|
use crate::nostr::NostrEvent;
|
||||||
use crate::nostr::messages::RelayMessage;
|
use crate::nostr::messages::RelayMessage;
|
||||||
@@ -20,7 +21,7 @@ use crate::nostr::messages::RelayMessage;
|
|||||||
pub trait NostrEventExt {
|
pub trait NostrEventExt {
|
||||||
fn serialize_for_id(&self) -> String; // 用于计算事件 ID 的序列化
|
fn serialize_for_id(&self) -> String; // 用于计算事件 ID 的序列化
|
||||||
fn verify(&self) -> bool; // 验证 ID、时间戳、标签数量和签名
|
fn verify(&self) -> bool; // 验证 ID、时间戳、标签数量和签名
|
||||||
async fn save(&self, pool: &SqlitePool) -> Result<(), sqlx::Error>; // 保存事件到数据库
|
async fn save(&self, pool: &SqlitePool) -> Result<(), anyhow::Error>; // 保存事件到数据库
|
||||||
async fn handle_auth_event(
|
async fn handle_auth_event(
|
||||||
&self,
|
&self,
|
||||||
client_conn: &Arc<RwLock<ClientConnection>>,
|
client_conn: &Arc<RwLock<ClientConnection>>,
|
||||||
@@ -89,6 +90,11 @@ impl NostrEventExt for NostrEvent {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 验证只有一个e标签
|
||||||
|
if self.tags.iter().filter(|tag| tag[0] == "e").count() > 1 {
|
||||||
|
debug!("Event has more than one e tag");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// 4. 解析公钥:从十六进制字符串解析 XOnlyPublicKey
|
// 4. 解析公钥:从十六进制字符串解析 XOnlyPublicKey
|
||||||
let pubkey_bytes: Vec<u8> = match hex::decode(&self.pubkey) {
|
let pubkey_bytes: Vec<u8> = match hex::decode(&self.pubkey) {
|
||||||
@@ -174,9 +180,17 @@ impl NostrEventExt for NostrEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存事件到数据库,并处理可替换事件和删除事件
|
// 保存事件到数据库,并处理可替换事件和删除事件
|
||||||
async fn save(&self, pool: &SqlitePool) -> Result<(), sqlx::Error> {
|
async fn save(&self, pool: &SqlitePool) -> Result<(), anyhow::Error> {
|
||||||
let tags_json = serde_json::to_string(&self.tags).unwrap();
|
let tags_json = serde_json::to_string(&self.tags).unwrap();
|
||||||
|
|
||||||
|
// 提取 d_tag
|
||||||
|
let d_tag = self
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.find(|tag| tag.len() >= 2 && tag[0] == "d")
|
||||||
|
.map(|tag| tag[1].clone())
|
||||||
|
.unwrap_or_default(); // 默认为空字符串
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
// NIP-09 事件删除 (Event Deletion)
|
// NIP-09 事件删除 (Event Deletion)
|
||||||
5 => {
|
5 => {
|
||||||
@@ -188,65 +202,228 @@ impl NostrEventExt for NostrEvent {
|
|||||||
"Attempting to delete event with ID: {} by request of pubkey {}",
|
"Attempting to delete event with ID: {} by request of pubkey {}",
|
||||||
event_id_to_delete, self.pubkey
|
event_id_to_delete, self.pubkey
|
||||||
);
|
);
|
||||||
let sql = "DELETE FROM events WHERE id = ? AND pubkey = ?";
|
let result =
|
||||||
let result = sqlx::query(sql)
|
sqlx::query("DELETE FROM events WHERE id = ? AND pubkey = ?")
|
||||||
.bind(event_id_to_delete)
|
.bind(event_id_to_delete)
|
||||||
.bind(&self.pubkey)
|
.bind(&self.pubkey)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await?;
|
.await?;
|
||||||
if result.rows_affected() > 0 {
|
if result.rows_affected() > 0 {
|
||||||
info!(
|
info!(
|
||||||
"Deleted event {} by pubkey (kind 5): {}",
|
"Deleted event {} by pubkey (kind 5): {}",
|
||||||
event_id_to_delete, self.pubkey
|
event_id_to_delete, self.pubkey
|
||||||
);
|
);
|
||||||
|
let result = sqlx::query(
|
||||||
|
"INSERT INTO deleted_events (event_id, pubkey, deleted_at) VALUES (?, ?, ?)",
|
||||||
|
)
|
||||||
|
.bind(event_id_to_delete)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
if result.rows_affected() > 0 {
|
||||||
|
info!(
|
||||||
|
"Deleted event {} by pubkey (kind 5): {}",
|
||||||
|
event_id_to_delete, self.pubkey
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Could not delete event {} for pubkey {}.",
|
||||||
|
event_id_to_delete, self.pubkey
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"Could not delete event {} for pubkey {}. It might not exist or unauthorized.",
|
"Could not delete event {} for pubkey {}.",
|
||||||
event_id_to_delete, self.pubkey
|
event_id_to_delete, self.pubkey
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if tag.get(0).map(|s| s == "a").unwrap_or(false) {
|
||||||
|
let d_tag_value = &tag[1];
|
||||||
|
let tag_d_vector = d_tag_value.splitn(3, ':').collect::<Vec<&str>>();
|
||||||
|
if tag_d_vector.len() != 3 {
|
||||||
|
debug!("Invalid a-tag format: {}", d_tag_value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let result = sqlx::query(
|
||||||
|
"DELETE FROM events WHERE kind = ? AND pubkey = ? AND d_tag = ? AND created_at <= ?;",
|
||||||
|
)
|
||||||
|
.bind(tag_d_vector[0].parse::<i64>().unwrap())
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(tag_d_vector[2])
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
if result.rows_affected() > 0 {
|
||||||
|
info!(
|
||||||
|
"Deleted event {:?} by pubkey (kind 5): {}",
|
||||||
|
tag_d_vector, self.pubkey
|
||||||
|
);
|
||||||
|
let result = sqlx::query(
|
||||||
|
"INSERT INTO deleted_events (kind, pubkey, d_tag, deleted_at) VALUES (?, ?, ?, ?)",
|
||||||
|
)
|
||||||
|
.bind(tag_d_vector[0])
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(tag_d_vector[2])
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
if result.rows_affected() > 0 {
|
||||||
|
info!(
|
||||||
|
"Deleted event {:?} by pubkey (kind 5): {}",
|
||||||
|
tag_d_vector, self.pubkey
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Could not delete event {:?} for pubkey {}.",
|
||||||
|
tag_d_vector, self.pubkey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Could not delete event {:?} for pubkey {}.",
|
||||||
|
tag_d_vector, self.pubkey
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NIP-01 可替换事件 (Replaceable Events): kind 0 (Metadata), kind 3 (Contact List)
|
// NIP-01 可替换事件 (Replaceable Events): kind 0 (Metadata), kind 3 (Contact List)
|
||||||
// NIP-16 可替换事件 (Replaceable Events): kinds 10000 to 19999
|
// NIP-16 可替换事件 (Replaceable Events): kinds 10000 to 19999
|
||||||
// NIP-33 参数化可替换事件 (Parameterized Replaceable Events): kinds 30000 to 39999
|
// 对于这些可替换事件,新事件会替换掉旧事件。
|
||||||
// 对于所有这些可替换事件,新事件会替换掉旧事件。
|
0 | 3 | _ if (self.kind >= 10000 && self.kind < 20000) => {
|
||||||
// 这里的简化处理是,对于所有可替换事件,都基于 (pubkey, kind) 来删除旧事件。
|
|
||||||
// 对于 NIP-33 事件,严格来说还需要匹配 'd' tag。
|
|
||||||
// 但考虑到数据库操作的复杂性以及原始代码的实现,这里仍采用 (pubkey, kind) 的方式。
|
|
||||||
// 一个更健壮的 NIP-33 实现可能需要单独的字段来存储 'd' tag 或更复杂的 SQL。
|
|
||||||
0 | 3 | _
|
|
||||||
if (self.kind >= 10000 && self.kind < 20000)
|
|
||||||
|| (self.kind >= 30000 && self.kind < 40000) =>
|
|
||||||
{
|
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting to delete previous replaceable event for pubkey: {}, kind: {}",
|
"Attempting to delete previous replaceable event for pubkey: {}, kind: {}",
|
||||||
self.pubkey, self.kind
|
self.pubkey, self.kind
|
||||||
);
|
);
|
||||||
let sql = "DELETE FROM events WHERE pubkey = ? AND kind = ?";
|
let is_deleted = sqlx::query(
|
||||||
sqlx::query(sql)
|
"SELECT 1 FROM deleted_events WHERE pubkey = ? AND kind = ? AND deleted_at > ?",
|
||||||
|
)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.kind)
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if is_deleted.is_some() {
|
||||||
|
return Err(anyhow::anyhow!("Event already deleted"));
|
||||||
|
}
|
||||||
|
let sql = "SELECT created_at FROM events WHERE pubkey = ? AND kind = ? ORDER BY created_at DESC LIMIT 1";
|
||||||
|
let created_at: Option<u64> = sqlx::query_scalar(sql)
|
||||||
.bind(&self.pubkey)
|
.bind(&self.pubkey)
|
||||||
.bind(self.kind)
|
.bind(self.kind)
|
||||||
.execute(pool)
|
.fetch_optional(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(prev_created_at) = created_at {
|
||||||
|
if prev_created_at <= self.created_at {
|
||||||
|
let sql = "DELETE FROM events WHERE pubkey = ? AND kind = ?";
|
||||||
|
sqlx::query(sql)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.kind)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
debug!(
|
||||||
|
"Deleted previous replaceable event for pubkey: {}, kind: {}",
|
||||||
|
self.pubkey, self.kind
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Previous replaceable event for pubkey: {}, kind: {} is not older than current event",
|
||||||
|
self.pubkey, self.kind
|
||||||
|
);
|
||||||
|
// 如果新事件不比旧事件新,则报错
|
||||||
|
anyhow::bail!("Event is not newer than existing replaceable event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NIP-33 参数化可替换事件 (Parameterized Replaceable Events): kinds 30000 to 39999
|
||||||
|
_ if (self.kind >= 30000 && self.kind < 40000) => {
|
||||||
|
debug!(
|
||||||
|
"Attempting to delete previous parameterized replaceable event for pubkey: {}, kind: {}, d_tag: {}",
|
||||||
|
self.pubkey, self.kind, d_tag
|
||||||
|
);
|
||||||
|
let result = sqlx::query(
|
||||||
|
"SELECT * FROM deleted_events WHERE pubkey = ? AND kind = ? AND d_tag = ? AND deleted_at > ?",
|
||||||
|
)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.kind)
|
||||||
|
.bind(&d_tag)
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await?;
|
||||||
|
if let Some(_) = result {
|
||||||
|
debug!(
|
||||||
|
"Event {} already deleted by pubkey {}",
|
||||||
|
self.id, self.pubkey
|
||||||
|
);
|
||||||
|
return Err(anyhow::anyhow!("Event already deleted"));
|
||||||
|
}
|
||||||
|
let sql = "SELECT created_at FROM events WHERE pubkey = ? AND kind = ? AND d_tag = ? ORDER BY created_at DESC LIMIT 1";
|
||||||
|
let created_at: Option<u64> = sqlx::query_scalar(sql)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.kind)
|
||||||
|
.bind(&d_tag)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(prev_created_at) = created_at {
|
||||||
|
if prev_created_at < self.created_at {
|
||||||
|
let sql = "DELETE FROM events WHERE pubkey = ? AND kind = ? AND d_tag = ?";
|
||||||
|
sqlx::query(sql)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.kind)
|
||||||
|
.bind(&d_tag)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
debug!(
|
||||||
|
"Deleted previous parameterized replaceable event for pubkey: {}, kind: {}, d_tag: {}",
|
||||||
|
self.pubkey, self.kind, d_tag
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Previous parameterized replaceable event for pubkey: {}, kind: {}, d_tag: {} is not older than current event",
|
||||||
|
self.pubkey, self.kind, d_tag
|
||||||
|
);
|
||||||
|
anyhow::bail!(
|
||||||
|
"Event is not newer than existing parameterized replaceable event"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => { /* 非可替换事件不需要在插入前删除 */ }
|
_ => { /* 非可替换事件不需要在插入前删除 */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入新事件
|
// 插入新事件
|
||||||
let sql = "INSERT OR IGNORE INTO events (id, pubkey, created_at, kind, tags, content, sig) VALUES (?, ?, ?, ?, ?, ?, ?);";
|
if !(self.kind >= 20000 && self.kind < 30000) {
|
||||||
sqlx::query(sql)
|
let result =
|
||||||
.bind(&self.id)
|
sqlx::query("SELECT * FROM deleted_events WHERE event_id = ? AND pubkey = ?")
|
||||||
.bind(&self.pubkey)
|
.bind(&self.id)
|
||||||
.bind(self.created_at as i64)
|
.bind(&self.pubkey)
|
||||||
.bind(self.kind)
|
.fetch_optional(pool)
|
||||||
.bind(tags_json)
|
.await?;
|
||||||
.bind(&self.content)
|
if let Some(_) = result {
|
||||||
.bind(&self.sig)
|
debug!(
|
||||||
.execute(pool)
|
"Event {} already deleted by pubkey {}",
|
||||||
.await?;
|
self.id, self.pubkey
|
||||||
|
);
|
||||||
|
return Err(anyhow::anyhow!("Event already deleted"));
|
||||||
|
}
|
||||||
|
let sql = "INSERT OR IGNORE INTO events (id, pubkey, created_at, kind, tags, content, sig, d_tag) VALUES (?, ?, ?, ?, ?, ?, ?, ?);";
|
||||||
|
sqlx::query(sql)
|
||||||
|
.bind(&self.id)
|
||||||
|
.bind(&self.pubkey)
|
||||||
|
.bind(self.created_at as i64)
|
||||||
|
.bind(self.kind)
|
||||||
|
.bind(tags_json)
|
||||||
|
.bind(&self.content)
|
||||||
|
.bind(&self.sig)
|
||||||
|
.bind(&d_tag)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,10 +481,23 @@ impl NostrEventExt for NostrEvent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let relay_url = match relay_url {
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
let _ = RelayMessage::send_notice(
|
||||||
|
"AUTH event missing relay tag".to_string(),
|
||||||
|
to_client_msg_tx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
// 4. 验证 challenge 是否匹配客户端连接 ID,并且未过期
|
// 4. 验证 challenge 是否匹配客户端连接 ID,并且未过期
|
||||||
let is_valid_challenge = {
|
let is_valid_challenge = {
|
||||||
let conn = client_conn.read().await;
|
let conn = client_conn.read().await;
|
||||||
if conn.id.to_string() == *challenge {
|
if relay_url.as_str() != RELAY_URL.as_str() {
|
||||||
|
false
|
||||||
|
} else if conn.id.to_string() == *challenge {
|
||||||
// 检查 challenge 是否在 15 分钟内有效 (可配置)
|
// 检查 challenge 是否在 15 分钟内有效 (可配置)
|
||||||
let connected_at_duration = UNIX_EPOCH + Duration::from_secs(conn.connected_at);
|
let connected_at_duration = UNIX_EPOCH + Duration::from_secs(conn.connected_at);
|
||||||
match SystemTime::now().duration_since(connected_at_duration) {
|
match SystemTime::now().duration_since(connected_at_duration) {
|
||||||
|
|||||||
Reference in New Issue
Block a user