Terminal-themed markdown diary

Single-user diary served as one Rust binary with the Svelte frontend
embedded.

- axum 0.8 + SQLite (sqlx), migrations run at startup
- stateless HMAC session cookie; credentials from .env, compared in
  constant time
- FTS5 full-text search, user input rewritten into quoted prefix terms
- media uploads linked to entries by scanning bodies for /api/media/<id>,
  which scopes share-token media access and entry deletion
- unguessable share links (192-bit token) serving a public read-only page
- vim keybindings via CodeMirror plus a modal browse layer and : command
  line; disabled on touch devices
This commit is contained in:
nvrl committed 2026-09-05 05:39:25 +02:00
commit 4002a35305
41 files changed
+6195

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
use axum::{
extract::State,
http::{header, StatusCode},
response::IntoResponse,
Json,
};
use serde::Deserialize;
use serde_json::json;
use crate::{
auth::{self, Session},
error::{AppError, AppResult},
state::AppState,
};
#[derive(Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
pub async fn login(
State(state): State<AppState>,
Json(body): Json<LoginRequest>,
) -> AppResult<impl IntoResponse> {
if !auth::credentials_match(&state.config, &body.username, &body.password) {
return Err(AppError::BadRequest("invalid credentials".into()));
}
let token = auth::issue_token(&state.config);
Ok((
StatusCode::OK,
[(
header::SET_COOKIE,
auth::session_cookie(&state.config, &token),
)],
Json(json!({ "user": state.config.username })),
))
}
pub async fn logout(State(state): State<AppState>) -> impl IntoResponse {
(
StatusCode::OK,
[(header::SET_COOKIE, auth::clear_cookie(&state.config))],
Json(json!({ "ok": true })),
)
}
pub async fn me(_: Session, State(state): State<AppState>) -> impl IntoResponse {
Json(json!({ "user": state.config.username }))
}