1use crate::prelude::*;
2use serde::{Deserialize, Serialize};
3use sqlx::{Row, postgres::PgRow};
4use std::{collections::BTreeMap, sync::LazyLock};
5#[derive(Serialize, Deserialize)]
6pub struct ServerVariable {
7 pub variable: super::nest_egg_variable::NestEggVariable,
8
9 pub value: String,
10
11 pub created: chrono::NaiveDateTime,
12
13 extension_data: super::ModelExtensionData,
14}
15
16impl BaseModel for ServerVariable {
17 const NAME: &'static str = "server_variable";
18
19 fn get_extension_list() -> &'static super::ModelExtensionList {
20 static EXTENSIONS: LazyLock<super::ModelExtensionList> =
21 LazyLock::new(|| parking_lot::RwLock::new(Vec::new()));
22
23 &EXTENSIONS
24 }
25
26 fn get_extension_data(&self) -> &super::ModelExtensionData {
27 &self.extension_data
28 }
29
30 #[inline]
31 fn base_columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
32 let prefix = prefix.unwrap_or_default();
33
34 let mut columns = BTreeMap::from([
35 (
36 "server_variables.value",
37 compact_str::format_compact!("{prefix}value"),
38 ),
39 (
40 "server_variables.created",
41 compact_str::format_compact!("{prefix}created"),
42 ),
43 ]);
44
45 columns.extend(super::nest_egg_variable::NestEggVariable::base_columns(
46 Some("variable_"),
47 ));
48
49 columns
50 }
51
52 #[inline]
53 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
54 let prefix = prefix.unwrap_or_default();
55
56 let variable = super::nest_egg_variable::NestEggVariable::map(Some("variable_"), row)?;
57 let value = row
58 .try_get(compact_str::format_compact!("{prefix}value").as_str())
59 .unwrap_or_else(|_| {
60 variable
61 .default_value
62 .clone()
63 .unwrap_or_else(|| "".to_string())
64 });
65
66 Ok(Self {
67 variable,
68 value,
69 created: row
70 .try_get(compact_str::format_compact!("{prefix}created").as_str())
71 .unwrap_or_else(|_| chrono::Utc::now().naive_utc()),
72 extension_data: Self::map_extensions(prefix, row)?,
73 })
74 }
75}
76
77impl ServerVariable {
78 pub async fn create(
79 database: &crate::database::Database,
80 server_uuid: uuid::Uuid,
81 variable_uuid: uuid::Uuid,
82 value: &str,
83 ) -> Result<(), crate::database::DatabaseError> {
84 sqlx::query(
85 r#"
86 INSERT INTO server_variables (server_uuid, variable_uuid, value)
87 VALUES ($1, $2, $3)
88 ON CONFLICT (server_uuid, variable_uuid) DO UPDATE SET value = EXCLUDED.value
89 "#,
90 )
91 .bind(server_uuid)
92 .bind(variable_uuid)
93 .bind(value)
94 .execute(database.write())
95 .await?;
96
97 Ok(())
98 }
99
100 pub async fn create_with_transaction(
101 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
102 server_uuid: uuid::Uuid,
103 variable_uuid: uuid::Uuid,
104 value: &str,
105 ) -> Result<(), crate::database::DatabaseError> {
106 sqlx::query(
107 r#"
108 INSERT INTO server_variables (server_uuid, variable_uuid, value)
109 VALUES ($1, $2, $3)
110 ON CONFLICT (server_uuid, variable_uuid) DO UPDATE SET value = EXCLUDED.value
111 "#,
112 )
113 .bind(server_uuid)
114 .bind(variable_uuid)
115 .bind(value)
116 .execute(&mut **transaction)
117 .await?;
118
119 Ok(())
120 }
121
122 pub async fn all_by_server_uuid_egg_uuid(
123 database: &crate::database::Database,
124 server_uuid: uuid::Uuid,
125 egg_uuid: uuid::Uuid,
126 ) -> Result<Vec<Self>, crate::database::DatabaseError> {
127 let rows = sqlx::query(sqlx::AssertSqlSafe(format!(
128 r#"
129 SELECT {}
130 FROM nest_egg_variables
131 LEFT JOIN server_variables ON server_variables.variable_uuid = nest_egg_variables.uuid AND server_variables.server_uuid = $1
132 WHERE nest_egg_variables.egg_uuid = $2
133 ORDER BY nest_egg_variables.order_, nest_egg_variables.created
134 "#,
135 Self::columns_sql(None)
136 )))
137 .bind(server_uuid)
138 .bind(egg_uuid)
139 .fetch_all(database.read())
140 .await?;
141
142 rows.into_iter()
143 .map(|row| Self::map(None, &row))
144 .try_collect_vec()
145 }
146}
147
148#[async_trait::async_trait]
149impl IntoApiObject for ServerVariable {
150 type ApiObject = ApiServerVariable;
151 type ExtraArgs<'a> = ();
152
153 async fn into_api_object<'a>(
154 self,
155 state: &crate::State,
156 _args: Self::ExtraArgs<'a>,
157 ) -> Result<Self::ApiObject, crate::database::DatabaseError> {
158 let api_object = ApiServerVariable::init_hooks(&self, state).await?;
159
160 let api_object = finish_extendible!(
161 ApiServerVariable {
162 name: self.variable.name,
163 name_translations: self.variable.name_translations,
164 description: self.variable.description,
165 description_translations: self.variable.description_translations,
166 env_variable: self.variable.env_variable,
167 default_value: self.variable.default_value,
168 value: self.value,
169 is_editable: self.variable.user_editable,
170 is_secret: self.variable.secret,
171 rules: self.variable.rules,
172 created: self.created.and_utc(),
173 },
174 api_object,
175 state
176 )?;
177
178 Ok(api_object)
179 }
180}
181
182#[schema_extension_derive::extendible]
183#[init_args(ServerVariable, crate::State)]
184#[hook_args(crate::State)]
185#[derive(ToSchema, Serialize)]
186#[schema(title = "ServerVariable")]
187pub struct ApiServerVariable {
188 pub name: compact_str::CompactString,
189 pub name_translations: BTreeMap<compact_str::CompactString, compact_str::CompactString>,
190 pub description: Option<compact_str::CompactString>,
191 pub description_translations: BTreeMap<compact_str::CompactString, compact_str::CompactString>,
192
193 pub env_variable: compact_str::CompactString,
194 pub default_value: Option<String>,
195 pub value: String,
196 pub is_editable: bool,
197 pub is_secret: bool,
198 pub rules: Vec<compact_str::CompactString>,
199
200 pub created: chrono::DateTime<chrono::Utc>,
201}