Skip to content

Commit eaf6039

Browse files
author
glendc
committed
refactor app, make ready for authentication
1 parent 6f8ab2f commit eaf6039

File tree

14 files changed

+110
-79
lines changed

14 files changed

+110
-79
lines changed

src/main.rs

+4-24
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
1-
use std::path::PathBuf;
2-
3-
use axum::Router;
41
use shuttle_secrets::SecretStore;
5-
use tower::ServiceBuilder;
6-
use tower_http::{
7-
compression::CompressionLayer, normalize_path::NormalizePathLayer, services::ServeDir,
8-
trace::TraceLayer,
9-
};
102

11-
mod auth;
123
mod router;
4+
mod services;
135

146
#[shuttle_runtime::main]
157
async fn axum(#[shuttle_secrets::Secrets] secret_store: SecretStore) -> shuttle_axum::ShuttleAxum {
16-
// TODO:
17-
// - add `authorizer to state` ???
18-
// - make it compile again...
19-
20-
let _auth = auth::Auth::new(
8+
let auth = services::Auth::new(
219
secret_store.get("AUTH_PRIVATE_KEY").unwrap(),
2210
secret_store.get("AUTHORIZED_EMAILS").unwrap(),
2311
secret_store.get("SENDGRID_API_KEY").unwrap(),
2412
);
2513

26-
let router = Router::new()
27-
.nest_service("/static", ServeDir::new(PathBuf::from("static")))
28-
.nest("/", router::new())
29-
.fallback(router::not_found::any)
30-
.layer(
31-
ServiceBuilder::new()
32-
.layer(TraceLayer::new_for_http())
33-
.layer(CompressionLayer::new())
34-
.layer(NormalizePathLayer::trim_trailing_slash()),
35-
);
14+
let state = router::State { auth };
15+
let router = router::new(state);
3616

3717
Ok(router.into())
3818
}

src/router/login.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use askama_axum::{IntoResponse, Response};
2-
use axum::{extract::Query, response::Redirect, Form};
2+
use axum::{extract::Query, http::StatusCode, response::Redirect, Form};
33
use serde::Deserialize;
44

55
#[derive(Deserialize)]
@@ -24,11 +24,14 @@ pub struct PostParams {
2424

2525
pub async fn post(Form(params): Form<PostParams>) -> Response {
2626
if params.email.is_empty() {
27-
return super::shared::ErrorTemplate {
28-
title: "email is required".to_string(),
29-
message: "Please enter your email address.".to_string(),
30-
}
31-
.into_response();
27+
return (
28+
StatusCode::BAD_REQUEST,
29+
super::shared::ErrorTemplate {
30+
title: "email is required".to_string(),
31+
message: "Please enter your email address.".to_string(),
32+
},
33+
)
34+
.into_response();
3235
}
3336
super::shared::InfoTemplate {
3437
title: format!("email sent to {}", params.email),

src/router/memory.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use axum::http::HeaderMap;
2+
3+
pub async fn get_robots_txt() -> &'static str {
4+
r"User-agent: *
5+
Allow: /
6+
Sitemap: https://bckt.xyz/sitemap.xml
7+
"
8+
}
9+
10+
pub async fn get_sitemap_xml() -> impl axum::response::IntoResponse {
11+
let body = r#"<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
12+
<url>
13+
<loc>https://bckt.xyz/</loc>
14+
</url>
15+
</urlset>"#;
16+
17+
let mut headers = HeaderMap::new();
18+
headers.insert("content-type", "application/xml".parse().unwrap());
19+
20+
(headers, body)
21+
}

src/router/mod.rs

+29-25
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,50 @@
1+
use std::{path::PathBuf, sync::Arc};
2+
13
use axum::{
2-
http::HeaderMap,
3-
response::IntoResponse,
44
routing::{get, post},
55
Router,
66
};
7+
use tower::ServiceBuilder;
8+
use tower_http::{
9+
compression::CompressionLayer, normalize_path::NormalizePathLayer, services::ServeDir,
10+
trace::TraceLayer,
11+
};
712

813
mod index;
914
mod link;
1015
mod login;
16+
mod memory;
17+
mod not_found;
1118
mod redirect;
1219
mod shared;
1320

14-
pub mod not_found;
15-
16-
pub async fn get_robots_txt() -> &'static str {
17-
r"User-agent: *
18-
Allow: /
19-
Sitemap: https://bckt.xyz/sitemap.xml
20-
"
21-
}
22-
23-
pub async fn get_sitemap_xml() -> impl IntoResponse {
24-
let body = r#"<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
25-
<url>
26-
<loc>https://bckt.xyz/</loc>
27-
</url>
28-
</urlset>"#;
29-
30-
let mut headers = HeaderMap::new();
31-
headers.insert("content-type", "application/xml".parse().unwrap());
32-
33-
(headers, body)
21+
#[derive(Debug, Clone)]
22+
pub struct State {
23+
pub auth: crate::services::Auth,
3424
}
3525

36-
pub fn new() -> Router {
26+
fn new_root(state: State) -> Router {
3727
Router::new()
3828
.route("/", get(index::get))
39-
.route("/robots.txt", get(get_robots_txt))
40-
.route("/sitemap.xml", get(get_sitemap_xml))
29+
.route("/robots.txt", get(memory::get_robots_txt))
30+
.route("/sitemap.xml", get(memory::get_sitemap_xml))
4131
.route("/link", get(link::get))
4232
.route("/link", post(link::post))
4333
.route("/login", get(login::get))
4434
.route("/login", post(login::post))
4535
.route("/:hash", get(redirect::get))
36+
.with_state(Arc::new(state))
37+
}
38+
39+
pub fn new(state: State) -> Router {
40+
Router::new()
41+
.nest_service("/static", ServeDir::new(PathBuf::from("static")))
42+
.nest("/", new_root(state))
43+
.fallback(not_found::any)
44+
.layer(
45+
ServiceBuilder::new()
46+
.layer(TraceLayer::new_for_http())
47+
.layer(CompressionLayer::new())
48+
.layer(NormalizePathLayer::trim_trailing_slash()),
49+
)
4650
}

src/router/not_found.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
use askama::Template;
1+
use axum::{
2+
body::Body,
3+
http::{Request, StatusCode},
4+
};
25

3-
#[derive(Template)]
4-
#[template(path = "../templates/404.html")]
5-
pub struct GetTemplate;
6+
use crate::router::shared::ErrorTemplate;
67

7-
pub async fn any() -> GetTemplate {
8-
GetTemplate
8+
pub async fn any(request: Request<Body>) -> (StatusCode, ErrorTemplate) {
9+
(
10+
StatusCode::NOT_FOUND,
11+
ErrorTemplate {
12+
title: "404 — Not Found".to_string(),
13+
message: format!("The page '{}' does not exist.", request.uri().path()),
14+
},
15+
)
916
}

src/auth.rs src/services/auth.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[derive(Debug)]
1+
#[derive(Debug, Clone)]
22
pub struct Auth;
33

44
// TODO implement using

src/services/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod auth;
2+
pub use auth::Auth;

static/css/style.css

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
--info-fg: #1D3557;
2424
--ok-fg: #457B9D;
2525
--bad-fg: #E63946;
26+
--custom-error-text: #fff1f2;
2627
--warn-fg: #E76F51;
2728
--plain-graphical-fg: #1D3557;
2829
--info-graphical-fg: #457B9D;
@@ -52,6 +53,7 @@
5253
--info-fg: #F1FAEE;
5354
--ok-fg: #2A9D8F;
5455
--bad-fg: #E63946;
56+
--custom-error-text: #fff1f2;
5557
--warn-fg: #E76F51;
5658
--plain-graphical-fg: #F1FAEE;
5759
--info-graphical-fg: #A8DADC;
@@ -61,7 +63,7 @@
6163
--plain-bg: #1D3557;
6264
--info-bg: #457B9D;
6365
--ok-bg: #2A9D8F;
64-
--bad-bg: #f88690;
66+
--bad-bg: #f7c1c5;
6567
--warn-bg: #E76F51;
6668
}
6769
}

static/js/bckt.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function init() {
2+
console.log('hello');
3+
4+
document.body.addEventListener('htmx:beforeSwap', (evt) => {
5+
console.log('beforeSwap', evt);
6+
if ([400, 404].includes(evt.detail.xhr.status)) {
7+
evt.detail.shouldSwap = true;
8+
evt.detail.isError = false;
9+
}
10+
});
11+
}
12+
13+
document.addEventListener("DOMContentLoaded", init);
14+
15+
export { };

templates/404.html

-10
This file was deleted.

templates/base.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
<meta property="og:image" content="/og-image">
2424

2525
<script src="/static/js/htmx.min.js?v=1.9.6"></script>
26+
<script type="module" src="/static/js/bckt.js?v=0.1.0"></script>
27+
2628
<link rel="stylesheet" href="/static/css/missing.min.css?v=1.1.1" />
27-
<link rel="stylesheet" href="/static/css/style.css?t=bucket&v=0.1.0" />
29+
<link rel="stylesheet" href="/static/css/style.css?t=bucket&v=0.1.1" />
2830
</head>
2931

3032
<body>

templates/content/login.html

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
<div class="table rows spacious">
77
<p>
88
<label for="email">email:</label>
9-
<input id="email" name="email" type="email" placeholder="email" style="width: 100%; margin: 0 5px 0 0;">
9+
<input
10+
id="email" name="email"
11+
type="email" placeholder="email"
12+
style="width: 100%; margin: 0 5px 0 0;"
13+
autofocus
14+
>
1015
</p>
1116
</div>
1217
<p>

templates/content/shared/error.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{% extends "base.html" %}
22
{% block content %}
33
<div class="box bad" style="width: 100%; max-width: 500px;">
4-
<strong class="block titlebar" style="color: var(--bg); text-shadow: none;">{{ title }}</strong>
5-
<p>{{ message }}</p>
4+
<strong class="block titlebar" style="color: var(--custom-error-text); text-shadow: none;">{{ title }}</strong>
5+
<p style="color: var(--bg);">{{ message }}</p>
66
<p>
7-
<a href="/" class="<button> big" style="margin: 10px 0 0 0; float: right;">↩ go back</a>
7+
<a href="/" class="<button> big" style="margin: 10px 0 0 0; float: right;" autofocus>↩ go back</a>
88
</p>
99
</div>
1010
{% endblock %}

templates/content/shared/info.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<strong class="block titlebar">{{ title }}</strong>
55
<p>{{ message }}</p>
66
<p>
7-
<a href="/" class="<button> good big" style="margin: 10px 0 0 0; float: right;">↩ go back</a>
7+
<a href="/" class="<button> good big" style="margin: 10px 0 0 0; float: right;" autofocus>↩ go back</a>
88
</p>
99
</div>
1010
{% endblock %}

0 commit comments

Comments
 (0)