Add basic analytics
This commit is contained in:
parent
938be8949f
commit
a4193f830f
144
Cargo.lock
generated
144
Cargo.lock
generated
@ -36,6 +36,21 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.79"
|
||||
@ -168,6 +183,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@ -194,9 +215,10 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chela"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
"color-eyre",
|
||||
"eyre",
|
||||
"hyper",
|
||||
@ -210,6 +232,21 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.3"
|
||||
@ -243,6 +280,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
@ -724,6 +767,29 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@ -777,6 +843,15 @@ version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -1450,6 +1525,7 @@ dependencies = [
|
||||
"atoi",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
@ -1532,6 +1608,7 @@ dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"dotenvy",
|
||||
@ -1573,6 +1650,7 @@ dependencies = [
|
||||
"base64",
|
||||
"bitflags 2.5.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
"dotenvy",
|
||||
"etcetera",
|
||||
@ -1608,6 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"chrono",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -1954,6 +2033,60 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
@ -1970,6 +2103,15 @@ dependencies = [
|
||||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "chela"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7.5", features = ["tokio"] }
|
||||
chrono = { version = "0.4.37", features = ["serde"] }
|
||||
color-eyre = "0.6.3"
|
||||
eyre = "0.6.12"
|
||||
hyper = "1.2.0"
|
||||
@ -14,7 +15,7 @@ hyper-util = { version = "0.1.3", features = ["tokio"] }
|
||||
info_utils = "2.2.3"
|
||||
serde = "1.0.197"
|
||||
sqids = "0.4.1"
|
||||
sqlx = { version = "0.7.4", features = ["runtime-tokio", "postgres", "macros", "migrate", "tls-rustls"] }
|
||||
sqlx = { version = "0.7.4", features = ["runtime-tokio", "postgres", "macros", "migrate", "tls-rustls", "chrono"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tower = "0.4.13"
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
||||
|
14
README.md
14
README.md
@ -7,6 +7,8 @@ Chela is a minimal URL shortener built in Rust. It is named after the small claw
|
||||
## Usage
|
||||
You can create a redirect by navigating to the `/create` page and filling out the form. By default, every path passed to Chela will be treated as a redirect except `/` and `/create`.
|
||||
|
||||
Chela also supports basic analytics for shortened URLs. This page is available at `/tracking`, and `/tracking/<URL ID>`.
|
||||
|
||||
## Install and Run
|
||||
### With Docker
|
||||
#### CLI
|
||||
@ -86,9 +88,9 @@ $ ./target/release/chela
|
||||
```
|
||||
|
||||
## Hosting
|
||||
Chela uses the [axum](https://crates.io/crates/axum) to manage HTTP requests, so it is possible to expose it directly to the outer internet. However, there is no authentication for the `/create` endpoint so anyone will be able to create redirects.
|
||||
Chela uses the [axum](https://crates.io/crates/axum) to manage HTTP requests, so it is possible to expose it directly to the outer internet. However, there is no authentication for the `/create` or `/tracking` endpoints so anyone will be able to create redirects and view analytics.
|
||||
|
||||
If you would prefer to be the only one able to create redirects, then you can proxy Chela through Nginx with http-basic-auth. Refer to [this](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/) documentation for more information.
|
||||
If you would prefer to be the only one able to access these pages, then you can proxy Chela through Nginx with http-basic-auth. Refer to [this](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/) documentation for more information.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
@ -103,5 +105,13 @@ server {
|
||||
auth_basic_user_file /path/to/your/.htpasswd;
|
||||
}
|
||||
}
|
||||
|
||||
location /tracking {
|
||||
proxy_pass http://localhost:3000/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
auth_basic 'Restricted';
|
||||
auth_basic_user_file /path/to/your/.htpasswd;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
255
src/get.rs
255
src/get.rs
@ -1,3 +1,4 @@
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::extract::{ConnectInfo, Path};
|
||||
@ -9,9 +10,16 @@ use axum::Extension;
|
||||
use info_utils::prelude::*;
|
||||
|
||||
use crate::ServerState;
|
||||
use crate::TrackingRow;
|
||||
use crate::UdsConnectInfo;
|
||||
use crate::UrlRow;
|
||||
|
||||
enum TrackingParameter {
|
||||
Ip,
|
||||
Referrer,
|
||||
UserAgent,
|
||||
}
|
||||
|
||||
pub async fn index(Extension(state): Extension<ServerState>) -> impl IntoResponse {
|
||||
if let Some(redirect) = state.main_page_redirect {
|
||||
return Redirect::temporary(redirect.as_str()).into_response();
|
||||
@ -212,3 +220,250 @@ pub async fn create_id(Extension(state): Extension<ServerState>) -> Html<String>
|
||||
state.host
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn tracking(Extension(state): Extension<ServerState>) -> impl IntoResponse {
|
||||
let url_rows: Vec<UrlRow> = sqlx::query_as("SELECT * FROM chela.urls")
|
||||
.fetch_all(&state.db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let html = format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{} Tracking</title>
|
||||
</head>
|
||||
<style>{}</style>
|
||||
<body>
|
||||
{}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
state.host,
|
||||
table_css(),
|
||||
make_table_from_urls(&url_rows)
|
||||
);
|
||||
|
||||
return Html(html).into_response();
|
||||
}
|
||||
|
||||
pub async fn tracking_id(
|
||||
Extension(state): Extension<ServerState>,
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
let tracking_rows: Vec<TrackingRow> =
|
||||
sqlx::query_as("SELECT * FROM chela.tracking WHERE id = $1")
|
||||
.bind(id.clone())
|
||||
.fetch_all(&state.db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let url: UrlRow = sqlx::query_as("SELECT * FROM chela.urls WHERE id = $1")
|
||||
.bind(id.clone())
|
||||
.fetch_one(&state.db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let html = format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{} Tracking {}</title>
|
||||
</head>
|
||||
<style>{}</style>
|
||||
<body>
|
||||
<h1>Tracking for <a href="{}">{}</a> from ID '{}'</h1>
|
||||
<h2>Visited {} times</h2>
|
||||
{}
|
||||
|
||||
<h2>By IP</h2>
|
||||
{}
|
||||
<h2>By Referrer</h2>
|
||||
{}
|
||||
<h2>By User Agent</h2>
|
||||
{}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
state.host,
|
||||
id,
|
||||
table_css(),
|
||||
url.url,
|
||||
url.url,
|
||||
url.id,
|
||||
tracking_rows.len(),
|
||||
make_table_from_tracking(&tracking_rows),
|
||||
make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::Ip),
|
||||
make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::Referrer),
|
||||
make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::UserAgent)
|
||||
);
|
||||
|
||||
return Html(html).into_response();
|
||||
}
|
||||
|
||||
fn make_table_from_tracking(rows: &Vec<TrackingRow>) -> String {
|
||||
let mut html = r#"<table>
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>ID</th>
|
||||
<th>IP</th>
|
||||
<th>Referrer</th>
|
||||
<th>User Agent</th>
|
||||
</tr>
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
for row in rows {
|
||||
html += &format!(
|
||||
r#"
|
||||
<tr>
|
||||
<td>{}</td>
|
||||
<td>{}</td>
|
||||
<td>{}</td>
|
||||
<td>{}</td>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
"#,
|
||||
row.timestamp,
|
||||
row.id,
|
||||
row.ip.as_ref().unwrap_or(&String::default()),
|
||||
row.referrer.as_ref().unwrap_or(&String::default()),
|
||||
row.user_agent.as_ref().unwrap_or(&String::default())
|
||||
);
|
||||
}
|
||||
|
||||
html += r#"
|
||||
</table>
|
||||
"#;
|
||||
|
||||
html
|
||||
}
|
||||
|
||||
fn make_grouped_table_from_tracking(rows: &Vec<TrackingRow>, group: TrackingParameter) -> String {
|
||||
let column_name = match group {
|
||||
TrackingParameter::Ip => "IP",
|
||||
TrackingParameter::Referrer => "Referrer",
|
||||
TrackingParameter::UserAgent => "User Agent",
|
||||
}
|
||||
.to_string();
|
||||
|
||||
let mut html = format!(
|
||||
r#"
|
||||
<table>
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>Occurrences</th>
|
||||
<th>{}</th>
|
||||
</tr>
|
||||
"#,
|
||||
column_name
|
||||
);
|
||||
|
||||
let mut aggregate: HashMap<String, u32> = HashMap::new();
|
||||
|
||||
for row in rows {
|
||||
let tracker = match group {
|
||||
TrackingParameter::Ip => {
|
||||
let v = match &row.ip {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
};
|
||||
v
|
||||
}
|
||||
TrackingParameter::Referrer => {
|
||||
let v = match &row.referrer {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
};
|
||||
v
|
||||
}
|
||||
TrackingParameter::UserAgent => {
|
||||
let v = match &row.user_agent {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
};
|
||||
v
|
||||
}
|
||||
};
|
||||
let count = aggregate.get(tracker).unwrap_or(&0);
|
||||
aggregate.insert(tracker.to_string(), count + 1);
|
||||
}
|
||||
|
||||
for (key, val) in aggregate {
|
||||
html += &format!(
|
||||
r#"
|
||||
<tr>
|
||||
<td>{}</td>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
"#,
|
||||
val, key
|
||||
);
|
||||
}
|
||||
|
||||
html += r#"
|
||||
</table>
|
||||
"#;
|
||||
|
||||
html
|
||||
}
|
||||
|
||||
fn make_table_from_urls(urls: &Vec<UrlRow>) -> String {
|
||||
let mut html = r#"<table>
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th>ID</th>
|
||||
<th>URL</th>
|
||||
<th>Custom ID</th>
|
||||
</tr>
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
for url in urls {
|
||||
html += &format!(
|
||||
r#"
|
||||
<tr>
|
||||
<td>{}</td>
|
||||
<td><a href="/tracking/{}">{}</a></td>
|
||||
<td><a href="{}">{}</a></td>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
"#,
|
||||
url.index, url.id, url.id, url.url, url.url, url.custom_id
|
||||
);
|
||||
}
|
||||
html += r#"
|
||||
</table>
|
||||
"#;
|
||||
|
||||
html
|
||||
}
|
||||
|
||||
fn table_css() -> String {
|
||||
r#"
|
||||
tr:nth-child(even) {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
22
src/main.rs
22
src/main.rs
@ -36,6 +36,16 @@ pub struct UrlRow {
|
||||
pub index: i64,
|
||||
pub id: String,
|
||||
pub url: String,
|
||||
pub custom_id: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, sqlx::FromRow, PartialEq, Eq)]
|
||||
pub struct TrackingRow {
|
||||
pub timestamp: chrono::DateTime<chrono::Utc>,
|
||||
pub id: String,
|
||||
pub ip: Option<String>,
|
||||
pub referrer: Option<String>,
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
@ -73,7 +83,7 @@ async fn main() -> eyre::Result<()> {
|
||||
.unwrap_or("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string());
|
||||
let sqids = Sqids::builder()
|
||||
.alphabet(alphabet.chars().collect())
|
||||
.blocklist(["create".to_string()].into())
|
||||
.blocklist(["create".to_string(), "tracking".to_string()].into())
|
||||
.build()?;
|
||||
let main_page_redirect = env::var("CHELA_MAIN_PAGE_REDIRECT").unwrap_or_default();
|
||||
let behind_proxy = env::var("CHELA_BEHIND_PROXY").is_ok();
|
||||
@ -94,8 +104,10 @@ async fn serve(state: ServerState) -> eyre::Result<()> {
|
||||
if unix_socket.is_empty() {
|
||||
let router = Router::new()
|
||||
.route("/", get(get::index))
|
||||
.route("/:id", get(get::id))
|
||||
.route("/create", get(get::create_id))
|
||||
.route("/tracking", get(get::tracking))
|
||||
.route("/tracking/:id", get(get::tracking_id))
|
||||
.route("/:id", get(get::id))
|
||||
.route("/", post(post::create_link))
|
||||
.layer(axum::Extension(state));
|
||||
let address = env::var("CHELA_LISTEN_ADDRESS").unwrap_or("0.0.0.0".to_string());
|
||||
@ -110,8 +122,10 @@ async fn serve(state: ServerState) -> eyre::Result<()> {
|
||||
} else {
|
||||
let router = Router::new()
|
||||
.route("/", get(get::index))
|
||||
.route("/:id", get(get::id_unix))
|
||||
.route("/create", get(get::create_id))
|
||||
.route("/tracking", get(get::tracking))
|
||||
.route("/tracking/:id", get(get::tracking_id))
|
||||
.route("/:id", get(get::id_unix))
|
||||
.route("/", post(post::create_link))
|
||||
.layer(axum::Extension(state));
|
||||
let unix_socket_path = std::path::Path::new(&unix_socket);
|
||||
@ -184,7 +198,7 @@ CREATE TABLE IF NOT EXISTS chela.urls (
|
||||
sqlx::query(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS chela.tracking (
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
id TEXT NOT NULL,
|
||||
ip TEXT,
|
||||
referrer TEXT,
|
||||
|
Loading…
Reference in New Issue
Block a user