feat: setup api

This commit is contained in:
kieran 2025-01-13 14:51:39 +00:00
parent 0202a7da5f
commit 20c9d107b7
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
3 changed files with 89 additions and 20 deletions

View File

@ -40,8 +40,8 @@ overseer:
zap-stream: zap-stream:
cost: 16 cost: 16
nsec: "nsec1wya428srvpu96n4h78gualaj7wqw4ecgatgja8d5ytdqrxw56r2se440y4" nsec: "nsec1wya428srvpu96n4h78gualaj7wqw4ecgatgja8d5ytdqrxw56r2se440y4"
#blossom: blossom:
# - "http://localhost:8881" - "http://localhost:8881"
relays: relays:
- "ws://localhost:7766" - "ws://localhost:7766"
database: "mysql://root:root@localhost:3368/zap_stream?max_connections=2" database: "mysql://root:root@localhost:3368/zap_stream?max_connections=2"

View File

@ -6,6 +6,7 @@ use http_body_util::{BodyExt, Full, StreamBody};
use hyper::body::{Frame, Incoming}; use hyper::body::{Frame, Incoming};
use hyper::service::Service; use hyper::service::Service;
use hyper::{Method, Request, Response}; use hyper::{Method, Request, Response};
use log::error;
use std::future::Future; use std::future::Future;
use std::path::PathBuf; use std::path::PathBuf;
use std::pin::Pin; use std::pin::Pin;
@ -83,7 +84,10 @@ impl Service<Request<Incoming>> for HttpServer {
Box::pin(async move { Box::pin(async move {
match overseer.api(req).await { match overseer.api(req).await {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err(e) => Err(e), Err(e) => {
error!("{}", e);
Ok(Response::builder().status(500).body(BoxBody::default())?)
}
} }
}) })
} }

View File

@ -8,6 +8,8 @@ use crate::settings::LndSettings;
use crate::variant::StreamMapping; use crate::variant::StreamMapping;
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use async_trait::async_trait; use async_trait::async_trait;
use base64::alphabet::STANDARD;
use base64::Engine;
use bytes::Bytes; use bytes::Bytes;
use chrono::Utc; use chrono::Utc;
use fedimint_tonic_lnd::verrpc::VersionRequest; use fedimint_tonic_lnd::verrpc::VersionRequest;
@ -23,6 +25,7 @@ use log::{error, info, warn};
use nostr_sdk::bitcoin::PrivateKey; use nostr_sdk::bitcoin::PrivateKey;
use nostr_sdk::prelude::Coordinate; use nostr_sdk::prelude::Coordinate;
use nostr_sdk::{Client, Event, EventBuilder, JsonUtil, Keys, Kind, Tag, ToBech32}; use nostr_sdk::{Client, Event, EventBuilder, JsonUtil, Keys, Kind, Tag, ToBech32};
use serde::Serialize;
use std::collections::HashSet; use std::collections::HashSet;
use std::env::temp_dir; use std::env::temp_dir;
use std::fs::create_dir_all; use std::fs::create_dir_all;
@ -35,7 +38,7 @@ use uuid::Uuid;
use zap_stream_db::sqlx::Encode; use zap_stream_db::sqlx::Encode;
use zap_stream_db::{UserStream, UserStreamState, ZapStreamDb}; use zap_stream_db::{UserStream, UserStreamState, ZapStreamDb};
const STREAM_EVENT_KIND: u16 = 30_311; const STREAM_EVENT_KIND: u16 = 30_313;
/// zap.stream NIP-53 overseer /// zap.stream NIP-53 overseer
pub struct ZapStreamOverseer { pub struct ZapStreamOverseer {
@ -184,21 +187,19 @@ impl ZapStreamOverseer {
} }
async fn publish_stream_event(&self, stream: &UserStream, pubkey: &Vec<u8>) -> Result<Event> { async fn publish_stream_event(&self, stream: &UserStream, pubkey: &Vec<u8>) -> Result<Event> {
let mut extra_tags = vec![ let extra_tags = vec![
Tag::parse(&["p", hex::encode(pubkey).as_str(), "", "host"])?, Tag::parse(&["p", hex::encode(pubkey).as_str(), "", "host"])?,
Tag::parse(&[ Tag::parse(&[
"streaming", "streaming",
self.map_to_public_url(stream, "live.m3u8")?.as_str(), self.map_to_stream_public_url(stream, "live.m3u8")?.as_str(),
])?, ])?,
Tag::parse(&[ Tag::parse(&[
"image", "image",
self.map_to_public_url(stream, "thumb.webp")?.as_str(), self.map_to_stream_public_url(stream, "thumb.webp")?
.as_str(),
])?, ])?,
Tag::parse(&["service", self.map_to_public_url("api/v1")?.as_str()])?,
]; ];
// flag NIP94 streaming when using blossom servers
if self.blossom_servers.len() > 0 {
extra_tags.push(Tag::parse(&["streaming", "nip94"])?);
}
let ev = self let ev = self
.stream_to_event_builder(stream)? .stream_to_event_builder(stream)?
.add_tags(extra_tags) .add_tags(extra_tags)
@ -207,29 +208,93 @@ impl ZapStreamOverseer {
Ok(ev) Ok(ev)
} }
fn map_to_public_url<'a>( fn map_to_stream_public_url(
&self, &self,
stream: &UserStream, stream: &UserStream,
path: impl Into<&'a str>, path: &str,
) -> Result<String> { ) -> Result<String> {
self.map_to_public_url(&format!("{}/{}", stream.id, path))
}
fn map_to_public_url(&self, path: &str) -> Result<String> {
let u: Url = self.public_url.parse()?; let u: Url = self.public_url.parse()?;
Ok(u.join(&format!("/{}/", stream.id))? Ok(u.join(path)?.to_string())
.join(path.into())? }
.to_string())
fn check_nip98_auth(&self, req: Request<Incoming>) -> Result<()> {
let auth = if let Some(a) = req.headers().get("authorization") {
a.to_str()?
} else {
bail!("Authorization header missing");
};
if !auth.starts_with("Nostr ") {
bail!("Invalid authorization scheme");
}
let json = String::from_utf8(
base64::engine::general_purpose::STANDARD.decode(auth[6..].as_bytes())?,
)?;
info!("{}", json);
Ok(())
} }
} }
#[derive(Serialize)]
struct Endpoint {}
#[derive(Serialize)]
struct AccountInfo {
pub endpoints: Vec<Endpoint>,
pub event: Event,
pub balance: u64,
}
#[async_trait] #[async_trait]
impl Overseer for ZapStreamOverseer { impl Overseer for ZapStreamOverseer {
async fn api(&self, req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, anyhow::Error>>> { async fn api(&self, req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, anyhow::Error>>> {
let base = Response::builder()
.header("server", "zap-stream-core")
.header("access-control-allow-origin", "*")
.header("access-control-allow-headers", "*")
.header("access-control-allow-methods", "HEAD, GET");
Ok(match (req.method(), req.uri().path()) { Ok(match (req.method(), req.uri().path()) {
(&Method::GET, "/api/v1/account") => { (&Method::GET, "/api/v1/account") => {
self.check_nip98_auth(req)?;
base.body(Default::default())?
}
(&Method::PATCH, "/api/v1/account") => {
bail!("Not implemented") bail!("Not implemented")
} }
_ => Response::builder() (&Method::GET, "/api/v1/topup") => {
.header("server", "zap-stream-core") bail!("Not implemented")
.status(404) }
.body(Full::from("").map_err(anyhow::Error::new).boxed())?, (&Method::PATCH, "/api/v1/event") => {
bail!("Not implemented")
}
(&Method::POST, "/api/v1/withdraw") => {
bail!("Not implemented")
}
(&Method::POST, "/api/v1/account/forward") => {
bail!("Not implemented")
}
(&Method::DELETE, "/api/v1/account/forward/<id>") => {
bail!("Not implemented")
}
(&Method::GET, "/api/v1/account/history") => {
bail!("Not implemented")
}
(&Method::GET, "/api/v1/account/keys") => {
bail!("Not implemented")
}
_ => {
if req.method() == Method::OPTIONS {
base.body(Default::default())?
} else {
base.status(404).body(Default::default())?
}
}
}) })
} }