feat: plausible analytics
This commit is contained in:
parent
46a68272cc
commit
f10173d880
@ -15,11 +15,13 @@ path = "src/bin/main.rs"
|
||||
name = "route96"
|
||||
|
||||
[features]
|
||||
default = ["nip96", "blossom"]
|
||||
default = ["nip96", "blossom", "analytics"]
|
||||
labels = ["nip96", "dep:candle-core", "dep:candle-nn", "dep:candle-transformers"]
|
||||
nip96 = ["dep:ffmpeg-sys-the-third", "dep:blurhash", "dep:libc"]
|
||||
blossom = []
|
||||
bin-void-cat-migrate = ["dep:sqlx-postgres", "dep:clap", "dep:clap_derive"]
|
||||
torrent-v2 = []
|
||||
analytics = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.21"
|
||||
|
@ -21,3 +21,6 @@ public_url = "http://localhost:8000"
|
||||
|
||||
# Webhook api endpoint
|
||||
# webhook_url = "https://api.snort.social/api/v1/media/webhook"
|
||||
|
||||
# Analytics support
|
||||
# plausible_url = "https://plausible.com/"
|
41
src/analytics/mod.rs
Normal file
41
src/analytics/mod.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use anyhow::Error;
|
||||
use log::warn;
|
||||
use rocket::fairing::{Fairing, Info, Kind};
|
||||
use rocket::{Data, Request};
|
||||
|
||||
pub mod plausible;
|
||||
|
||||
pub trait Analytics {
|
||||
fn track(&self, req: &Request) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct AnalyticsFairing {
|
||||
inner: Box<dyn Analytics + Sync + Send>,
|
||||
}
|
||||
|
||||
impl AnalyticsFairing {
|
||||
pub fn new<T>(inner: T) -> Self
|
||||
where
|
||||
T: Analytics + Send + Sync + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: Box::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl Fairing for AnalyticsFairing {
|
||||
fn info(&self) -> Info {
|
||||
Info {
|
||||
name: "Analytics",
|
||||
kind: Kind::Request,
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_request(&self, req: &mut Request<'_>, _data: &mut Data<'_>) {
|
||||
if let Err(e) = self.inner.track(req) {
|
||||
warn!("Failed to track! {}", e);
|
||||
}
|
||||
}
|
||||
}
|
55
src/analytics/plausible.rs
Normal file
55
src/analytics/plausible.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::analytics::Analytics;
|
||||
use crate::settings::Settings;
|
||||
use anyhow::Error;
|
||||
use log::{info, warn};
|
||||
use rocket::Request;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Event {
|
||||
pub name: String,
|
||||
pub domain: String,
|
||||
pub url: String,
|
||||
pub referrer: Option<String>,
|
||||
}
|
||||
|
||||
pub struct PlausibleAnalytics {
|
||||
tx: UnboundedSender<Event>,
|
||||
}
|
||||
|
||||
impl PlausibleAnalytics {
|
||||
pub fn new(settings: &Settings) -> Self {
|
||||
let (tx, mut rx) = unbounded_channel::<Event>();
|
||||
let url = match &settings.plausible_url {
|
||||
Some(s) => s.clone(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
let pub_url = settings.public_url.clone();
|
||||
tokio::spawn(async move {
|
||||
while let Some(mut msg) = rx.recv().await {
|
||||
msg.url = format!("{}{}", pub_url, msg.url);
|
||||
match ureq::post(&format!("{}/api/event", url)).send_json(&msg) {
|
||||
Ok(v) => info!("Sent {:?}", msg),
|
||||
Err(e) => warn!("Failed to track: {}", e),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Self { tx }
|
||||
}
|
||||
}
|
||||
|
||||
impl Analytics for PlausibleAnalytics {
|
||||
fn track(&self, req: &Request) -> Result<(), Error> {
|
||||
Ok(self.tx.send(Event {
|
||||
name: "pageview".to_string(),
|
||||
domain: match req.host() {
|
||||
Some(s) => s.to_string(),
|
||||
None => return Ok(()), // ignore request
|
||||
},
|
||||
url: req.uri().to_string(),
|
||||
referrer: None,
|
||||
})?)
|
||||
}
|
||||
}
|
@ -7,7 +7,10 @@ use rocket::config::Ident;
|
||||
use rocket::data::{ByteUnit, Limits};
|
||||
use rocket::routes;
|
||||
use rocket::shield::Shield;
|
||||
|
||||
#[cfg(feature = "analytics")]
|
||||
use route96::analytics::plausible::PlausibleAnalytics;
|
||||
#[cfg(feature = "analytics")]
|
||||
use route96::analytics::AnalyticsFairing;
|
||||
use route96::cors::CORS;
|
||||
use route96::db::Database;
|
||||
use route96::filesystem::FileStore;
|
||||
@ -61,6 +64,12 @@ async fn main() -> Result<(), Error> {
|
||||
.attach(Shield::new()) // disable
|
||||
.mount("/", routes![root, get_blob, head_blob]);
|
||||
|
||||
#[cfg(feature = "analytics")]
|
||||
{
|
||||
if settings.plausible_url.is_some() {
|
||||
rocket = rocket.attach(AnalyticsFairing::new(PlausibleAnalytics::new(&settings)))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "blossom")]
|
||||
{
|
||||
rocket = rocket.mount("/", routes::blossom_routes());
|
||||
|
@ -8,3 +8,5 @@ pub mod processing;
|
||||
pub mod routes;
|
||||
pub mod settings;
|
||||
pub mod webhook;
|
||||
#[cfg(feature = "analytics")]
|
||||
pub mod analytics;
|
@ -1,5 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
@ -26,4 +26,7 @@ pub struct Settings {
|
||||
|
||||
/// Webhook api endpoint
|
||||
pub webhook_url: Option<String>,
|
||||
|
||||
/// Analytics tracking
|
||||
pub plausible_url: Option<String>,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user