Implement logging in

This commit is contained in:
Nettika 2026-01-25 16:02:13 -08:00
parent 3ce2fb04eb
commit a00671115e
No known key found for this signature in database
2 changed files with 40 additions and 5 deletions

View file

@ -13,7 +13,7 @@
[x] Add .env file support using the dotenv crate. Load environment variables on server startup.
[x] Create a `GET /login` endpoint that returns a simple HTML login form (username and password fields).
[x] Add a rouille::session manager to set a session cookie.
[ ] Create a `POST /login` endpoint that validates credentials against USERNAME and PASSWORD environment variables.
[x] Create a `POST /login` endpoint that validates credentials against USERNAME and PASSWORD environment variables.
[ ] Protect write endpoints (POST /projects, and any future write operations) with authentication. Redirect to /login if not authenticated.
[ ] Add a login button to the front page (GET /projects) that links to /login.
[ ] When logged in, show a "Create Project" button on the front page that links to /new-project.

View file

@ -1,5 +1,7 @@
use maud::{DOCTYPE, html};
use rouille::{router, try_or_400, session};
use rouille::{router, session, try_or_400};
use std::collections::HashSet;
use std::sync::Mutex;
mod db;
mod project;
@ -13,10 +15,13 @@ fn main() {
println!("Starting server on localhost:8080");
// Track authenticated sessions
let authenticated_sessions: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
rouille::start_server("localhost:8080", move |request| {
// Wrap all requests with session management
// This sets a session cookie with a 1-hour timeout
session::session(request, "SESSION", 3600, |_session| {
session::session(request, "SESSION", 3600, |session| {
router!(request,
(GET) ["/"] => {
rouille::Response::redirect_302("/projects")
@ -32,6 +37,7 @@ fn main() {
(GET) ["/projects"] => display_projects(),
(GET) ["/new-project"] => new_project_form(),
(GET) ["/login"] => login_form(),
(POST) ["/login"] => handle_login(request, session, &authenticated_sessions),
(POST) ["/projects"] => create_project(request),
_ => rouille::Response::empty_404()
)
@ -47,10 +53,12 @@ fn display_projects() -> rouille::Response {
.unwrap()
.as_secs() as i64;
let active_projects: Vec<_> = projects.iter()
let active_projects: Vec<_> = projects
.iter()
.filter(|p| !p.archived && (now - p.last_modified_time) <= STALE_AGE)
.collect();
let stale_projects: Vec<_> = projects.iter()
let stale_projects: Vec<_> = projects
.iter()
.filter(|p| !p.archived && (now - p.last_modified_time) > STALE_AGE)
.collect();
let archived_projects: Vec<_> = projects.iter().filter(|p| p.archived).collect();
@ -167,3 +175,30 @@ fn create_project(request: &rouille::Request) -> rouille::Response {
Err(_) => rouille::Response::text("Failed to create project").with_status_code(500),
}
}
fn handle_login(
request: &rouille::Request,
session: &session::Session,
authenticated_sessions: &Mutex<HashSet<String>>,
) -> rouille::Response {
let input = try_or_400!(rouille::post_input!(request, {
username: String,
password: String,
}));
// Get credentials from environment variables
let valid_username = std::env::var("USERNAME").unwrap_or_default();
let valid_password = std::env::var("PASSWORD").unwrap_or_default();
// Validate credentials
if input.username == valid_username && input.password == valid_password {
// Add session ID to authenticated sessions
if let Ok(mut sessions) = authenticated_sessions.lock() {
sessions.insert(session.id().to_string());
}
rouille::Response::redirect_302("/projects")
} else {
// Invalid credentials - redirect back to login
rouille::Response::redirect_302("/login")
}
}