✨ Adds gotify notifications
This commit is contained in:
828
Cargo.lock
generated
828
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -12,3 +12,4 @@ diesel = { version = "2.3.2", features = ["postgres", "uuid", "chrono"] }
|
||||
chrono = "0.4.42"
|
||||
log = "0.4.28"
|
||||
fern = "0.7.1"
|
||||
reqwest = { version = "0.12.23", features = ["json"] }
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::models::AppState;
|
||||
use crate::models::alert::Alert;
|
||||
use crate::models::hit::Hit;
|
||||
use crate::models::tracker::Tracker;
|
||||
use crate::schema::{hits, trackers};
|
||||
use chrono::Utc;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use diesel::{ExpressionMethods, QueryDsl, QueryResult, RunQueryDsl};
|
||||
use rocket::fs::NamedFile;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{FromRequest, Outcome};
|
||||
@@ -51,33 +52,76 @@ pub async fn get(id: &str, meta: ReqMeta, state: &State<AppState>) -> Result<Nam
|
||||
None => return Err(Status::BadRequest),
|
||||
};
|
||||
|
||||
let mut tracker: Option<Tracker> = None;
|
||||
let mut result: Option<QueryResult<usize>> = None;
|
||||
let now = Utc::now().naive_utc();
|
||||
|
||||
{
|
||||
let mut db = state.db.lock().unwrap();
|
||||
|
||||
let result = trackers::dsl::trackers
|
||||
let tresult = trackers::dsl::trackers
|
||||
.filter(trackers::id.eq(id))
|
||||
.first::<Tracker>(&mut *db)
|
||||
.ok();
|
||||
|
||||
if result.is_none() {
|
||||
if tresult.is_none() {
|
||||
return Err(Status::NotFound);
|
||||
}
|
||||
tracker = Some(tresult.unwrap());
|
||||
|
||||
let result = diesel::insert_into(hits::table)
|
||||
.values(&Hit {
|
||||
id: Uuid::new_v4(),
|
||||
tracker_id: id,
|
||||
ip: meta.ip,
|
||||
agent: meta.agent,
|
||||
language: meta.language,
|
||||
created_at: Utc::now().naive_utc(),
|
||||
})
|
||||
.execute(&mut *db);
|
||||
if result.is_err() {
|
||||
result = Some(
|
||||
diesel::insert_into(hits::table)
|
||||
.values(&Hit {
|
||||
id: Uuid::new_v4(),
|
||||
tracker_id: id,
|
||||
ip: meta.ip.clone(),
|
||||
agent: meta.agent.clone(),
|
||||
language: meta.language.clone(),
|
||||
created_at: now,
|
||||
})
|
||||
.execute(&mut *db),
|
||||
);
|
||||
if result.as_ref().unwrap().is_err() {
|
||||
error!("Failed to insert hit: {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
let url = state.gotify.url.clone();
|
||||
let token = state.gotify.token.clone();
|
||||
|
||||
let tracker = tracker.unwrap();
|
||||
let tracker_name = tracker.name.clone().unwrap_or("unknown".to_string());
|
||||
let tracker_id = tracker.id.clone();
|
||||
|
||||
if state.gotify.enabled {
|
||||
let alert = Alert {
|
||||
title: format!("Tracker '{tracker_name}' ({tracker_id}) has been hit"),
|
||||
message: format!(
|
||||
"IP: {}\nAgent: {}\nLanguage: {}\nCreated at: {}\nError: {}",
|
||||
meta.ip,
|
||||
meta.agent.unwrap_or("unknown".to_string()),
|
||||
meta.language.unwrap_or("unknown".to_string()),
|
||||
now.to_string(),
|
||||
result
|
||||
.unwrap()
|
||||
.err()
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
),
|
||||
priority: 5,
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let result = client
|
||||
.post(format!("{url}/message?token={token}"))
|
||||
.json(&alert)
|
||||
.send()
|
||||
.await;
|
||||
if result.is_err() {
|
||||
error!("Failed to send a alert to {url}: {result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
NamedFile::open(Path::new(state.static_dir.as_str()).join("image.png"))
|
||||
.await
|
||||
.map_err(|_| Status::InternalServerError)
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@@ -7,7 +7,7 @@ mod schema;
|
||||
use crate::api::hit;
|
||||
use crate::api::image;
|
||||
use crate::api::tracker;
|
||||
use crate::models::AppState;
|
||||
use crate::models::{AppState, GotifyState};
|
||||
use chrono::Local;
|
||||
use diesel::{Connection, PgConnection};
|
||||
use rocket::State;
|
||||
@@ -55,10 +55,24 @@ fn rocket() -> _ {
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let static_dir = env::var("STATIC_DIR").expect("STATIC_DIR must be set");
|
||||
|
||||
let gotify_url = env::var("GOTIFY_URL").unwrap_or("".to_string());
|
||||
let gotify_token = env::var("GOTIFY_TOKEN").unwrap_or("".to_string());
|
||||
let gotify_enabled_raw = env::var("GOTIFY_ENABLED").unwrap_or("false".to_string());
|
||||
let gotify_enabled = gotify_enabled_raw.to_lowercase().trim() == "true";
|
||||
if (gotify_url.is_empty() || gotify_token.is_empty()) && gotify_enabled {
|
||||
panic!("Gotify is enabled but either GOTIFY_URL or GOTIFY_TOKEN isn't set");
|
||||
}
|
||||
|
||||
let db = PgConnection::establish(&database_url)
|
||||
.expect(&format!("Error connecting to {}", database_url));
|
||||
|
||||
let app_data = AppState::new(db, static_dir);
|
||||
let gotify_data = GotifyState {
|
||||
url: gotify_url,
|
||||
token: gotify_token,
|
||||
enabled: gotify_enabled
|
||||
};
|
||||
let app_data = AppState::new(db, static_dir, gotify_data);
|
||||
rocket::build()
|
||||
.manage(app_data)
|
||||
.mount("/", routes![index])
|
||||
|
||||
8
src/models/alert.rs
Normal file
8
src/models/alert.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Alert {
|
||||
pub title: String,
|
||||
pub message: String,
|
||||
pub priority: i32,
|
||||
}
|
||||
@@ -1,19 +1,28 @@
|
||||
use diesel::PgConnection;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub mod alert;
|
||||
pub mod hit;
|
||||
pub mod tracker;
|
||||
|
||||
pub struct GotifyState {
|
||||
pub url: String,
|
||||
pub token: String,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
pub db: Arc<Mutex<PgConnection>>,
|
||||
pub static_dir: String,
|
||||
pub gotify: GotifyState,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(db: PgConnection, static_dir: String) -> Self {
|
||||
pub fn new(db: PgConnection, static_dir: String, gotify: GotifyState) -> Self {
|
||||
AppState {
|
||||
db: Arc::new(Mutex::new(db)),
|
||||
static_dir,
|
||||
gotify,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user