aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/get.rs99
-rw-r--r--src/main.rs102
-rw-r--r--src/post.rs3
3 files changed, 204 insertions, 0 deletions
diff --git a/src/get.rs b/src/get.rs
new file mode 100644
index 0000000..9507125
--- /dev/null
+++ b/src/get.rs
@@ -0,0 +1,99 @@
+use std::net::SocketAddr;
+
+use axum::extract::{ConnectInfo, Path};
+use axum::http::HeaderMap;
+use axum::http::StatusCode;
+use axum::response::{Html, IntoResponse, Redirect};
+use axum::Extension;
+
+use info_utils::prelude::*;
+
+use crate::ServerState;
+use crate::UrlRow;
+
+pub async fn get_index() -> Html<&'static str> {
+ Html("hello, world!")
+}
+
+pub async fn get_id(
+ headers: HeaderMap,
+ ConnectInfo(addr): ConnectInfo<SocketAddr>,
+ Extension(state): Extension<ServerState>,
+ Path(id): Path<String>,
+) -> impl IntoResponse {
+ let mut show_request = false;
+ log!("Request for '{}' from {}", id.clone(), addr.ip());
+ let mut use_id = id;
+ if use_id.ends_with('+') {
+ show_request = true;
+ use_id.pop();
+ }
+
+ let item = sqlx::query_as!(UrlRow, "SELECT * FROM chela.urls WHERE id = $1", use_id)
+ .fetch_one(&state.db_pool)
+ .await;
+ if let Ok(it) = item {
+ if url::Url::parse(&it.url).is_ok() {
+ if show_request {
+ return Html(format!(
+ "<pre>{}/{} -> <a href={}>{}</a></pre>",
+ state.host, it.id, it.url, it.url
+ ))
+ .into_response();
+ } else {
+ log!("Redirecting {} -> {}", it.id, it.url);
+ save_analytics(headers, it.clone(), addr, state).await;
+ return Redirect::temporary(it.url.as_str()).into_response();
+ }
+ }
+ }
+
+ return (StatusCode::NOT_FOUND, Html("<pre>404</pre>")).into_response();
+}
+
+pub async fn save_analytics(
+ headers: HeaderMap,
+ item: UrlRow,
+ addr: SocketAddr,
+ state: ServerState,
+) {
+ let id = item.id;
+ let ip = addr.ip().to_string();
+ let referer = match headers.get("referer") {
+ Some(it) => {
+ if let Ok(i) = it.to_str() {
+ Some(i)
+ } else {
+ None
+ }
+ }
+ None => None,
+ };
+ let user_agent = match headers.get("user-agent") {
+ Some(it) => {
+ if let Ok(i) = it.to_str() {
+ Some(i)
+ } else {
+ None
+ }
+ }
+ None => None,
+ };
+
+ let res = sqlx::query!(
+ "
+INSERT INTO chela.tracking (id,ip,referrer,user_agent)
+VALUES ($1,$2,$3,$4)
+ ",
+ id,
+ ip,
+ referer,
+ user_agent
+ )
+ .execute(&state.db_pool)
+ .await;
+
+ if res.is_ok() {
+ log!("Saved analytics for '{id}' from {ip}");
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..51dd969
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,102 @@
+use std::net::SocketAddr;
+
+use axum::routing::{get, post};
+use axum::Router;
+
+use sqlx::postgres::PgPoolOptions;
+use sqlx::{Pool, Postgres};
+
+use info_utils::prelude::*;
+
+pub mod get;
+pub mod post;
+
+const LISTEN_ADDRESS: &'static str = "0.0.0.0:3000";
+
+#[derive(Clone)]
+pub struct ServerState {
+ pub db_pool: Pool<Postgres>,
+ pub host: String,
+}
+
+#[derive(Debug, Clone)]
+pub struct UrlRow {
+ pub index: i32,
+ pub id: String,
+ pub url: String,
+}
+
+#[tokio::main]
+async fn main() -> eyre::Result<()> {
+ color_eyre::install()?;
+
+ let db_pool = init_db().await?;
+
+ let server_state = ServerState {
+ db_pool,
+ host: "trkt.in".to_string(),
+ };
+
+ let router = init_routes(server_state)?;
+ let listener = tokio::net::TcpListener::bind(LISTEN_ADDRESS).await?;
+ log!("Listening at {}", LISTEN_ADDRESS);
+ axum::serve(
+ listener,
+ router.into_make_service_with_connect_info::<SocketAddr>(),
+ )
+ .await?;
+ Ok(())
+}
+
+async fn init_db() -> eyre::Result<Pool<Postgres>> {
+ let db_pool = PgPoolOptions::new()
+ .max_connections(15)
+ .connect(std::env::var("DATABASE_URL")?.as_str())
+ .await?;
+ log!("Successfully connected to database");
+
+ sqlx::query!("CREATE SCHEMA IF NOT EXISTS chela")
+ .execute(&db_pool)
+ .await?;
+ log!("Created schema chela");
+
+ sqlx::query!(
+ "
+CREATE TABLE IF NOT EXISTS chela.urls (
+ index SERIAL PRIMARY KEY,
+ id TEXT NOT NULL UNIQUE,
+ url TEXT NOT NULL
+)
+ ",
+ )
+ .execute(&db_pool)
+ .await?;
+ log!("Created table chela.urls");
+
+ sqlx::query!(
+ "
+CREATE TABLE IF NOT EXISTS chela.tracking (
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ id TEXT NOT NULL,
+ ip TEXT NOT NULL,
+ referrer TEXT,
+ user_agent TEXT
+)
+ ",
+ )
+ .execute(&db_pool)
+ .await?;
+ log!("Created table chela.tracking");
+
+ Ok(db_pool)
+}
+
+fn init_routes(state: ServerState) -> eyre::Result<Router> {
+ let router = Router::new()
+ .route("/", get(get::get_index))
+ .route("/:id", get(get::get_id))
+ .route("/", post(post::create_link))
+ .layer(axum::Extension(state));
+
+ Ok(router)
+}
diff --git a/src/post.rs b/src/post.rs
new file mode 100644
index 0000000..d3d1257
--- /dev/null
+++ b/src/post.rs
@@ -0,0 +1,3 @@
+pub async fn create_link() {
+
+}