Files
calendar/backend/src/models.rs
Connor Johnstone 25bf194d19 Implement complete full-stack authentication system
- Restructure project with separate frontend/backend architecture
- Create dedicated backend with Axum, SQLite, JWT authentication
- Implement real API endpoints for register/login/verify
- Update frontend to use HTTP requests instead of mock auth
- Add bcrypt password hashing and secure token generation
- Separate Cargo.toml files for frontend and backend builds
- Fix Trunk compilation by isolating WASM-incompatible dependencies
- Create demo user in database for easy testing
- Both servers running: frontend (8081), backend (3000)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 16:15:37 -04:00

90 lines
2.4 KiB
Rust

use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
// Database models
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub username: String,
pub email: String,
pub password_hash: String,
pub created_at: DateTime<Utc>,
}
// API request/response types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserInfo {
pub id: String,
pub username: String,
pub email: String,
}
#[derive(Debug, Deserialize)]
pub struct RegisterRequest {
pub username: String,
pub email: String,
pub password: String,
}
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
#[derive(Debug, Serialize)]
pub struct AuthResponse {
pub token: String,
pub user: UserInfo,
}
// Error handling
#[derive(Debug)]
pub enum ApiError {
Database(String),
NotFound(String),
Unauthorized(String),
BadRequest(String),
Conflict(String),
Internal(String),
}
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let (status, error_message) = match self {
ApiError::Database(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
ApiError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg),
ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
ApiError::Conflict(msg) => (StatusCode::CONFLICT, msg),
ApiError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
};
let body = Json(serde_json::json!({
"error": error_message,
"status": status.as_u16()
}));
(status, body).into_response()
}
}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ApiError::Database(msg) => write!(f, "Database error: {}", msg),
ApiError::NotFound(msg) => write!(f, "Not found: {}", msg),
ApiError::Unauthorized(msg) => write!(f, "Unauthorized: {}", msg),
ApiError::BadRequest(msg) => write!(f, "Bad request: {}", msg),
ApiError::Conflict(msg) => write!(f, "Conflict: {}", msg),
ApiError::Internal(msg) => write!(f, "Internal error: {}", msg),
}
}
}
impl std::error::Error for ApiError {}