forked from mirrors/relay
Start work on admin API
This commit is contained in:
parent
08374d0382
commit
fe844a807f
9 changed files with 471 additions and 4 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -291,6 +291,7 @@ dependencies = [
|
|||
"awc",
|
||||
"background-jobs",
|
||||
"base64",
|
||||
"bcrypt",
|
||||
"clap",
|
||||
"config",
|
||||
"console-subscriber",
|
||||
|
@ -544,6 +545,18 @@ version = "1.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e7c93a3fb23b2fdde989b2c9ec4dd153063ec81f408507f84c090cd91c6641"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blowfish",
|
||||
"getrandom",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -559,6 +572,16 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blowfish"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
|
@ -614,6 +637,16 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.26"
|
||||
|
@ -1313,6 +1346,15 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
|
|
@ -29,6 +29,7 @@ activitystreams = "0.7.0-alpha.19"
|
|||
activitystreams-ext = "0.1.0-alpha.2"
|
||||
ammonia = "3.1.0"
|
||||
awc = { version = "3.0.0", default-features = false, features = ["rustls"] }
|
||||
bcrypt = "0.13"
|
||||
base64 = "0.13"
|
||||
clap = { version = "4.0.0", features = ["derive"] }
|
||||
config = "0.13.0"
|
||||
|
|
24
src/admin.rs
Normal file
24
src/admin.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use activitystreams::iri_string::types::IriString;
|
||||
|
||||
pub mod client;
|
||||
pub mod routes;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub(crate) struct Domains {
|
||||
domains: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub(crate) struct AllowedDomains {
|
||||
allowed_domains: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub(crate) struct BlockedDomains {
|
||||
blocked_domains: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub(crate) struct ConnectedActors {
|
||||
connected_actors: Vec<IriString>,
|
||||
}
|
87
src/admin/client.rs
Normal file
87
src/admin/client.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use crate::{
|
||||
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
||||
config::{AdminUrlKind, Config},
|
||||
error::{Error, ErrorKind},
|
||||
};
|
||||
use awc::Client;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
pub(crate) async fn allow(
|
||||
client: &Client,
|
||||
config: &Config,
|
||||
domains: Vec<String>,
|
||||
) -> Result<(), Error> {
|
||||
post_domains(client, config, domains, AdminUrlKind::Allow).await
|
||||
}
|
||||
|
||||
pub(crate) async fn block(
|
||||
client: &Client,
|
||||
config: &Config,
|
||||
domains: Vec<String>,
|
||||
) -> Result<(), Error> {
|
||||
post_domains(client, config, domains, AdminUrlKind::Block).await
|
||||
}
|
||||
|
||||
pub(crate) async fn allowed(client: &Client, config: &Config) -> Result<AllowedDomains, Error> {
|
||||
get_results(client, config, AdminUrlKind::Allowed).await
|
||||
}
|
||||
|
||||
pub(crate) async fn blocked(client: &Client, config: &Config) -> Result<BlockedDomains, Error> {
|
||||
get_results(client, config, AdminUrlKind::Blocked).await
|
||||
}
|
||||
|
||||
pub(crate) async fn connected(client: &Client, config: &Config) -> Result<ConnectedActors, Error> {
|
||||
get_results(client, config, AdminUrlKind::Connected).await
|
||||
}
|
||||
|
||||
async fn get_results<T: DeserializeOwned>(
|
||||
client: &Client,
|
||||
config: &Config,
|
||||
url_kind: AdminUrlKind,
|
||||
) -> Result<T, Error> {
|
||||
let x_api_token = config.x_api_token().ok_or(ErrorKind::MissingApiToken)?;
|
||||
|
||||
let iri = config.generate_admin_url(url_kind);
|
||||
|
||||
let mut res = client
|
||||
.get(iri.as_str())
|
||||
.insert_header(x_api_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| ErrorKind::SendRequest(iri.to_string(), e.to_string()))?;
|
||||
|
||||
if !res.status().is_success() {
|
||||
return Err(ErrorKind::Status(iri.to_string(), res.status()).into());
|
||||
}
|
||||
|
||||
let t = res
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| ErrorKind::ReceiveResponse(iri.to_string(), e.to_string()))?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
async fn post_domains(
|
||||
client: &Client,
|
||||
config: &Config,
|
||||
domains: Vec<String>,
|
||||
url_kind: AdminUrlKind,
|
||||
) -> Result<(), Error> {
|
||||
let x_api_token = config.x_api_token().ok_or(ErrorKind::MissingApiToken)?;
|
||||
|
||||
let iri = config.generate_admin_url(url_kind);
|
||||
|
||||
let res = client
|
||||
.post(iri.as_str())
|
||||
.insert_header(x_api_token)
|
||||
.send_json(&Domains { domains })
|
||||
.await
|
||||
.map_err(|e| ErrorKind::SendRequest(iri.to_string(), e.to_string()))?;
|
||||
|
||||
if !res.status().is_success() {
|
||||
tracing::warn!("Failed to allow domains");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
42
src/admin/routes.rs
Normal file
42
src/admin/routes.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::{
|
||||
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
||||
error::Error,
|
||||
extractors::Admin,
|
||||
};
|
||||
use actix_web::{web::Json, HttpResponse};
|
||||
|
||||
pub(crate) async fn allow(
|
||||
admin: Admin,
|
||||
Json(Domains { domains }): Json<Domains>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
admin.db_ref().add_allows(domains).await?;
|
||||
|
||||
Ok(HttpResponse::NoContent().finish())
|
||||
}
|
||||
|
||||
pub(crate) async fn block(
|
||||
admin: Admin,
|
||||
Json(Domains { domains }): Json<Domains>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
admin.db_ref().add_blocks(domains).await?;
|
||||
|
||||
Ok(HttpResponse::NoContent().finish())
|
||||
}
|
||||
|
||||
pub(crate) async fn allowed(admin: Admin) -> Result<HttpResponse, Error> {
|
||||
let allowed_domains = admin.db_ref().allowed_domains().await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(AllowedDomains { allowed_domains }))
|
||||
}
|
||||
|
||||
pub(crate) async fn blocked(admin: Admin) -> Result<HttpResponse, Error> {
|
||||
let blocked_domains = admin.db_ref().blocks().await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(BlockedDomains { blocked_domains }))
|
||||
}
|
||||
|
||||
pub(crate) async fn connected(admin: Admin) -> Result<HttpResponse, Error> {
|
||||
let connected_actors = admin.db_ref().connected_ids().await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ConnectedActors { connected_actors }))
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
data::{ActorCache, State},
|
||||
error::Error,
|
||||
extractors::{AdminConfig, XApiToken},
|
||||
middleware::MyVerify,
|
||||
requests::Requests,
|
||||
};
|
||||
|
@ -32,6 +33,7 @@ pub(crate) struct ParsedConfig {
|
|||
opentelemetry_url: Option<IriString>,
|
||||
telegram_token: Option<String>,
|
||||
telegram_admin_handle: Option<String>,
|
||||
api_token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -49,6 +51,7 @@ pub struct Config {
|
|||
opentelemetry_url: Option<IriString>,
|
||||
telegram_token: Option<String>,
|
||||
telegram_admin_handle: Option<String>,
|
||||
api_token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -65,6 +68,15 @@ pub enum UrlKind {
|
|||
Outbox,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AdminUrlKind {
|
||||
Allow,
|
||||
Block,
|
||||
Allowed,
|
||||
Blocked,
|
||||
Connected,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Config {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Config")
|
||||
|
@ -84,6 +96,7 @@ impl std::fmt::Debug for Config {
|
|||
)
|
||||
.field("telegram_token", &"[redacted]")
|
||||
.field("telegram_admin_handle", &self.telegram_admin_handle)
|
||||
.field("api_token", &"[redacted]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +106,7 @@ impl Config {
|
|||
let config = config::Config::builder()
|
||||
.set_default("hostname", "localhost:8080")?
|
||||
.set_default("addr", "127.0.0.1")?
|
||||
.set_default::<_, u64>("port", 8080)?
|
||||
.set_default("port", 8080u64)?
|
||||
.set_default("debug", true)?
|
||||
.set_default("restricted_mode", false)?
|
||||
.set_default("validate_signatures", false)?
|
||||
|
@ -104,6 +117,7 @@ impl Config {
|
|||
.set_default("opentelemetry_url", None as Option<&str>)?
|
||||
.set_default("telegram_token", None as Option<&str>)?
|
||||
.set_default("telegram_admin_handle", None as Option<&str>)?
|
||||
.set_default("api_token", None as Option<&str>)?
|
||||
.add_source(Environment::default())
|
||||
.build()?;
|
||||
|
||||
|
@ -126,6 +140,7 @@ impl Config {
|
|||
opentelemetry_url: config.opentelemetry_url,
|
||||
telegram_token: config.telegram_token,
|
||||
telegram_admin_handle: config.telegram_admin_handle,
|
||||
api_token: config.api_token,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -158,6 +173,24 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn x_api_token(&self) -> Option<XApiToken> {
|
||||
self.api_token.clone().map(XApiToken::new)
|
||||
}
|
||||
|
||||
pub(crate) fn admin_config(&self) -> Option<actix_web::web::Data<AdminConfig>> {
|
||||
if let Some(api_token) = &self.api_token {
|
||||
match AdminConfig::build(api_token) {
|
||||
Ok(conf) => Some(actix_web::web::Data::new(conf)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error creating admin config: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bind_address(&self) -> (IpAddr, u16) {
|
||||
(self.addr, self.port)
|
||||
}
|
||||
|
@ -281,4 +314,26 @@ impl Config {
|
|||
|
||||
Ok(iri)
|
||||
}
|
||||
|
||||
pub(crate) fn generate_admin_url(&self, kind: AdminUrlKind) -> IriString {
|
||||
self.do_generate_admin_url(kind)
|
||||
.expect("Generated valid IRI")
|
||||
}
|
||||
|
||||
fn do_generate_admin_url(&self, kind: AdminUrlKind) -> Result<IriString, Error> {
|
||||
let iri = match kind {
|
||||
AdminUrlKind::Allow => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||
.try_resolve(IriRelativeStr::new("api/v1/admin/allow")?.as_ref())?,
|
||||
AdminUrlKind::Block => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||
.try_resolve(IriRelativeStr::new("api/v1/admin/block")?.as_ref())?,
|
||||
AdminUrlKind::Allowed => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||
.try_resolve(IriRelativeStr::new("api/v1/admin/allowed")?.as_ref())?,
|
||||
AdminUrlKind::Blocked => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||
.try_resolve(IriRelativeStr::new("api/v1/admin/blocked")?.as_ref())?,
|
||||
AdminUrlKind::Connected => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||
.try_resolve(IriRelativeStr::new("api/v1/admin/connected")?.as_ref())?,
|
||||
};
|
||||
|
||||
Ok(iri)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,6 +180,9 @@ pub(crate) enum ErrorKind {
|
|||
|
||||
#[error("Failed to extract fields from {0}")]
|
||||
Extract(&'static str),
|
||||
|
||||
#[error("No API Token supplied")]
|
||||
MissingApiToken,
|
||||
}
|
||||
|
||||
impl ResponseError for Error {
|
||||
|
|
194
src/extractors.rs
Normal file
194
src/extractors.rs
Normal file
|
@ -0,0 +1,194 @@
|
|||
use actix_web::{
|
||||
dev::Payload,
|
||||
error::ParseError,
|
||||
http::{
|
||||
header::{from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue},
|
||||
StatusCode,
|
||||
},
|
||||
web::Data,
|
||||
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
||||
};
|
||||
use bcrypt::{BcryptError, DEFAULT_COST};
|
||||
use http_signature_normalization_actix::prelude::InvalidHeaderValue;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
future::{ready, Ready},
|
||||
str::FromStr,
|
||||
};
|
||||
use tracing_error::SpanTrace;
|
||||
|
||||
use crate::db::Db;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AdminConfig {
|
||||
hashed_api_token: String,
|
||||
}
|
||||
|
||||
impl AdminConfig {
|
||||
pub(crate) fn build(api_token: &str) -> Result<Self, Error> {
|
||||
Ok(AdminConfig {
|
||||
hashed_api_token: bcrypt::hash(api_token, DEFAULT_COST).map_err(Error::bcrypt_hash)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn verify(&self, token: XApiToken) -> Result<bool, Error> {
|
||||
Ok(bcrypt::verify(&self.hashed_api_token, &token.0).map_err(Error::bcrypt_verify)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Admin {
|
||||
db: Data<Db>,
|
||||
}
|
||||
|
||||
impl Admin {
|
||||
#[tracing::instrument(level = "debug", skip(req))]
|
||||
fn verify(req: &HttpRequest) -> Result<Self, Error> {
|
||||
let hashed_api_token = req
|
||||
.app_data::<Data<AdminConfig>>()
|
||||
.ok_or_else(Error::missing_config)?;
|
||||
|
||||
let x_api_token = XApiToken::parse(req).map_err(Error::parse_header)?;
|
||||
|
||||
if hashed_api_token.verify(x_api_token)? {
|
||||
let db = req.app_data::<Data<Db>>().ok_or_else(Error::missing_db)?;
|
||||
|
||||
return Ok(Self { db: db.clone() });
|
||||
}
|
||||
|
||||
Err(Error::invalid())
|
||||
}
|
||||
|
||||
pub(crate) fn db_ref(&self) -> &Db {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Failed authentication")]
|
||||
pub(crate) struct Error {
|
||||
context: SpanTrace,
|
||||
#[source]
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn invalid() -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_config() -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::MissingConfig,
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_db() -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::MissingDb,
|
||||
}
|
||||
}
|
||||
|
||||
fn bcrypt_verify(e: BcryptError) -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::BCryptVerify(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn bcrypt_hash(e: BcryptError) -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::BCryptHash(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_header(e: ParseError) -> Self {
|
||||
Error {
|
||||
context: SpanTrace::capture(),
|
||||
kind: ErrorKind::ParseHeader(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum ErrorKind {
|
||||
#[error("Invalid API Token")]
|
||||
Invalid,
|
||||
|
||||
#[error("Missing Config")]
|
||||
MissingConfig,
|
||||
|
||||
#[error("Missing Db")]
|
||||
MissingDb,
|
||||
|
||||
#[error("Verifying")]
|
||||
BCryptVerify(#[source] BcryptError),
|
||||
|
||||
#[error("Hashing")]
|
||||
BCryptHash(#[source] BcryptError),
|
||||
|
||||
#[error("Parse Header")]
|
||||
ParseHeader(#[source] ParseError),
|
||||
}
|
||||
|
||||
impl ResponseError for Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self.kind {
|
||||
ErrorKind::Invalid | ErrorKind::ParseHeader(_) => StatusCode::BAD_REQUEST,
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::build(self.status_code())
|
||||
.json(serde_json::json!({ "msg": self.kind.to_string() }))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRequest for Admin {
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
ready(Admin::verify(req))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct XApiToken(String);
|
||||
|
||||
impl XApiToken {
|
||||
pub(crate) fn new(token: String) -> Self {
|
||||
Self(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl Header for XApiToken {
|
||||
fn name() -> HeaderName {
|
||||
HeaderName::from_static("x-api-token")
|
||||
}
|
||||
|
||||
fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
|
||||
from_one_raw_str(msg.headers().get(Self::name()))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoHeaderValue for XApiToken {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::from_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for XApiToken {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(XApiToken(s.to_string()))
|
||||
}
|
||||
}
|
25
src/main.rs
25
src/main.rs
|
@ -12,12 +12,14 @@ use tracing_error::ErrorLayer;
|
|||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{filter::Targets, fmt::format::FmtSpan, layer::SubscriberExt, Layer};
|
||||
|
||||
mod admin;
|
||||
mod apub;
|
||||
mod args;
|
||||
mod config;
|
||||
mod data;
|
||||
mod db;
|
||||
mod error;
|
||||
mod extractors;
|
||||
mod jobs;
|
||||
mod middleware;
|
||||
mod requests;
|
||||
|
@ -135,15 +137,22 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
|
||||
let bind_address = config.bind_address();
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(TracingLogger::default())
|
||||
let app = App::new()
|
||||
.app_data(web::Data::new(db.clone()))
|
||||
.app_data(web::Data::new(state.clone()))
|
||||
.app_data(web::Data::new(state.requests(&config)))
|
||||
.app_data(web::Data::new(actors.clone()))
|
||||
.app_data(web::Data::new(config.clone()))
|
||||
.app_data(web::Data::new(job_server.clone()))
|
||||
.app_data(web::Data::new(media.clone()))
|
||||
.app_data(web::Data::new(media.clone()));
|
||||
|
||||
let app = if let Some(data) = config.admin_config() {
|
||||
app.app_data(data)
|
||||
} else {
|
||||
app
|
||||
};
|
||||
|
||||
app.wrap(TracingLogger::default())
|
||||
.service(web::resource("/").route(web::get().to(index)))
|
||||
.service(web::resource("/media/{path}").route(web::get().to(routes::media)))
|
||||
.service(
|
||||
|
@ -165,6 +174,16 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
.service(web::resource("/nodeinfo").route(web::get().to(nodeinfo_meta))),
|
||||
)
|
||||
.service(web::resource("/static/{filename}").route(web::get().to(statics)))
|
||||
.service(
|
||||
web::scope("/api/v1").service(
|
||||
web::scope("/admin")
|
||||
.route("/allow", web::post().to(admin::routes::allow))
|
||||
.route("/block", web::post().to(admin::routes::block))
|
||||
.route("/allowed", web::get().to(admin::routes::allowed))
|
||||
.route("/blocked", web::get().to(admin::routes::blocked))
|
||||
.route("/connected", web::get().to(admin::routes::connected)),
|
||||
),
|
||||
)
|
||||
})
|
||||
.bind(bind_address)?
|
||||
.run()
|
||||
|
|
Loading…
Reference in a new issue