aboutsummaryrefslogtreecommitdiff
path: root/src/models
diff options
context:
space:
mode:
authorUMTS at Teleco <crt@teleco.ch>2025-12-13 02:48:13 +0100
committerUMTS at Teleco <crt@teleco.ch>2025-12-13 02:48:13 +0100
commite52b8e1c2e110d0feb74feb7905c2ff064b51d55 (patch)
tree3090814e422250e07e72cf1c83241ffd95cf20f7 /src/models
committing to insanityHEADmaster
Diffstat (limited to 'src/models')
-rw-r--r--src/models/mod.rs294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/models/mod.rs b/src/models/mod.rs
new file mode 100644
index 0000000..4d9f734
--- /dev/null
+++ b/src/models/mod.rs
@@ -0,0 +1,294 @@
+// 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<String>,
+ pub password: Option<String>,
+ pub pin: Option<String>,
+ pub login_string: Option<String>,
+}
+
+#[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<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub user: Option<UserInfo>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub error: Option<String>,
+}
+
+#[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<QueryAction>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub table: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub columns: Option<Vec<String>>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub data: Option<serde_json::Value>,
+ // Enhanced WHERE clause - supports both simple and complex conditions
+ #[serde(rename = "where", skip_serializing_if = "Option::is_none")]
+ pub where_clause: Option<serde_json::Value>,
+ // New structured filter for complex queries
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub filter: Option<FilterCondition>,
+ // JOIN support - allows multi-table queries with permission validation
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub joins: Option<Vec<Join>>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub limit: Option<u32>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub offset: Option<u32>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub order_by: Option<Vec<OrderBy>>,
+
+ // Batch mode (when queries is Some) - action/table apply to ALL queries in batch
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub queries: Option<Vec<BatchQuery>>,
+ /// Whether to rollback on error in batch mode (defaults to config setting)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub rollback_on_error: Option<bool>,
+}
+
+/// 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<Vec<String>>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub data: Option<serde_json::Value>,
+ #[serde(rename = "where", skip_serializing_if = "Option::is_none")]
+ pub where_clause: Option<serde_json::Value>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub filter: Option<FilterCondition>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub limit: Option<u32>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub offset: Option<u32>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub order_by: Option<Vec<OrderBy>>,
+}
+
+/// 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<Vec<FilterCondition>>,
+ #[serde(rename = "or")]
+ or_conditions: Option<Vec<FilterCondition>>,
+ },
+ /// NOT condition: {"not": condition}
+ Not { not: Box<FilterCondition> },
+}
+
+/// 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_json::Value>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub rows_affected: Option<u64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub error: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub warning: Option<String>,
+ // Batch results (when queries field was used)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub results: Option<Vec<QueryResponse>>,
+}
+
+// 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<String>,
+ pub login_string: Option<String>,
+ pub role_id: i32,
+ pub email: Option<String>,
+ pub phone: Option<String>,
+ pub notes: Option<String>,
+ pub active: bool,
+ pub last_login_date: Option<DateTime<Utc>>,
+ pub created_date: DateTime<Utc>,
+ pub password_reset_token: Option<String>,
+ pub password_reset_expiry: Option<DateTime<Utc>>,
+}
+
+#[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<Utc>,
+}
+
+// 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<Utc>,
+ pub last_accessed: DateTime<Utc>,
+}
+
+// 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<String, String>,
+ pub security_clearance: Option<String>,
+ pub user_settings_access: String,
+}