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
handle timestamps
ericwood.org
5 months ago
422f425f
e55c21e8
+273
-6
7 changed files
expand all
collapse all
unified
split
Cargo.lock
Cargo.toml
src
date_time.rs
db.rs
main.rs
photos.rs
templates
photos
index.jinja
+187
Cargo.lock
···
24
24
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
25
25
26
26
[[package]]
27
27
+
name = "android_system_properties"
28
28
+
version = "0.1.5"
29
29
+
source = "registry+https://github.com/rust-lang/crates.io-index"
30
30
+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
31
31
+
dependencies = [
32
32
+
"libc",
33
33
+
]
34
34
+
35
35
+
[[package]]
27
36
name = "anyhow"
28
37
version = "1.0.100"
29
38
source = "registry+https://github.com/rust-lang/crates.io-index"
···
148
157
]
149
158
150
159
[[package]]
160
160
+
name = "bumpalo"
161
161
+
version = "3.19.0"
162
162
+
source = "registry+https://github.com/rust-lang/crates.io-index"
163
163
+
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
164
164
+
165
165
+
[[package]]
151
166
name = "byteorder"
152
167
version = "1.5.0"
153
168
source = "registry+https://github.com/rust-lang/crates.io-index"
···
176
191
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
177
192
178
193
[[package]]
194
194
+
name = "chrono"
195
195
+
version = "0.4.42"
196
196
+
source = "registry+https://github.com/rust-lang/crates.io-index"
197
197
+
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
198
198
+
dependencies = [
199
199
+
"iana-time-zone",
200
200
+
"js-sys",
201
201
+
"num-traits",
202
202
+
"wasm-bindgen",
203
203
+
"windows-link",
204
204
+
]
205
205
+
206
206
+
[[package]]
179
207
name = "concurrent-queue"
180
208
version = "2.5.0"
181
209
source = "registry+https://github.com/rust-lang/crates.io-index"
···
189
217
version = "0.9.6"
190
218
source = "registry+https://github.com/rust-lang/crates.io-index"
191
219
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
220
220
+
221
221
+
[[package]]
222
222
+
name = "core-foundation-sys"
223
223
+
version = "0.8.7"
224
224
+
source = "registry+https://github.com/rust-lang/crates.io-index"
225
225
+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
192
226
193
227
[[package]]
194
228
name = "cpufeatures"
···
614
648
]
615
649
616
650
[[package]]
651
651
+
name = "iana-time-zone"
652
652
+
version = "0.1.64"
653
653
+
source = "registry+https://github.com/rust-lang/crates.io-index"
654
654
+
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
655
655
+
dependencies = [
656
656
+
"android_system_properties",
657
657
+
"core-foundation-sys",
658
658
+
"iana-time-zone-haiku",
659
659
+
"js-sys",
660
660
+
"log",
661
661
+
"wasm-bindgen",
662
662
+
"windows-core",
663
663
+
]
664
664
+
665
665
+
[[package]]
666
666
+
name = "iana-time-zone-haiku"
667
667
+
version = "0.1.2"
668
668
+
source = "registry+https://github.com/rust-lang/crates.io-index"
669
669
+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
670
670
+
dependencies = [
671
671
+
"cc",
672
672
+
]
673
673
+
674
674
+
[[package]]
617
675
name = "icu_collections"
618
676
version = "2.0.0"
619
677
source = "registry+https://github.com/rust-lang/crates.io-index"
···
748
806
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
749
807
750
808
[[package]]
809
809
+
name = "js-sys"
810
810
+
version = "0.3.81"
811
811
+
source = "registry+https://github.com/rust-lang/crates.io-index"
812
812
+
checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
813
813
+
dependencies = [
814
814
+
"once_cell",
815
815
+
"wasm-bindgen",
816
816
+
]
817
817
+
818
818
+
[[package]]
751
819
name = "lazy_static"
752
820
version = "1.5.0"
753
821
source = "registry+https://github.com/rust-lang/crates.io-index"
···
998
1066
dependencies = [
999
1067
"anyhow",
1000
1068
"axum",
1069
1069
+
"chrono",
1001
1070
"dotenv",
1002
1071
"minijinja",
1003
1072
"serde",
···
1145
1214
version = "0.1.26"
1146
1215
source = "registry+https://github.com/rust-lang/crates.io-index"
1147
1216
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
1217
1217
+
1218
1218
+
[[package]]
1219
1219
+
name = "rustversion"
1220
1220
+
version = "1.0.22"
1221
1221
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1222
1222
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
1148
1223
1149
1224
[[package]]
1150
1225
name = "ryu"
···
1817
1892
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
1818
1893
1819
1894
[[package]]
1895
1895
+
name = "wasm-bindgen"
1896
1896
+
version = "0.2.104"
1897
1897
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1898
1898
+
checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
1899
1899
+
dependencies = [
1900
1900
+
"cfg-if",
1901
1901
+
"once_cell",
1902
1902
+
"rustversion",
1903
1903
+
"wasm-bindgen-macro",
1904
1904
+
"wasm-bindgen-shared",
1905
1905
+
]
1906
1906
+
1907
1907
+
[[package]]
1908
1908
+
name = "wasm-bindgen-backend"
1909
1909
+
version = "0.2.104"
1910
1910
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1911
1911
+
checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
1912
1912
+
dependencies = [
1913
1913
+
"bumpalo",
1914
1914
+
"log",
1915
1915
+
"proc-macro2",
1916
1916
+
"quote",
1917
1917
+
"syn",
1918
1918
+
"wasm-bindgen-shared",
1919
1919
+
]
1920
1920
+
1921
1921
+
[[package]]
1922
1922
+
name = "wasm-bindgen-macro"
1923
1923
+
version = "0.2.104"
1924
1924
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1925
1925
+
checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
1926
1926
+
dependencies = [
1927
1927
+
"quote",
1928
1928
+
"wasm-bindgen-macro-support",
1929
1929
+
]
1930
1930
+
1931
1931
+
[[package]]
1932
1932
+
name = "wasm-bindgen-macro-support"
1933
1933
+
version = "0.2.104"
1934
1934
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1935
1935
+
checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
1936
1936
+
dependencies = [
1937
1937
+
"proc-macro2",
1938
1938
+
"quote",
1939
1939
+
"syn",
1940
1940
+
"wasm-bindgen-backend",
1941
1941
+
"wasm-bindgen-shared",
1942
1942
+
]
1943
1943
+
1944
1944
+
[[package]]
1945
1945
+
name = "wasm-bindgen-shared"
1946
1946
+
version = "0.2.104"
1947
1947
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1948
1948
+
checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
1949
1949
+
dependencies = [
1950
1950
+
"unicode-ident",
1951
1951
+
]
1952
1952
+
1953
1953
+
[[package]]
1820
1954
name = "whoami"
1821
1955
version = "1.6.1"
1822
1956
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1827
1961
]
1828
1962
1829
1963
[[package]]
1964
1964
+
name = "windows-core"
1965
1965
+
version = "0.62.2"
1966
1966
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1967
1967
+
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
1968
1968
+
dependencies = [
1969
1969
+
"windows-implement",
1970
1970
+
"windows-interface",
1971
1971
+
"windows-link",
1972
1972
+
"windows-result",
1973
1973
+
"windows-strings",
1974
1974
+
]
1975
1975
+
1976
1976
+
[[package]]
1977
1977
+
name = "windows-implement"
1978
1978
+
version = "0.60.2"
1979
1979
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1980
1980
+
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
1981
1981
+
dependencies = [
1982
1982
+
"proc-macro2",
1983
1983
+
"quote",
1984
1984
+
"syn",
1985
1985
+
]
1986
1986
+
1987
1987
+
[[package]]
1988
1988
+
name = "windows-interface"
1989
1989
+
version = "0.59.3"
1990
1990
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1991
1991
+
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
1992
1992
+
dependencies = [
1993
1993
+
"proc-macro2",
1994
1994
+
"quote",
1995
1995
+
"syn",
1996
1996
+
]
1997
1997
+
1998
1998
+
[[package]]
1830
1999
name = "windows-link"
1831
2000
version = "0.2.1"
1832
2001
source = "registry+https://github.com/rust-lang/crates.io-index"
1833
2002
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
2003
2003
+
2004
2004
+
[[package]]
2005
2005
+
name = "windows-result"
2006
2006
+
version = "0.4.1"
2007
2007
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2008
2008
+
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
2009
2009
+
dependencies = [
2010
2010
+
"windows-link",
2011
2011
+
]
2012
2012
+
2013
2013
+
[[package]]
2014
2014
+
name = "windows-strings"
2015
2015
+
version = "0.5.1"
2016
2016
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2017
2017
+
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
2018
2018
+
dependencies = [
2019
2019
+
"windows-link",
2020
2020
+
]
1834
2021
1835
2022
[[package]]
1836
2023
name = "windows-sys"
+1
Cargo.toml
···
6
6
[dependencies]
7
7
anyhow = "1.0.100"
8
8
axum = "0.8.6"
9
9
+
chrono = "0.4.42"
9
10
dotenv = "0.15.0"
10
11
minijinja = { version = "2.12.0", features = ["loader"] }
11
12
serde = "1.0.228"
+69
src/date_time.rs
···
1
1
+
use std::{convert, fmt, str};
2
2
+
3
3
+
use chrono::{self, FixedOffset, ParseError};
4
4
+
use serde::{
5
5
+
Deserialize, Deserializer, Serialize, Serializer,
6
6
+
de::{self, Visitor},
7
7
+
};
8
8
+
9
9
+
pub struct DateTime(chrono::DateTime<FixedOffset>);
10
10
+
11
11
+
impl convert::TryFrom<String> for DateTime {
12
12
+
type Error = ParseError;
13
13
+
14
14
+
fn try_from(value: String) -> Result<Self, ParseError> {
15
15
+
let dt = chrono::DateTime::parse_from_rfc3339(&value)?;
16
16
+
Ok(DateTime(dt))
17
17
+
}
18
18
+
}
19
19
+
20
20
+
impl convert::From<DateTime> for String {
21
21
+
fn from(value: DateTime) -> String {
22
22
+
value.0.to_rfc3339()
23
23
+
}
24
24
+
}
25
25
+
26
26
+
impl convert::From<&DateTime> for String {
27
27
+
fn from(value: &DateTime) -> String {
28
28
+
value.0.to_rfc3339()
29
29
+
}
30
30
+
}
31
31
+
32
32
+
struct DateTimeVisitor;
33
33
+
34
34
+
impl<'de> Visitor<'de> for DateTimeVisitor {
35
35
+
type Value = DateTime;
36
36
+
37
37
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
38
38
+
formatter.write_str("an ISO 8601 string")
39
39
+
}
40
40
+
41
41
+
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
42
42
+
where
43
43
+
E: de::Error,
44
44
+
{
45
45
+
match chrono::DateTime::parse_from_rfc3339(value) {
46
46
+
Ok(dt) => Ok(DateTime(dt)),
47
47
+
Err(_) => Err(E::custom(format!("unable to parse DateTime: {value}"))),
48
48
+
}
49
49
+
}
50
50
+
}
51
51
+
52
52
+
impl Serialize for DateTime {
53
53
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
54
54
+
where
55
55
+
S: Serializer,
56
56
+
{
57
57
+
let result: String = self.into();
58
58
+
serializer.serialize_str(&result)
59
59
+
}
60
60
+
}
61
61
+
62
62
+
impl<'de> Deserialize<'de> for DateTime {
63
63
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64
64
+
where
65
65
+
D: Deserializer<'de>,
66
66
+
{
67
67
+
deserializer.deserialize_str(DateTimeVisitor)
68
68
+
}
69
69
+
}
+5
-3
src/db.rs
···
2
2
use sqlx::SqlitePool;
3
3
4
4
pub async fn get_photos(pool: &SqlitePool) -> anyhow::Result<Vec<Photo>> {
5
5
-
let photos = sqlx::query_as::<_, Photo>("SELECT id, caption, filename FROM photos")
6
6
-
.fetch_all(pool)
7
7
-
.await?;
5
5
+
let photos = sqlx::query_as::<_, Photo>(
6
6
+
"SELECT id, caption, filename, taken_at, created_at FROM photos",
7
7
+
)
8
8
+
.fetch_all(pool)
9
9
+
.await?;
8
10
9
11
Ok(photos)
10
12
}
+1
src/main.rs
···
4
4
use std::{env, sync::Arc};
5
5
mod app_error;
6
6
use app_error::AppError;
7
7
+
mod date_time;
7
8
mod db;
8
9
mod photos;
9
10
mod templates;
+6
-2
src/photos.rs
···
1
1
+
use crate::date_time::DateTime;
2
2
+
1
3
use serde::Serialize;
2
4
use sqlx::FromRow;
3
5
···
6
8
pub id: String,
7
9
pub caption: String,
8
10
pub filename: String,
9
9
-
// pub taken_at TEXT,
10
10
-
// pub created_at TEXT
11
11
+
#[sqlx(try_from = "String")]
12
12
+
pub taken_at: DateTime,
13
13
+
#[sqlx(try_from = "String")]
14
14
+
pub created_at: DateTime,
11
15
}
+4
-1
templates/photos/index.jinja
···
2
2
{% block title %}Photos{% endblock %}
3
3
{% block body %}
4
4
{% for photo in photos %}
5
5
-
<img src="/thumbnails/{{ photo.id }}.webp">
5
5
+
<div>
6
6
+
<p>{{ photo.taken_at }}</p>
7
7
+
<img src="/thumbnails/{{ photo.id }}.webp">
8
8
+
</div>
6
9
{% endfor %}
7
10
{% endblock %}