Publish metadata on startup
This commit is contained in:
+10
-1
@@ -37,6 +37,9 @@ Environment variables:
|
|||||||
| `RELAY_DOMAIN` | Relay base domain for subdomains | `spaces.coracle.social` |
|
| `RELAY_DOMAIN` | Relay base domain for subdomains | `spaces.coracle.social` |
|
||||||
| `NWC_URL` | Platform NWC URL for invoice generation | _required for billing_ |
|
| `NWC_URL` | Platform NWC URL for invoice generation | _required for billing_ |
|
||||||
| `NOSTR_INDEXER_RELAYS` | Comma-separated relays to fetch kind `10050` DM relays | _required for notifications_ |
|
| `NOSTR_INDEXER_RELAYS` | Comma-separated relays to fetch kind `10050` DM relays | _required for notifications_ |
|
||||||
|
| `PLATFORM_NAME` | Platform display name for kind `0` metadata | _optional_ |
|
||||||
|
| `PLATFORM_DESCRIPTION` | Platform description for kind `0` metadata | _optional_ |
|
||||||
|
| `PLATFORM_MESSAGING_RELAYS` | Comma-separated relays published in kind `10050` | _optional_ |
|
||||||
|
|
||||||
The database directory is created automatically if it doesn’t exist.
|
The database directory is created automatically if it doesn’t exist.
|
||||||
|
|
||||||
@@ -82,6 +85,13 @@ The backend runs an in-process billing loop that:
|
|||||||
- Sends NIP-17 DMs with invoices when recurring is off
|
- Sends NIP-17 DMs with invoices when recurring is off
|
||||||
- Sends NIP-17 DMs on successful payment when recurring is on
|
- Sends NIP-17 DMs on successful payment when recurring is on
|
||||||
|
|
||||||
|
On startup, the backend publishes:
|
||||||
|
|
||||||
|
- Kind `0` metadata (name/description)
|
||||||
|
- Kind `10050` relay list for DMs
|
||||||
|
|
||||||
|
These are published to the relays listed in `NOSTR_INDEXER_RELAYS`.
|
||||||
|
|
||||||
## API Routes
|
## API Routes
|
||||||
|
|
||||||
Tenant routes (all require NIP-98 auth; pubkey is inferred from the token):
|
Tenant routes (all require NIP-98 auth; pubkey is inferred from the token):
|
||||||
@@ -108,4 +118,3 @@ Admin routes (all require NIP-98 auth; pubkey must be in `HOSTING_ADMIN_PUBKEYS`
|
|||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Add invoice generation and billing jobs
|
- Add invoice generation and billing jobs
|
||||||
- On start, publish kind 0, 10002, 10050 to indexer relays based on env vars
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ pub struct Config {
|
|||||||
pub relay_domain: String,
|
pub relay_domain: String,
|
||||||
pub platform_nwc_url: String,
|
pub platform_nwc_url: String,
|
||||||
pub indexer_relays: Vec<String>,
|
pub indexer_relays: Vec<String>,
|
||||||
|
pub platform_name: String,
|
||||||
|
pub platform_description: String,
|
||||||
|
pub platform_messaging_relays: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -40,6 +43,14 @@ impl Config {
|
|||||||
.map(|v| v.trim().to_string())
|
.map(|v| v.trim().to_string())
|
||||||
.filter(|v| !v.is_empty())
|
.filter(|v| !v.is_empty())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let platform_name = env::var("PLATFORM_NAME").unwrap_or_default();
|
||||||
|
let platform_description = env::var("PLATFORM_DESCRIPTION").unwrap_or_default();
|
||||||
|
let platform_messaging_relays = env::var("PLATFORM_MESSAGING_RELAYS")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.split(',')
|
||||||
|
.map(|v| v.trim().to_string())
|
||||||
|
.filter(|v| !v.is_empty())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
database_url,
|
database_url,
|
||||||
@@ -51,6 +62,9 @@ impl Config {
|
|||||||
relay_domain,
|
relay_domain,
|
||||||
platform_nwc_url,
|
platform_nwc_url,
|
||||||
indexer_relays,
|
indexer_relays,
|
||||||
|
platform_name,
|
||||||
|
platform_description,
|
||||||
|
platform_messaging_relays,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod config;
|
|||||||
mod db;
|
mod db;
|
||||||
mod models;
|
mod models;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
mod platform;
|
||||||
mod provisioning;
|
mod provisioning;
|
||||||
mod repo;
|
mod repo;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ use crate::config::Config;
|
|||||||
use crate::db::init_pool;
|
use crate::db::init_pool;
|
||||||
use crate::billing::BillingService;
|
use crate::billing::BillingService;
|
||||||
use crate::notifications::Nip17Notifier;
|
use crate::notifications::Nip17Notifier;
|
||||||
|
use crate::platform::publish_platform_identity;
|
||||||
use crate::provisioning::Provisioner;
|
use crate::provisioning::Provisioner;
|
||||||
use crate::repo::Repo;
|
use crate::repo::Repo;
|
||||||
|
|
||||||
@@ -35,6 +37,14 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let pool = init_pool(&config.database_url).await?;
|
let pool = init_pool(&config.database_url).await?;
|
||||||
let repo = Repo::new(pool);
|
let repo = Repo::new(pool);
|
||||||
|
publish_platform_identity(
|
||||||
|
&config.platform_secret,
|
||||||
|
&config.indexer_relays,
|
||||||
|
&config.platform_name,
|
||||||
|
&config.platform_description,
|
||||||
|
&config.platform_messaging_relays,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let notifier = Nip17Notifier::new(config.platform_secret.clone(), config.indexer_relays.clone()).await?;
|
let notifier = Nip17Notifier::new(config.platform_secret.clone(), config.indexer_relays.clone()).await?;
|
||||||
let billing = BillingService::new(
|
let billing = BillingService::new(
|
||||||
repo.clone(),
|
repo.clone(),
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use nostr_sdk::prelude::*;
|
||||||
|
|
||||||
|
pub async fn publish_platform_identity(
|
||||||
|
platform_secret: &str,
|
||||||
|
indexer_relays: &[String],
|
||||||
|
name: &str,
|
||||||
|
description: &str,
|
||||||
|
messaging_relays: &[String],
|
||||||
|
) -> Result<()> {
|
||||||
|
if indexer_relays.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if platform_secret.trim().is_empty() {
|
||||||
|
return Err(anyhow!("PLATFORM_SECRET is required for platform identity"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = Keys::parse(platform_secret)?;
|
||||||
|
let client = Client::new(keys);
|
||||||
|
|
||||||
|
for relay in indexer_relays {
|
||||||
|
client.add_relay(relay).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.connect().await;
|
||||||
|
|
||||||
|
let mut metadata = Metadata::new();
|
||||||
|
if !name.is_empty() {
|
||||||
|
metadata = metadata.name(name);
|
||||||
|
}
|
||||||
|
if !description.is_empty() {
|
||||||
|
metadata = metadata.about(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata_builder = EventBuilder::metadata(&metadata);
|
||||||
|
client.send_event_builder(metadata_builder).await?;
|
||||||
|
|
||||||
|
if messaging_relays.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tags = Vec::new();
|
||||||
|
for relay in messaging_relays {
|
||||||
|
let tag = Tag::parse(["relay", relay.as_str()])?;
|
||||||
|
tags.push(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
let relay_builder = EventBuilder::new(Kind::Custom(10050), "").tags(tags);
|
||||||
|
client.send_event_builder(relay_builder).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user