impl webhook route #1
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": ["rust-lang.rust-analyzer"],
|
"extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml", "fill-labs.dependi"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"[rust]": {
|
"[rust]": {
|
||||||
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
||||||
|
|||||||
Generated
+107
@@ -100,6 +100,15 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.2"
|
||||||
@@ -145,6 +154,12 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmov"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -155,6 +170,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -181,6 +202,45 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctutils"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||||
|
dependencies = [
|
||||||
|
"cmov",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"const-oid",
|
||||||
|
"crypto-common",
|
||||||
|
"ctutils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -353,13 +413,34 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
|
"bytes",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
|
"hex",
|
||||||
|
"hmac",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -405,6 +486,15 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hybrid-array"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@@ -1183,6 +1273,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -1469,6 +1570,12 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
|||||||
@@ -11,3 +11,9 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
axum = "0.8"
|
axum = "0.8"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
thiserror = "2.0"
|
||||||
|
hmac = "0.13"
|
||||||
|
sha2 = "0.11"
|
||||||
|
hex = "0.4"
|
||||||
|
subtle = "2.6"
|
||||||
|
bytes = "1.11"
|
||||||
+63
-23
@@ -1,16 +1,28 @@
|
|||||||
use axum::response::{Response, IntoResponse};
|
use anyhow::anyhow;
|
||||||
use axum::{Json, Router};
|
use axum::body::to_bytes;
|
||||||
use axum::extract::FromRequest;
|
use axum::extract::FromRequest;
|
||||||
use axum::routing::{post, get};
|
use axum::response::{IntoResponse, Response};
|
||||||
use reqwest::StatusCode;
|
use axum::routing::{get, post};
|
||||||
|
use axum::{Json, Router};
|
||||||
|
use hmac::{Hmac, KeyInit, Mac};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use sha2::Sha256;
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
use crate::env::{EnvConfig};
|
use crate::errors::AppError;
|
||||||
use crate::gitea::WebhookType;
|
use crate::gitea::WebhookType;
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
pub async fn start_api(config: EnvConfig) -> anyhow::Result<()> {
|
const MAX_WEBHOOK_BODY_SIZE: usize = 1024 * 1024; // 1 Mo
|
||||||
let app = Router::new().route("/", get(root)).route("/webhook", post(webhook));
|
|
||||||
let listerner = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", config.http_port)).await?;
|
pub async fn start(app_state: AppState) -> anyhow::Result<()> {
|
||||||
|
let http_port = app_state.config.http_port;
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.with_state(app_state)
|
||||||
|
.route("/", get(root))
|
||||||
|
.route("/webhook", post(webhook));
|
||||||
|
let listerner = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", http_port)).await?;
|
||||||
|
|
||||||
axum::serve(listerner, app)
|
axum::serve(listerner, app)
|
||||||
.await
|
.await
|
||||||
@@ -21,11 +33,12 @@ async fn root() -> &'static str {
|
|||||||
"Hi, i'm Herald :)"
|
"Hi, i'm Herald :)"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn webhook(WebhookExtract(wb): WebhookExtract) -> Response {
|
async fn webhook(WebhookExtract(wb): WebhookExtract) -> Result<Response, AppError> {
|
||||||
match wb {
|
Ok(match wb {
|
||||||
WebhookType::Review(id, _) => format!("Received {} pr id", id),
|
WebhookType::Review(id, _) => format!("Received {} pr id", id),
|
||||||
_ => String::from("Nothing to see :/")
|
_ => String::from("Nothing to see :/"),
|
||||||
}.into_response()
|
}
|
||||||
|
.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WebhookExtract(pub WebhookType);
|
pub struct WebhookExtract(pub WebhookType);
|
||||||
@@ -34,21 +47,48 @@ impl<S> FromRequest<S> for WebhookExtract
|
|||||||
where
|
where
|
||||||
S: Send + Sync,
|
S: Send + Sync,
|
||||||
{
|
{
|
||||||
type Rejection = Response;
|
type Rejection = AppError;
|
||||||
|
|
||||||
async fn from_request(
|
async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
req: axum::extract::Request,
|
let headers = req.headers();
|
||||||
state: &S,
|
let sig_header = headers
|
||||||
) -> Result<Self, Self::Rejection> {
|
.get("x-gitea-signature")
|
||||||
let Json(value) = Json::<Value>::from_request(req, state)
|
.ok_or(AppError::WebHookSigHeaderNotFoundErr)?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|err| anyhow!(err))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let body = req.into_body();
|
||||||
|
let body_bytes = to_bytes(body, MAX_WEBHOOK_BODY_SIZE)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.into_response())?;
|
.map_err(|err| anyhow!(err))?;
|
||||||
|
|
||||||
let webhook = WebhookType::try_from(value)
|
let Json(value) =
|
||||||
.map_err(|e| {
|
Json::<Value>::from_bytes(&body_bytes).map_err(|_| AppError::MalformedJsonErr)?;
|
||||||
(StatusCode::UNPROCESSABLE_ENTITY, e.to_string()).into_response()
|
let webhook = WebhookType::try_from(value)?;
|
||||||
})?;
|
|
||||||
|
check_sig_header(sig_header.as_bytes(), &body_bytes)?;
|
||||||
|
|
||||||
Ok(WebhookExtract(webhook))
|
Ok(WebhookExtract(webhook))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_sig_header(sig_header: &[u8], body: &[u8]) -> Result<(), AppError> {
|
||||||
|
let sig_header_decoded = hex::decode(sig_header).map_err(|err| anyhow!(err))?;
|
||||||
|
|
||||||
|
let webhook_sig_header_secret =
|
||||||
|
std::env::var("WEBHOOK_SIG_HEADER_SECRET").map_err(|err| anyhow!(err))?;
|
||||||
|
|
||||||
|
let mut mac = Hmac::<Sha256>::new_from_slice(&webhook_sig_header_secret.into_bytes())
|
||||||
|
.map_err(|err| anyhow!(err))?;
|
||||||
|
|
||||||
|
mac.update(body);
|
||||||
|
|
||||||
|
let generated_hmac = mac.finalize().into_bytes();
|
||||||
|
let check_result: bool = generated_hmac.ct_eq(&sig_header_decoded).into();
|
||||||
|
|
||||||
|
match check_result {
|
||||||
|
true => Ok(()),
|
||||||
|
false => Err(AppError::WebHookSigHeaderInvalidErr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+11
-3
@@ -1,5 +1,13 @@
|
|||||||
use crate::gitea::WebhookType;
|
use crate::{env::EnvConfig, gitea::WebhookType};
|
||||||
|
|
||||||
pub async fn exec(webhook: WebhookType) {
|
|
||||||
|
|
||||||
|
pub struct Bot {
|
||||||
|
config: EnvConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bot {
|
||||||
|
pub fn new(config: EnvConfig) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn exec(&self, webhook: WebhookType) {}
|
||||||
}
|
}
|
||||||
+20
-3
@@ -1,19 +1,36 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct EnvConfig {
|
pub struct EnvConfig {
|
||||||
pub http_port: u16,
|
pub http_port: u16,
|
||||||
|
pub webhook_secret: String,
|
||||||
|
pub open_router_api_key: String,
|
||||||
pub bot_name: String,
|
pub bot_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_config() -> anyhow::Result<EnvConfig> {
|
pub fn load_config() -> anyhow::Result<EnvConfig> {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
let http_port = std::env::var("HTTP_PORT")?.parse()?;
|
let http_port = try_get_env("HTTP_PORT")?.parse()?;
|
||||||
let bot_name = std::env::var("BOT_NAME")?;
|
let bot_name = try_get_env("BOT_NAME")?;
|
||||||
|
let webhook_secret = try_get_env("WEBHOOK_SIG_HEADER_SECRET")?;
|
||||||
|
let open_router_api_key = try_get_env("OPEN_ROUTER_API_KEY")?;
|
||||||
|
|
||||||
Ok(EnvConfig {
|
Ok(EnvConfig {
|
||||||
http_port,
|
http_port,
|
||||||
|
webhook_secret,
|
||||||
bot_name,
|
bot_name,
|
||||||
|
open_router_api_key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_get_env(key: &str) -> anyhow::Result<String> {
|
||||||
|
let env = std::env::var(key)?;
|
||||||
|
|
||||||
|
if env.trim().len() == 0 {
|
||||||
|
return Err(anyhow!(format!("env var {} is empty", env)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(env)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
use axum::response::IntoResponse;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("Malformed Json")]
|
||||||
|
MalformedJsonErr,
|
||||||
|
|
||||||
|
#[error("Json not contains mandatory fields")]
|
||||||
|
BadJsonStructErr,
|
||||||
|
|
||||||
|
#[error("WebHook sig header not found")]
|
||||||
|
WebHookSigHeaderNotFoundErr,
|
||||||
|
|
||||||
|
#[error("WebHook sig header is invalid")]
|
||||||
|
WebHookSigHeaderInvalidErr,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for AppError {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
match self {
|
||||||
|
AppError::MalformedJsonErr => (StatusCode::BAD_REQUEST, "Malformed Json"),
|
||||||
|
AppError::BadJsonStructErr => (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Json not contains mandatory fields",
|
||||||
|
),
|
||||||
|
AppError::WebHookSigHeaderNotFoundErr => {
|
||||||
|
(StatusCode::BAD_REQUEST, "WebHook sig header not found")
|
||||||
|
}
|
||||||
|
AppError::WebHookSigHeaderInvalidErr => {
|
||||||
|
(StatusCode::BAD_REQUEST, "WebHook sig header is invalid")
|
||||||
|
}
|
||||||
|
AppError::Other(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"),
|
||||||
|
}
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-3
@@ -1,17 +1,27 @@
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::errors::AppError;
|
||||||
|
|
||||||
pub enum WebhookType {
|
pub enum WebhookType {
|
||||||
Review(u64, String)
|
Review(u64, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Value> for WebhookType {
|
impl TryFrom<Value> for WebhookType {
|
||||||
type Error = anyhow::Error;
|
type Error = AppError;
|
||||||
|
|
||||||
fn try_from(json: Value) -> Result<Self, Self::Error> {
|
fn try_from(json: Value) -> Result<Self, Self::Error> {
|
||||||
let pull_request = json.get("pull_request");
|
let pull_request = json.get("pull_request");
|
||||||
let comment = json.get("comment");
|
let comment = json.get("comment");
|
||||||
|
let action = json
|
||||||
|
.get("action")
|
||||||
|
.ok_or(anyhow!("action not found"))?
|
||||||
|
.as_str()
|
||||||
|
.ok_or(anyhow!("error while action"))?;
|
||||||
|
|
||||||
|
if action != "created" {
|
||||||
|
return Err(AppError::BadJsonStructErr);
|
||||||
|
}
|
||||||
|
|
||||||
if let (Some(pull_request), Some(comment)) = (pull_request, comment) {
|
if let (Some(pull_request), Some(comment)) = (pull_request, comment) {
|
||||||
let comment_body = comment
|
let comment_body = comment
|
||||||
@@ -30,6 +40,6 @@ impl TryFrom<Value> for WebhookType {
|
|||||||
return Ok(WebhookType::Review(pr_id, comment_body));
|
return Ok(WebhookType::Review(pr_id, comment_body));
|
||||||
}
|
}
|
||||||
|
|
||||||
anyhow::bail!("unknow webhook type")
|
Err(AppError::BadJsonStructErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+16
-3
@@ -1,11 +1,24 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{bot::Bot, state::AppState};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod env;
|
|
||||||
mod gitea;
|
|
||||||
mod bot;
|
mod bot;
|
||||||
|
mod env;
|
||||||
|
mod errors;
|
||||||
|
mod gitea;
|
||||||
|
mod state;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let config = env::load_config()?;
|
let config = env::load_config()?;
|
||||||
|
|
||||||
api::start_api(config).await
|
let app_state = AppState {
|
||||||
|
bot: Arc::new(Mutex::new(Bot::new(config.clone()))),
|
||||||
|
config: config,
|
||||||
|
};
|
||||||
|
|
||||||
|
api::start(app_state).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{bot::Bot, env::EnvConfig};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub bot: Arc<Mutex<Bot>>,
|
||||||
|
pub config: EnvConfig,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user