Upload files to "src"
This commit is contained in:
65
src/handlers.rs
Normal file
65
src/handlers.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::Json,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc;
|
||||
use crate::model::{Entry, AppState};
|
||||
|
||||
pub async fn list_entries(State(state): State<AppState>) -> Json<Vec<Entry>> {
|
||||
let entries = state.entries.lock().unwrap();
|
||||
let mut sorted = entries.clone();
|
||||
// Sort: Pinned/Daily first, then by date
|
||||
sorted.sort_by(|a, b| b.created_at.cmp(&a.created_at));
|
||||
Json(sorted)
|
||||
}
|
||||
|
||||
pub async fn create_entry(
|
||||
State(state): State<AppState>,
|
||||
Json(mut payload): Json<Entry>,
|
||||
) -> Json<Entry> {
|
||||
if payload.id == Uuid::nil() { payload.id = Uuid::new_v4(); }
|
||||
payload.created_at = Utc::now();
|
||||
payload.status = "Active".to_string();
|
||||
|
||||
{
|
||||
let mut entries = state.entries.lock().unwrap();
|
||||
entries.push(payload.clone());
|
||||
}
|
||||
state.save();
|
||||
Json(payload)
|
||||
}
|
||||
|
||||
pub async fn update_entry(
|
||||
Path(id): Path<Uuid>,
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<Entry>,
|
||||
) -> StatusCode {
|
||||
let mut entries = state.entries.lock().unwrap();
|
||||
if let Some(entry) = entries.iter_mut().find(|e| e.id == id) {
|
||||
entry.title = payload.title;
|
||||
entry.description = payload.description;
|
||||
entry.tags = payload.tags;
|
||||
entry.frequency = payload.frequency;
|
||||
entry.details = payload.details;
|
||||
entry.event_date = payload.event_date;
|
||||
entry.updated_at = Some(Utc::now());
|
||||
drop(entries);
|
||||
state.save();
|
||||
StatusCode::OK
|
||||
} else {
|
||||
StatusCode::NOT_FOUND
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_entry(
|
||||
Path(id): Path<Uuid>,
|
||||
State(state): State<AppState>,
|
||||
) -> StatusCode {
|
||||
let mut entries = state.entries.lock().unwrap();
|
||||
entries.retain(|e| e.id != id);
|
||||
drop(entries);
|
||||
state.save();
|
||||
StatusCode::NO_CONTENT
|
||||
}
|
||||
30
src/main.rs
Normal file
30
src/main.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
mod handlers;
|
||||
mod model;
|
||||
|
||||
use axum::{
|
||||
routing::{get, put, delete}, // post is implied in route chaining
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::{services::ServeDir, trace::TraceLayer};
|
||||
use model::AppState;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let state = AppState::new("data.json");
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/entries", get(handlers::list_entries).post(handlers::create_entry))
|
||||
.route("/api/entries/:id", put(handlers::update_entry).delete(handlers::delete_entry))
|
||||
.nest_service("/", ServeDir::new("static"))
|
||||
.with_state(state)
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3030));
|
||||
println!("Life Manager running at http://{}", addr);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
71
src/model.rs
Normal file
71
src/model.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use uuid::Uuid;
|
||||
use serde_json::Value;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Entry {
|
||||
#[serde(default = "Uuid::new_v4")]
|
||||
pub id: Uuid,
|
||||
pub template_type: String,
|
||||
pub title: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
|
||||
pub frequency: Option<String>,
|
||||
|
||||
#[serde(default = "default_status")]
|
||||
pub status: String,
|
||||
|
||||
// NEW FIELD: Holds specific appointment times
|
||||
#[serde(default)]
|
||||
pub event_date: Option<DateTime<Utc>>,
|
||||
|
||||
pub details: Value,
|
||||
|
||||
#[serde(default = "Utc::now")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
|
||||
pub updated_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
// Helper function for the default value
|
||||
fn default_status() -> String {
|
||||
"Active".to_string()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub entries: Arc<Mutex<Vec<Entry>>>,
|
||||
pub file_path: String,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(file_path: &str) -> Self {
|
||||
// Try to read file, otherwise create empty list
|
||||
let entries = match std::fs::read_to_string(file_path) {
|
||||
Ok(content) => serde_json::from_str(&content).unwrap_or_default(),
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
Self {
|
||||
entries: Arc::new(Mutex::new(entries)),
|
||||
file_path: file_path.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
let entries = self.entries.lock().unwrap();
|
||||
let content = serde_json::to_string_pretty(&*entries).unwrap();
|
||||
// Added error printing so you can see if file permissions are wrong
|
||||
if let Err(e) = std::fs::write(&self.file_path, content) {
|
||||
eprintln!("CRITICAL ERROR: Could not save data.json: {}", e);
|
||||
} else {
|
||||
println!("Database saved to {}", self.file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user