tangled
alpha
login
or
join now
ericwood.org
/
photos-site
1
fork
atom
A little app to serve my photography from my personal website
1
fork
atom
overview
issues
pulls
pipelines
additional queries
ericwood.org
5 months ago
fea82ec7
85cb1e9d
+59
-4
5 changed files
expand all
collapse all
unified
split
src
db.rs
main.rs
models.rs
templates
photos
index.jinja
show.jinja
+18
-1
src/db.rs
···
1
1
-
use crate::Photo;
1
1
+
use crate::{Photo, Tag};
2
2
use sqlx::{QueryBuilder, Sqlite, SqlitePool, query::QueryAs};
3
3
4
4
pub enum SortDirection {
···
81
81
82
82
Ok(photos)
83
83
}
84
84
+
85
85
+
pub async fn get_photo(pool: &SqlitePool, id: String) -> anyhow::Result<Photo> {
86
86
+
let photo: Photo = sqlx::query_as("SELECT * FROM photos WHERE id = ?")
87
87
+
.bind(id)
88
88
+
.fetch_one(pool)
89
89
+
.await?;
90
90
+
91
91
+
Ok(photo)
92
92
+
}
93
93
+
94
94
+
pub async fn get_tags(pool: &SqlitePool) -> anyhow::Result<Vec<Tag>> {
95
95
+
let tags: Vec<Tag> = sqlx::query_as("SELECT * FROM tags ORDER BY count DESC")
96
96
+
.fetch_all(pool)
97
97
+
.await?;
98
98
+
99
99
+
Ok(tags)
100
100
+
}
+25
-3
src/main.rs
···
1
1
-
use axum::{Router, extract::State, response::Html, routing::get};
1
1
+
use axum::{
2
2
+
Router,
3
3
+
extract::{Path, State},
4
4
+
response::Html,
5
5
+
routing::get,
6
6
+
};
2
7
use dotenv::dotenv;
3
8
use minijinja::{Environment, context};
4
9
use std::{env, sync::Arc};
···
6
11
use app_error::AppError;
7
12
mod date_time;
8
13
mod db;
9
9
-
mod photos;
14
14
+
mod models;
10
15
mod templates;
11
11
-
use photos::Photo;
16
16
+
use models::{Photo, Tag};
12
17
use sqlx::SqlitePool;
13
18
use templates::load_templates;
14
19
use tower_http::services::ServeDir;
···
33
38
},
34
39
)
35
40
.await?;
41
41
+
42
42
+
let tags = db::get_tags(&state.pool).await?;
36
43
let template = state.template_env.get_template("photos/index")?;
37
44
let rendered = template.render(context! {
38
45
photos => photos,
46
46
+
tags => tags,
47
47
+
})?;
48
48
+
49
49
+
Ok(Html(rendered))
50
50
+
}
51
51
+
52
52
+
async fn show(
53
53
+
Path(id): Path<String>,
54
54
+
State(state): State<Arc<AppState>>,
55
55
+
) -> Result<Html<String>, AppError> {
56
56
+
let photo = db::get_photo(&state.pool, id).await?;
57
57
+
let template = state.template_env.get_template("photos/show")?;
58
58
+
let rendered = template.render(context! {
59
59
+
photo => photo
39
60
})?;
40
61
41
62
Ok(Html(rendered))
···
52
73
let app_state = Arc::new(AppState { template_env, pool });
53
74
let app = Router::new()
54
75
.route("/", get(root))
76
76
+
.route("/{id}", get(show))
55
77
.with_state(app_state)
56
78
.nest_service("/thumbnails", ServeDir::new(&env::var("THUMBNAIL_PATH")?))
57
79
.nest_service("/images", ServeDir::new(&env::var("IMAGE_PATH")?));
+6
src/photos.rs
src/models.rs
···
13
13
#[sqlx(try_from = "String")]
14
14
pub created_at: DateTime,
15
15
}
16
16
+
17
17
+
#[derive(FromRow, Serialize)]
18
18
+
pub struct Tag {
19
19
+
pub name: String,
20
20
+
pub count: u32,
21
21
+
}
+5
templates/photos/index.jinja
···
1
1
{% extends "layout" %}
2
2
{% block title %}Photos{% endblock %}
3
3
{% block body %}
4
4
+
<ul>
5
5
+
{% for tag in tags %}
6
6
+
<li>{{ tag.name }} ({{ tag.count }})</li>
7
7
+
{% endfor %}
8
8
+
</ul>
4
9
{% for photo in photos %}
5
10
<div>
6
11
<p>{{ photo.taken_at }}</p>
+5
templates/photos/show.jinja
···
1
1
+
{% extends "layout" %}
2
2
+
{% block title %}Photos / View{% endblock %}
3
3
+
{% block body %}
4
4
+
<img src="/images/{{ photo.filename }}">
5
5
+
{% endblock %}