1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Scripting engine types
use serde_json::Value as JsonValue;
use std::collections::HashMap;
use std::sync::atomic::AtomicUsize;
pub use super::auth::ScriptUser;
/// Service metadata stored in _services collection
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Service {
/// Service identifier (e.g., "users", "auth")
#[serde(rename = "_key")]
pub key: String,
/// Human-readable name
pub name: String,
/// Optional description
pub description: Option<String>,
/// API version (e.g., "1.0.0")
pub version: Option<String>,
/// Database this service belongs to
pub database: String,
/// Whether this service is enabled
#[serde(default = "default_enabled")]
pub enabled: bool,
/// Default auth requirement for scripts in this service
#[serde(default = "default_require_auth")]
pub require_auth: bool,
/// Creation timestamp
pub created_at: String,
/// Last modified timestamp
pub updated_at: String,
}
fn default_enabled() -> bool {
true
}
fn default_require_auth() -> bool {
true
}
/// Context passed to Lua scripts containing request information
#[derive(Debug, Clone)]
pub struct ScriptContext {
/// HTTP method (GET, POST, PUT, DELETE)
pub method: String,
/// Request path (after /api/custom/)
pub path: String,
/// Query parameters
pub query_params: HashMap<String, String>,
/// URL parameters (e.g., :id)
pub params: HashMap<String, String>,
/// Request headers
pub headers: HashMap<String, String>,
/// Request body (parsed as JSON if applicable)
pub body: Option<JsonValue>,
/// Whether this is a WebSocket connection
pub is_websocket: bool,
/// Current authenticated user (if any)
pub user: ScriptUser,
}
impl ScriptContext {
/// Who a script's by-name writes are attributed to: its **caller**, never
/// the server. The script's code is fixed by an admin, but a public
/// route can be called by anyone, and a script that writes to
/// `request.body.collection` would otherwise let that anyone pick
/// `_scripts`. The queue worker runs scripts as `_system` with the admin
/// role, so trigger and job scripts keep their admin rights.
pub fn write_actor(&self) -> crate::storage::WriteActor {
crate::storage::WriteActor::client(self.user.authenticated && self.user.has_role("admin"))
}
/// The principal a `db:query(...)` runs under — the same one the caller
/// would get on `/cursor`.
pub fn query_principal(&self) -> crate::sdbql::QueryPrincipal {
if self.user.authenticated {
crate::sdbql::QueryPrincipal::from_roles(
self.user.username.clone(),
self.user.roles.clone(),
)
} else {
crate::sdbql::QueryPrincipal::anonymous()
}
}
}
/// Script metadata stored in _system/_scripts
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Script {
#[serde(rename = "_key")]
pub key: String,
/// Human-readable name
pub name: String,
/// HTTP methods this script handles (e.g., ["GET", "POST"])
pub methods: Vec<String>,
/// URL path pattern (e.g., "users/:id" or "hello")
pub path: String,
/// Database this script belongs to
#[serde(default = "default_database")]
pub database: String,
/// Service this script belongs to (required)
#[serde(default = "default_service")]
pub service: String,
/// Collection this script is scoped to (optional)
pub collection: Option<String>,
/// The Lua source code
pub code: String,
/// Optional description
pub description: Option<String>,
/// Creation timestamp
pub created_at: String,
/// Last modified timestamp
pub updated_at: String,
}
fn default_database() -> String {
"_system".to_string()
}
fn default_service() -> String {
"default".to_string()
}
/// Runtime statistics for the script engine
#[derive(Debug, Default)]
pub struct ScriptStats {
/// Number of HTTP scripts currently executing
pub active_scripts: AtomicUsize,
/// Number of active WebSocket connections
pub active_ws: AtomicUsize,
/// Total number of HTTP scripts executed since start
pub total_scripts_executed: AtomicUsize,
/// Total number of WebSocket connections handled since start
pub total_ws_connections: AtomicUsize,
}
/// Result from script execution
#[derive(Debug)]
pub struct ScriptResult {
pub status: u16,
pub body: JsonValue,
pub headers: HashMap<String, String>,
/// Pre-serialized body — if set, sent as-is instead of serializing
/// `body`. JSON unless `headers` carries a `content-type`.
pub raw_body: Option<Vec<u8>>,
}