Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ minijinja = "2.0"
serde = { version = "1.0", features = ["serde_derive"], default-features = false }
serde_yaml = "0.9.34"
tokio = { version = "1.37", features = ["rt-multi-thread"] }
tower-http = { version = "0.6.1", features = ["fs"] }
tower-http = { version = "0.6.1", features = ["fs", "normalize-path"] }
rusqlite = { version = "0.32.0", features = ["bundled"] }
tower-livereload = "0.9.5"
tower = "0.5.2"
89 changes: 59 additions & 30 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use axum::extract::{Path, State, Json};
use axum::extract::{Json, Path, Request, State};
use axum::http::{HeaderMap, StatusCode};
use axum::response::{IntoResponse, Redirect};
use axum::{response::Html, routing::{get, post}, Router};
use axum::ServiceExt;
use axum::{
response::Html,
routing::{get, post},
Router,
};
use axum_response_cache::CacheLayer;
use chrono::NaiveDate;
use minijinja::{context, Environment};
use rusqlite::Connection;
Expand All @@ -12,7 +18,8 @@ use std::fs::{self, DirEntry};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;
use axum_response_cache::CacheLayer;
use tower::Layer;
use tower_http::normalize_path::NormalizePathLayer;
#[allow(unused_imports)] // This is only used in debug build
use tower_http::services::ServeDir;
#[allow(unused_imports)] // This is only used in debug build
Expand Down Expand Up @@ -45,7 +52,7 @@ async fn main() {
env,
posts,
page_hits: Default::default(),
db: Mutex::new(get_db())
db: Mutex::new(get_db()),
});

restore_views(app_state.clone());
Expand All @@ -56,11 +63,20 @@ async fn main() {
});
}
let app = Router::new()
.route("/", get(homepage).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)))
.route(
"/",
get(homepage).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)),
)
.route("/about", get(about))
.route("/p/:slug", get(get_posts).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)))
.route(
"/p/:slug",
get(get_posts).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)),
)
.route("/:year/:month/:day/:slug", get(redirect_old_routes))
.route("/feed.xml", get(atom_feed).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)))
.route(
"/feed.xml",
get(atom_feed).layer(CacheLayer::with_lifespan(RESPONSE_CACHE_TTL)),
)
.route("/pageview", post(store_pageview))
.route("/favicon.ico", get(favicon))
.fallback(not_found)
Expand All @@ -72,34 +88,44 @@ async fn main() {
#[cfg(debug_assertions)]
let app = app.layer(LiveReloadLayer::new());

let app = NormalizePathLayer::trim_trailing_slash().layer(app);

let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("Listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
axum::serve(listener, ServiceExt::<Request>::into_make_service(app))
.await
.unwrap();
}

fn get_jenv() -> Environment<'static> {
let mut env = Environment::new();
env.add_template("layout", include_str!("./templates/layout.html")).unwrap();
env.add_template("home", include_str!("./templates/home.html")).unwrap();
env.add_template("post", include_str!("./templates/post.html")).unwrap();
env.add_template("feed", include_str!("./templates/feed.xml")).unwrap();
env.add_template("style", include_str!("./templates/style.css")).unwrap();
env.add_template("pageview", include_str!("./templates/pageview.js")).unwrap();
env.add_template("layout", include_str!("./templates/layout.html"))
.unwrap();
env.add_template("home", include_str!("./templates/home.html"))
.unwrap();
env.add_template("post", include_str!("./templates/post.html"))
.unwrap();
env.add_template("feed", include_str!("./templates/feed.xml"))
.unwrap();
env.add_template("style", include_str!("./templates/style.css"))
.unwrap();
env.add_template("pageview", include_str!("./templates/pageview.js"))
.unwrap();
env.add_function("format_date", format_date);
env.set_trim_blocks(true);
env.set_lstrip_blocks(true);

env
}



fn format_date(date_str: String, short: bool) -> String {
let Ok(date) = NaiveDate::parse_from_str(&date_str, "%Y-%m-%d") else { return date_str };
let Ok(date) = NaiveDate::parse_from_str(&date_str, "%Y-%m-%d") else {
return date_str;
};
if short {
return format!("{}", date.format("%b %d, %Y"))
return format!("{}", date.format("%b %d, %Y"));
} else {
return format!("{}", date.format("%B %d, %Y"))
return format!("{}", date.format("%B %d, %Y"));
}
}

Expand Down Expand Up @@ -229,7 +255,6 @@ async fn atom_feed(State(state): State<Arc<AppState>>) -> impl IntoResponse {
(headers, rendered)
}


#[derive(Deserialize)]
struct Pageview {
path: String,
Expand All @@ -247,26 +272,26 @@ async fn store_pageview(State(state): State<Arc<AppState>>, Json(view): Json<Pag
return ();
}


fn persistence_thread(state: Arc<AppState>) {
loop {
thread::sleep(time::Duration::from_secs(60));
persist_views(state.clone());
}
}


fn persist_views(state: Arc<AppState>) {
let page_hits = state.page_hits.lock().unwrap();
let db = state.db.lock().unwrap();

page_hits.iter().for_each(|(k, v)| {
let _ = db.execute("
let _ = db.execute(
"
INSERT INTO page_hits (post, hits) values (?1, ?2)
ON CONFLICT(post) DO UPDATE SET hits= ?3
", (k, v, v));
",
(k, v, v),
);
})

}

fn restore_views(state: Arc<AppState>) {
Expand All @@ -277,9 +302,9 @@ fn restore_views(state: Arc<AppState>) {

let mut query = db.prepare("select post, hits from page_hits").unwrap();

let stat_iter = query.query_map([], |row| {
Ok(StatRow(row.get(0)?, row.get(1)?))
}).unwrap();
let stat_iter = query
.query_map([], |row| Ok(StatRow(row.get(0)?, row.get(1)?)))
.unwrap();

for stat in stat_iter {
let stat = stat.unwrap();
Expand All @@ -289,11 +314,15 @@ fn restore_views(state: Arc<AppState>) {

fn get_db() -> Connection {
let db = Connection::open(DB_LOCATION).unwrap();
db.execute("
db.execute(
"
CREATE TABLE IF NOT EXISTS page_hits (
post TEXT PRIMARY KEY,
hits INTEGER
)", ()).unwrap();
)",
(),
)
.unwrap();

db
}
2 changes: 1 addition & 1 deletion src/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<li>
<time datetime="{{ post.meta.date }}"> {{ format_date(post.meta.date, true) }} </time>
{%- if post.meta.external_url %}
<a target="_blank" href="{{ post.meta.external_url }}" >{{ post.meta.title }} <span style="transform: rotate(90deg); display: inline-block">⎋</span></a>
<a href="{{ post.meta.external_url }}" >{{ post.meta.title }} <span style="transform: rotate(90deg); display: inline-block">⎋</span></a>
{% else %}
<a href="/p/{{ post.slug }}" >{{ post.meta.title }}</a>
{%- endif %}
Expand Down
8 changes: 4 additions & 4 deletions src/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
<footer>
<nav>
<a href="/">Home</a> ·
<a target="_blank" href="https://github.com/ankush">GitHub</a> ·
<a href="https://github.com/ankush">GitHub</a> ·
<a href="mailto:[email protected]">Email</a> ·
<a target="_blank" href="/assets/resume.pdf">Resume</a> ·
<a target="_blank" href="/feed.xml">Feed</a> ·
<a target="_blank" href="{% block source %}https://github.com/ankush/ankush.dev{% endblock %}">Source</a>
<a href="/assets/resume.pdf">Resume</a> ·
<a href="/feed.xml">Feed</a> ·
<a href="{% block source %}https://github.com/ankush/ankush.dev{% endblock %}">Source</a>
</nav>
</footer>
</main>
Expand Down
2 changes: 1 addition & 1 deletion src/templates/post.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<time datetime={{ post.meta.date }} >{{ format_date(post.meta.date, false) }}</time>
{%- if post.meta.discussions -%}
{% for site, link in post.meta.discussions|items %}
· <a target="_blank" href="{{ link }}">{{ site }}</a>
· <a href="{{ link }}">{{ site }}</a>
{% endfor %}
{%- endif -%}

Expand Down
Loading