feat: setup api
This commit is contained in:
parent
0202a7da5f
commit
20c9d107b7
@ -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"
|
||||||
|
@ -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())?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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())?
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user