- Database: Add external_calendars table with user relationships and CRUD operations - Backend: Implement REST API endpoints for external calendar management and ICS fetching - Frontend: Add external calendar modal, sidebar section with visibility toggles - Calendar integration: Merge external events with regular events in unified view - ICS parsing: Support multiple datetime formats, recurring events, and timezone handling - Authentication: Integrate with existing JWT token system for user-specific calendars - UI: Visual distinction with 📅 indicator and separate management section 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
109 lines
3.6 KiB
Rust
109 lines
3.6 KiB
Rust
use axum::{
|
|
response::Json,
|
|
routing::{delete, get, post},
|
|
Router,
|
|
};
|
|
use std::sync::Arc;
|
|
use tower_http::cors::{Any, CorsLayer};
|
|
|
|
pub mod auth;
|
|
pub mod calendar;
|
|
pub mod config;
|
|
pub mod db;
|
|
pub mod handlers;
|
|
pub mod models;
|
|
|
|
use auth::AuthService;
|
|
use db::Database;
|
|
|
|
#[derive(Clone)]
|
|
pub struct AppState {
|
|
pub auth_service: AuthService,
|
|
pub db: Database,
|
|
}
|
|
|
|
pub async fn run_server() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Initialize logging
|
|
println!("🚀 Starting Calendar Backend Server");
|
|
|
|
// Initialize database
|
|
let database_url = std::env::var("DATABASE_URL")
|
|
.unwrap_or_else(|_| "sqlite:calendar.db".to_string());
|
|
|
|
let db = Database::new(&database_url).await?;
|
|
println!("✅ Database initialized");
|
|
|
|
// Create auth service
|
|
let jwt_secret = std::env::var("JWT_SECRET")
|
|
.unwrap_or_else(|_| "your-super-secret-jwt-key-change-in-production".to_string());
|
|
|
|
let auth_service = AuthService::new(jwt_secret, db.clone());
|
|
|
|
let app_state = AppState { auth_service, db };
|
|
|
|
// Build our application with routes
|
|
let app = Router::new()
|
|
.route("/", get(root))
|
|
.route("/api/health", get(health_check))
|
|
.route("/api/auth/login", post(handlers::login))
|
|
.route("/api/auth/verify", get(handlers::verify_token))
|
|
.route("/api/user/info", get(handlers::get_user_info))
|
|
.route("/api/calendar/create", post(handlers::create_calendar))
|
|
.route("/api/calendar/delete", post(handlers::delete_calendar))
|
|
.route("/api/calendar/events", get(handlers::get_calendar_events))
|
|
.route("/api/calendar/events/create", post(handlers::create_event))
|
|
.route("/api/calendar/events/update", post(handlers::update_event))
|
|
.route("/api/calendar/events/delete", post(handlers::delete_event))
|
|
.route("/api/calendar/events/:uid", get(handlers::refresh_event))
|
|
// Event series-specific endpoints
|
|
.route(
|
|
"/api/calendar/events/series/create",
|
|
post(handlers::create_event_series),
|
|
)
|
|
.route(
|
|
"/api/calendar/events/series/update",
|
|
post(handlers::update_event_series),
|
|
)
|
|
.route(
|
|
"/api/calendar/events/series/delete",
|
|
post(handlers::delete_event_series),
|
|
)
|
|
// User preferences endpoints
|
|
.route("/api/preferences", get(handlers::get_preferences))
|
|
.route("/api/preferences", post(handlers::update_preferences))
|
|
.route("/api/auth/logout", post(handlers::logout))
|
|
// External calendars endpoints
|
|
.route("/api/external-calendars", get(handlers::get_external_calendars))
|
|
.route("/api/external-calendars", post(handlers::create_external_calendar))
|
|
.route("/api/external-calendars/:id", post(handlers::update_external_calendar))
|
|
.route("/api/external-calendars/:id", delete(handlers::delete_external_calendar))
|
|
.route("/api/external-calendars/:id/events", get(handlers::fetch_external_calendar_events))
|
|
.layer(
|
|
CorsLayer::new()
|
|
.allow_origin(Any)
|
|
.allow_methods(Any)
|
|
.allow_headers(Any),
|
|
)
|
|
.with_state(Arc::new(app_state));
|
|
|
|
// Start server
|
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
|
println!("📡 Server listening on http://0.0.0.0:3000");
|
|
|
|
axum::serve(listener, app).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn root() -> &'static str {
|
|
"Calendar Backend API v0.1.0"
|
|
}
|
|
|
|
async fn health_check() -> Json<serde_json::Value> {
|
|
Json(serde_json::json!({
|
|
"status": "healthy",
|
|
"service": "calendar-backend",
|
|
"version": "0.1.0"
|
|
}))
|
|
}
|