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
basic validation
ericwood.org
4 months ago
bd8c773a
e9eeda00
0/0
Waiting for spindle ...
+174
-8
5 changed files
expand all
collapse all
unified
split
Cargo.lock
Cargo.toml
src
app_error.rs
routes
photos
index.rs
templates
photos
index.jinja
+160
-6
Cargo.lock
···
3
3
version = 4
4
4
5
5
[[package]]
6
6
+
name = "aho-corasick"
7
7
+
version = "1.1.4"
8
8
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9
9
+
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10
10
+
dependencies = [
11
11
+
"memchr",
12
12
+
]
13
13
+
14
14
+
[[package]]
6
15
name = "allocator-api2"
7
16
version = "0.2.21"
8
17
source = "registry+https://github.com/rust-lang/crates.io-index"
···
802
811
dependencies = [
803
812
"equivalent",
804
813
"hashbrown 0.16.0",
814
814
+
"serde",
815
815
+
"serde_core",
805
816
]
806
817
807
818
[[package]]
···
822
833
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
823
834
dependencies = [
824
835
"libc",
836
836
+
]
837
837
+
838
838
+
[[package]]
839
839
+
name = "itertools"
840
840
+
version = "0.14.0"
841
841
+
source = "registry+https://github.com/rust-lang/crates.io-index"
842
842
+
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
843
843
+
dependencies = [
844
844
+
"either",
825
845
]
826
846
827
847
[[package]]
···
1108
1128
]
1109
1129
1110
1130
[[package]]
1131
1131
+
name = "paste"
1132
1132
+
version = "1.0.15"
1133
1133
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1134
1134
+
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
1135
1135
+
1136
1136
+
[[package]]
1111
1137
name = "pem-rfc7468"
1112
1138
version = "0.7.0"
1113
1139
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1135
1161
"minijinja-autoreload",
1136
1162
"serde",
1137
1163
"serde_html_form",
1164
1164
+
"serde_valid",
1138
1165
"sqlx",
1139
1139
-
"thiserror",
1166
1166
+
"thiserror 2.0.17",
1140
1167
"tokio",
1141
1168
"tower-http",
1142
1169
"urlencoding",
···
1200
1227
]
1201
1228
1202
1229
[[package]]
1230
1230
+
name = "proc-macro-error-attr2"
1231
1231
+
version = "2.0.0"
1232
1232
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1233
1233
+
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
1234
1234
+
dependencies = [
1235
1235
+
"proc-macro2",
1236
1236
+
"quote",
1237
1237
+
]
1238
1238
+
1239
1239
+
[[package]]
1240
1240
+
name = "proc-macro-error2"
1241
1241
+
version = "2.0.1"
1242
1242
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1243
1243
+
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
1244
1244
+
dependencies = [
1245
1245
+
"proc-macro-error-attr2",
1246
1246
+
"proc-macro2",
1247
1247
+
"quote",
1248
1248
+
]
1249
1249
+
1250
1250
+
[[package]]
1203
1251
name = "proc-macro2"
1204
1252
version = "1.0.101"
1205
1253
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1255
1303
dependencies = [
1256
1304
"bitflags 2.9.4",
1257
1305
]
1306
1306
+
1307
1307
+
[[package]]
1308
1308
+
name = "regex"
1309
1309
+
version = "1.12.2"
1310
1310
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1311
1311
+
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
1312
1312
+
dependencies = [
1313
1313
+
"aho-corasick",
1314
1314
+
"memchr",
1315
1315
+
"regex-automata",
1316
1316
+
"regex-syntax",
1317
1317
+
]
1318
1318
+
1319
1319
+
[[package]]
1320
1320
+
name = "regex-automata"
1321
1321
+
version = "0.4.13"
1322
1322
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1323
1323
+
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
1324
1324
+
dependencies = [
1325
1325
+
"aho-corasick",
1326
1326
+
"memchr",
1327
1327
+
"regex-syntax",
1328
1328
+
]
1329
1329
+
1330
1330
+
[[package]]
1331
1331
+
name = "regex-syntax"
1332
1332
+
version = "0.8.8"
1333
1333
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1334
1334
+
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
1258
1335
1259
1336
[[package]]
1260
1337
name = "rsa"
···
1389
1466
]
1390
1467
1391
1468
[[package]]
1469
1469
+
name = "serde_valid"
1470
1470
+
version = "2.0.0"
1471
1471
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1472
1472
+
checksum = "de7f50148816c0fbeab7089476c91dbfae94bd891cc4e2c535f30b54ceb90ad2"
1473
1473
+
dependencies = [
1474
1474
+
"indexmap",
1475
1475
+
"itertools",
1476
1476
+
"num-traits",
1477
1477
+
"once_cell",
1478
1478
+
"paste",
1479
1479
+
"regex",
1480
1480
+
"serde",
1481
1481
+
"serde_json",
1482
1482
+
"serde_valid_derive",
1483
1483
+
"serde_valid_literal",
1484
1484
+
"thiserror 1.0.69",
1485
1485
+
"unicode-segmentation",
1486
1486
+
]
1487
1487
+
1488
1488
+
[[package]]
1489
1489
+
name = "serde_valid_derive"
1490
1490
+
version = "2.0.0"
1491
1491
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1492
1492
+
checksum = "c030af8a53ec37caf1ed98e5ce02cf7d4f65e637e8eaa089ed785689e86c9046"
1493
1493
+
dependencies = [
1494
1494
+
"itertools",
1495
1495
+
"paste",
1496
1496
+
"proc-macro-error2",
1497
1497
+
"proc-macro2",
1498
1498
+
"quote",
1499
1499
+
"strsim",
1500
1500
+
"syn",
1501
1501
+
]
1502
1502
+
1503
1503
+
[[package]]
1504
1504
+
name = "serde_valid_literal"
1505
1505
+
version = "2.0.0"
1506
1506
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1507
1507
+
checksum = "50b2fb68eefc69006cebe74b0f672ae68464c72b150dacd00d0877227c1300f7"
1508
1508
+
dependencies = [
1509
1509
+
"paste",
1510
1510
+
"regex",
1511
1511
+
]
1512
1512
+
1513
1513
+
[[package]]
1392
1514
name = "sha1"
1393
1515
version = "0.10.6"
1394
1516
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1510
1632
"serde_json",
1511
1633
"sha2",
1512
1634
"smallvec",
1513
1513
-
"thiserror",
1635
1635
+
"thiserror 2.0.17",
1514
1636
"tokio",
1515
1637
"tokio-stream",
1516
1638
"tracing",
···
1592
1714
"smallvec",
1593
1715
"sqlx-core",
1594
1716
"stringprep",
1595
1595
-
"thiserror",
1717
1717
+
"thiserror 2.0.17",
1596
1718
"tracing",
1597
1719
"whoami",
1598
1720
]
···
1629
1751
"smallvec",
1630
1752
"sqlx-core",
1631
1753
"stringprep",
1632
1632
-
"thiserror",
1754
1754
+
"thiserror 2.0.17",
1633
1755
"tracing",
1634
1756
"whoami",
1635
1757
]
···
1653
1775
"serde",
1654
1776
"serde_urlencoded",
1655
1777
"sqlx-core",
1656
1656
-
"thiserror",
1778
1778
+
"thiserror 2.0.17",
1657
1779
"tracing",
1658
1780
"url",
1659
1781
]
···
1676
1798
]
1677
1799
1678
1800
[[package]]
1801
1801
+
name = "strsim"
1802
1802
+
version = "0.11.1"
1803
1803
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1804
1804
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
1805
1805
+
1806
1806
+
[[package]]
1679
1807
name = "subtle"
1680
1808
version = "2.6.1"
1681
1809
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1711
1839
1712
1840
[[package]]
1713
1841
name = "thiserror"
1842
1842
+
version = "1.0.69"
1843
1843
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1844
1844
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
1845
1845
+
dependencies = [
1846
1846
+
"thiserror-impl 1.0.69",
1847
1847
+
]
1848
1848
+
1849
1849
+
[[package]]
1850
1850
+
name = "thiserror"
1714
1851
version = "2.0.17"
1715
1852
source = "registry+https://github.com/rust-lang/crates.io-index"
1716
1853
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
1717
1854
dependencies = [
1718
1718
-
"thiserror-impl",
1855
1855
+
"thiserror-impl 2.0.17",
1856
1856
+
]
1857
1857
+
1858
1858
+
[[package]]
1859
1859
+
name = "thiserror-impl"
1860
1860
+
version = "1.0.69"
1861
1861
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1862
1862
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
1863
1863
+
dependencies = [
1864
1864
+
"proc-macro2",
1865
1865
+
"quote",
1866
1866
+
"syn",
1719
1867
]
1720
1868
1721
1869
[[package]]
···
1928
2076
version = "0.1.3"
1929
2077
source = "registry+https://github.com/rust-lang/crates.io-index"
1930
2078
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
2079
2079
+
2080
2080
+
[[package]]
2081
2081
+
name = "unicode-segmentation"
2082
2082
+
version = "1.12.0"
2083
2083
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2084
2084
+
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
1931
2085
1932
2086
[[package]]
1933
2087
name = "url"
+1
Cargo.toml
···
13
13
minijinja-autoreload = "2.12.0"
14
14
serde = "1.0.228"
15
15
serde_html_form = "0.2.8"
16
16
+
serde_valid = "2.0.0"
16
17
sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite"] }
17
18
thiserror = "2.0.17"
18
19
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
+4
src/app_error.rs
···
11
11
#[error("internal server error")]
12
12
DbError(#[from] sqlx::Error),
13
13
14
14
+
#[error("bad request")]
15
15
+
ValidationError(#[from] serde_valid::validation::Errors),
16
16
+
14
17
#[error("internal server error")]
15
18
Anyhow(#[from] anyhow::Error),
16
19
}
···
19
22
pub fn status_code(&self) -> StatusCode {
20
23
match self {
21
24
Self::NotFound => StatusCode::NOT_FOUND,
25
25
+
Self::ValidationError(_) => StatusCode::BAD_REQUEST,
22
26
Self::DbError(_) | Self::Anyhow(_) => StatusCode::INTERNAL_SERVER_ERROR,
23
27
}
24
28
}
+8
-1
src/routes/photos/index.rs
···
4
4
use axum_extra::extract::Query;
5
5
use minijinja::context;
6
6
use serde::{self, Deserialize, Serialize};
7
7
+
use serde_valid::Validate;
7
8
8
9
use crate::{
9
10
AppError, AppState,
···
32
33
action: String,
33
34
}
34
35
35
35
-
#[derive(Deserialize, Serialize, Clone)]
36
36
+
#[derive(Deserialize, Serialize, Clone, Validate)]
36
37
pub struct IndexParams {
38
38
+
#[validate(minimum = 1)]
37
39
page: Option<u32>,
40
40
+
41
41
+
#[validate(minimum = 1)]
42
42
+
#[validate(maximum = 100)]
38
43
limit: Option<u32>,
44
44
+
39
45
tags: Option<Vec<String>>,
40
46
sort: Option<SortField>,
41
47
dir: Option<SortDirection>,
···
59
65
query: Query<IndexParams>,
60
66
State(state): State<Arc<AppState>>,
61
67
) -> Result<Html<String>, AppError> {
68
68
+
query.validate()?;
62
69
let query = query.0;
63
70
let default = IndexParams::default();
64
71
let page = query.page.unwrap_or(default.page.unwrap());
+1
-1
templates/photos/index.jinja
···
61
61
{% for tag in current_tags %}
62
62
<li class="selected">
63
63
{{ tag.name }}
64
64
-
<a href="/?{{ tag.action }}">
64
64
+
<a href="?{{ tag.action }}">
65
65
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
66
66
<use xlink:href="#close-icon" />
67
67
</svg>