Compare commits
2 commits
9c5d3ef58e
...
ff8f38c563
Author | SHA1 | Date | |
---|---|---|---|
Adrian Hedqvist | ff8f38c563 | ||
Adrian Hedqvist | 0d9115749b |
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -50,12 +50,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.3.15"
|
||||
|
@ -2085,7 +2079,6 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
|||
name = "website"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"cached",
|
||||
"chrono",
|
||||
|
|
|
@ -6,13 +6,11 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.70"
|
||||
axum = { version = "0.6.12", features = ["http2"] }
|
||||
cached = "0.42.0"
|
||||
chrono = { version = "0.4.24", features = ["serde"] }
|
||||
color-eyre = "0.6.1"
|
||||
glob = "0.3.0"
|
||||
#grass = { version = "0.12.3", features = ["random"] } # not really needed yet
|
||||
hyper = { version = "0.14.19", features = ["full"] }
|
||||
lazy_static = "1.4.0"
|
||||
prometheus = { version = "0.13.3", features = ["process"] }
|
||||
|
|
21
Dockerfile
21
Dockerfile
|
@ -1,12 +1,15 @@
|
|||
FROM rust:slim AS chef
|
||||
RUN cargo install cargo-chef
|
||||
WORKDIR app
|
||||
WORKDIR /app
|
||||
|
||||
####################################################################################################
|
||||
## Planner
|
||||
####################################################################################################
|
||||
FROM chef AS planner
|
||||
COPY . .
|
||||
WORKDIR /app
|
||||
COPY ./Cargo.lock ./
|
||||
COPY ./Cargo.toml ./
|
||||
COPY ./src ./src
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
####################################################################################################
|
||||
|
@ -33,10 +36,14 @@ RUN adduser \
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=planner /app/recipe.json .
|
||||
COPY --from=planner /app/recipe.json ./
|
||||
|
||||
RUN cargo chef cook --target x86_64-unknown-linux-musl --release --recipe-path recipe.json
|
||||
|
||||
COPY . .
|
||||
COPY ./Cargo.lock ./
|
||||
COPY ./Cargo.toml ./
|
||||
|
||||
COPY ./src ./src
|
||||
|
||||
RUN cargo build --target x86_64-unknown-linux-musl --release
|
||||
|
||||
|
@ -53,9 +60,9 @@ WORKDIR /app
|
|||
|
||||
# Copy our build
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/website ./
|
||||
COPY --from=builder /app/posts ./posts
|
||||
COPY --from=builder /app/static ./static
|
||||
COPY --from=builder /app/templates ./templates
|
||||
COPY ./static ./static
|
||||
COPY ./templates ./templates
|
||||
COPY ./posts ./posts
|
||||
|
||||
EXPOSE 8180
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
+++
|
||||
title="TOML metadata test"
|
||||
date=2023-03-25T14:50:25+01:00
|
||||
date=2023-03-26T11:57:00+02:00
|
||||
+++
|
||||
|
||||
hope it works yay
|
||||
|
||||
here have a squid miku to test relative paths:
|
||||
here have a squid kid miku to test relative paths:
|
||||
|
||||
![Squid kid miku](FbHSmoeUUAA2x-m.png)
|
||||
|
||||
modified post test, see if docker skips build using
|
||||
its cache if only a post has changed.
|
||||
|
|
|
@ -13,8 +13,11 @@ use crate::{State, WebsiteError};
|
|||
pub mod posts;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref HIT_COUNTER: IntCounterVec =
|
||||
prometheus::register_int_counter_vec!(opts!("page_hits", "Number of hits to various pages"), &["page"]).unwrap();
|
||||
pub static ref HIT_COUNTER: IntCounterVec = prometheus::register_int_counter_vec!(
|
||||
opts!("page_hits", "Number of hits to various pages"),
|
||||
&["page"]
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
|
@ -30,6 +33,10 @@ pub async fn index(
|
|||
Ok(Html(res.into()))
|
||||
}
|
||||
|
||||
pub async fn not_found() -> Response {
|
||||
(StatusCode::NOT_FOUND, ()).into_response()
|
||||
}
|
||||
|
||||
impl IntoResponse for WebsiteError {
|
||||
fn into_response(self) -> Response {
|
||||
match self {
|
||||
|
|
|
@ -15,23 +15,10 @@ pub fn router() -> Router {
|
|||
Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/:slug/", get(view))
|
||||
.route("/:slug/index.md", get(super::not_found))
|
||||
.fallback_service(tower_http::services::ServeDir::new("./posts"))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
pub async fn view(
|
||||
Path(slug): Path<String>,
|
||||
Extension(state): Extension<Arc<State>>,
|
||||
) -> Result<Html<String>, WebsiteError> {
|
||||
debug!("viewing post: {slug}");
|
||||
let post = state.posts.get(&slug).ok_or(WebsiteError::NotFound)?;
|
||||
|
||||
let res = render_post(&state.tera, post).await?;
|
||||
|
||||
HIT_COUNTER.with_label_values(&[&format!("/posts/{}/", slug)]).inc();
|
||||
Ok(Html(res))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct IndexContext<'a> {
|
||||
title: &'a str,
|
||||
|
@ -40,7 +27,11 @@ struct IndexContext<'a> {
|
|||
|
||||
#[instrument(skip(state))]
|
||||
pub async fn index(Extension(state): Extension<Arc<State>>) -> Result<Html<String>, WebsiteError> {
|
||||
let mut posts = state.posts.values().collect::<Vec<&Post>>();
|
||||
let mut posts = state
|
||||
.posts
|
||||
.values()
|
||||
.filter(|p| p.is_published())
|
||||
.collect::<Vec<&Post>>();
|
||||
|
||||
posts.sort_by_key(|p| &p.date);
|
||||
posts.reverse();
|
||||
|
@ -64,3 +55,22 @@ pub async fn index(Extension(state): Extension<Arc<State>>) -> Result<Html<Strin
|
|||
HIT_COUNTER.with_label_values(&["/posts/"]).inc();
|
||||
Ok(Html(res))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
pub async fn view(
|
||||
Path(slug): Path<String>,
|
||||
Extension(state): Extension<Arc<State>>,
|
||||
) -> Result<Html<String>, WebsiteError> {
|
||||
let post = state.posts.get(&slug).ok_or(WebsiteError::NotFound)?;
|
||||
if !post.is_published() {
|
||||
warn!("attempted to view post before it has been published!");
|
||||
return Err(WebsiteError::NotFound);
|
||||
}
|
||||
|
||||
let res = render_post(&state.tera, post).await?;
|
||||
|
||||
HIT_COUNTER
|
||||
.with_label_values(&[&format!("/posts/{slug}/")])
|
||||
.inc();
|
||||
Ok(Html(res))
|
||||
}
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -1,10 +1,10 @@
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use axum::{routing::get, Extension, Router, response::Response, body};
|
||||
use color_eyre::eyre::Result;
|
||||
use axum::{body, response::Response, routing::get, Extension, Router};
|
||||
use color_eyre::eyre::{Result, Error};
|
||||
use hyper::header::CONTENT_TYPE;
|
||||
use post::Post;
|
||||
use prometheus::{TextEncoder, Encoder};
|
||||
use prometheus::{Encoder, TextEncoder};
|
||||
use tera::Tera;
|
||||
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
|
||||
use tracing::{instrument, log::*};
|
||||
|
@ -50,7 +50,7 @@ pub async fn init_app() -> Result<Router> {
|
|||
.route("/", get(handlers::index))
|
||||
.nest("/posts", handlers::posts::router())
|
||||
.nest_service("/static", tower_http::services::ServeDir::new("./static"))
|
||||
.route("/.healthcheck", get(healthcheck))
|
||||
.route("/healthcheck", get(healthcheck))
|
||||
.route("/metrics", get(metrics))
|
||||
.layer(middleware);
|
||||
|
||||
|
@ -76,12 +76,12 @@ async fn metrics() -> Response {
|
|||
#[derive(Debug)]
|
||||
pub enum WebsiteError {
|
||||
NotFound,
|
||||
InternalError(anyhow::Error),
|
||||
InternalError(Error),
|
||||
}
|
||||
|
||||
impl<E> From<E> for WebsiteError
|
||||
where
|
||||
E: Into<anyhow::Error>,
|
||||
E: Into<Error>,
|
||||
{
|
||||
fn from(value: E) -> Self {
|
||||
WebsiteError::InternalError(value.into())
|
||||
|
|
|
@ -47,6 +47,14 @@ impl Post {
|
|||
tags: fm.tags.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_published(&self) -> bool {
|
||||
let now = chrono::offset::Local::now();
|
||||
if let Some(date) = self.date {
|
||||
return date.timestamp() - now.timestamp() <= 0;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
|
|
Loading…
Reference in a new issue