// Data models for SeckelAPI use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; // Authentication models #[derive(Debug, Deserialize, Serialize)] pub struct LoginRequest { pub method: AuthMethod, pub username: Option, pub password: Option, pub pin: Option, pub login_string: Option, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum AuthMethod { Password, Pin, Token, } #[derive(Debug, Serialize)] pub struct LoginResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub token: Option, #[serde(skip_serializing_if = "Option::is_none")] pub user: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } #[derive(Debug, Serialize)] pub struct UserInfo { pub id: i32, pub username: String, pub name: String, pub role: String, pub power: i32, } // Database query models #[derive(Debug, Deserialize, Serialize, Clone)] pub struct QueryRequest { // Single query mode (when queries is None) #[serde(skip_serializing_if = "Option::is_none")] pub action: Option, #[serde(skip_serializing_if = "Option::is_none")] pub table: Option, #[serde(skip_serializing_if = "Option::is_none")] pub columns: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, // Enhanced WHERE clause - supports both simple and complex conditions #[serde(rename = "where", skip_serializing_if = "Option::is_none")] pub where_clause: Option, // New structured filter for complex queries #[serde(skip_serializing_if = "Option::is_none")] pub filter: Option, // JOIN support - allows multi-table queries with permission validation #[serde(skip_serializing_if = "Option::is_none")] pub joins: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option, #[serde(skip_serializing_if = "Option::is_none")] pub offset: Option, #[serde(skip_serializing_if = "Option::is_none")] pub order_by: Option>, // Batch mode (when queries is Some) - action/table apply to ALL queries in batch #[serde(skip_serializing_if = "Option::is_none")] pub queries: Option>, /// Whether to rollback on error in batch mode (defaults to config setting) #[serde(skip_serializing_if = "Option::is_none")] pub rollback_on_error: Option, } /// Individual query in a batch - inherits action/table from parent QueryRequest #[derive(Debug, Deserialize, Serialize, Clone)] pub struct BatchQuery { // Only the variable parts per query - no action/table duplication #[serde(skip_serializing_if = "Option::is_none")] pub columns: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, #[serde(rename = "where", skip_serializing_if = "Option::is_none")] pub where_clause: Option, #[serde(skip_serializing_if = "Option::is_none")] pub filter: Option, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option, #[serde(skip_serializing_if = "Option::is_none")] pub offset: Option, #[serde(skip_serializing_if = "Option::is_none")] pub order_by: Option>, } /// JOIN specification for multi-table queries #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Join { /// Type of join (INNER, LEFT, RIGHT) #[serde(rename = "type")] pub join_type: JoinType, /// Table to join with pub table: String, /// Join condition (e.g., "assets.category_id = categories.id") pub on: String, } #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(rename_all = "UPPERCASE")] pub enum JoinType { Inner, Left, Right, } /// Enhanced filter condition supporting operators and nested logic #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(untagged)] pub enum FilterCondition { /// Simple condition: {"column": "name", "op": "=", "value": "John"} Simple { column: String, #[serde(rename = "op")] operator: FilterOperator, value: serde_json::Value, }, /// Logical AND/OR: {"and": [condition1, condition2]} Logical { #[serde(rename = "and")] and_conditions: Option>, #[serde(rename = "or")] or_conditions: Option>, }, /// NOT condition: {"not": condition} Not { not: Box }, } /// Supported WHERE clause operators #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[serde(rename_all = "lowercase")] pub enum FilterOperator { #[serde(rename = "=")] Eq, // Equal #[serde(rename = "!=")] Ne, // Not equal #[serde(rename = ">")] Gt, // Greater than #[serde(rename = ">=")] Gte, // Greater than or equal #[serde(rename = "<")] Lt, // Less than #[serde(rename = "<=")] Lte, // Less than or equal Like, // LIKE pattern matching #[serde(rename = "not_like")] NotLike, // NOT LIKE In, // IN (value1, value2, ...) #[serde(rename = "not_in")] NotIn, // NOT IN (...) #[serde(rename = "is_null")] IsNull, // IS NULL #[serde(rename = "is_not_null")] IsNotNull, // IS NOT NULL Between, // BETWEEN value1 AND value2 } /// ORDER BY clause #[derive(Debug, Deserialize, Serialize, Clone)] pub struct OrderBy { pub column: String, #[serde(default = "default_order_direction")] pub direction: OrderDirection, } #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[serde(rename_all = "UPPERCASE")] pub enum OrderDirection { ASC, DESC, } fn default_order_direction() -> OrderDirection { OrderDirection::ASC } #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[serde(rename_all = "lowercase")] pub enum QueryAction { Select, Insert, Update, Delete, Count, } #[derive(Debug, Serialize, Clone)] pub struct QueryResponse { pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, #[serde(skip_serializing_if = "Option::is_none")] pub rows_affected: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, #[serde(skip_serializing_if = "Option::is_none")] pub warning: Option, // Batch results (when queries field was used) #[serde(skip_serializing_if = "Option::is_none")] pub results: Option>, } // Database entities #[derive(Debug, sqlx::FromRow)] #[allow(dead_code)] // Fields are used for database serialization pub struct User { pub id: i32, pub name: String, pub username: String, pub password: String, pub pin_code: Option, pub login_string: Option, pub role_id: i32, pub email: Option, pub phone: Option, pub notes: Option, pub active: bool, pub last_login_date: Option>, pub created_date: DateTime, pub password_reset_token: Option, pub password_reset_expiry: Option>, } #[derive(Debug, sqlx::FromRow)] #[allow(dead_code)] // Fields are used for database serialization pub struct Role { pub id: i32, pub name: String, pub power: i32, pub created_at: DateTime, } // Session management #[derive(Debug, Clone)] pub struct Session { pub user_id: i32, pub username: String, pub role_id: i32, pub role_name: String, pub power: i32, pub created_at: DateTime, pub last_accessed: DateTime, } // Permission types #[derive(Debug, Clone, PartialEq)] pub enum Permission { Read, Write, ReadWrite, None, } impl Permission { pub fn from_str(s: &str) -> Self { match s { "r" => Permission::Read, "w" => Permission::Write, "rw" => Permission::ReadWrite, _ => Permission::None, } } pub fn can_read(&self) -> bool { matches!(self, Permission::Read | Permission::ReadWrite) } pub fn can_write(&self) -> bool { matches!(self, Permission::Write | Permission::ReadWrite) } } // Permissions response #[derive(Debug, Serialize)] pub struct PermissionsResponse { pub success: bool, pub user: UserInfo, pub permissions: HashMap, pub security_clearance: Option, pub user_settings_access: String, }