refactor(lib): refresh config schema, db entities, email runtime, obs, queue admin, webhook dispatcher
zhenyi committed
Modified lib/config/src/git.rs lib/config/src/git.rs +1 −1 View lib/config/src/git.rs
@@ -13,7 +13,7 @@
1313 pub lfs_storage_root: String,
1414 pub object_cache_prefix: String,
1515 pub object_cache_ttl: u64,
16 pub max_lfs_object_size: u64,
16 pub max_lfs_object_size: u64,
1717}
1818
1919impl Default for SmartConfig {
Modified lib/config/src/mcp.rs lib/config/src/mcp.rs +10 −10 View lib/config/src/mcp.rs
@@ -5,15 +5,15 @@
55#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(default)]
77pub struct McpSettings {
8 pub enabled: bool,
9 pub external_path: String,
10 pub internal_path: String,
11 pub internal_secret_key: Option<String>,
8 pub enabled: bool,
9 pub external_path: String,
10 pub internal_path: String,
11 pub internal_secret_key: Option<String>,
1212 /// Extra allowed CIDRs appended to the built-in whitelist (e.g. custom
13 pub internal_allowed_cidrs: Vec<String>,
14 pub trust_proxy: bool,
15 pub system_user_id: Option<String>,
16 pub max_request_bytes: usize,
13 pub internal_allowed_cidrs: Vec<String>,
14 pub trust_proxy: bool,
15 pub system_user_id: Option<String>,
16 pub max_request_bytes: usize,
1717}
1818
1919impl Default for McpSettings {
@@ -26,7 +26,7 @@
2626 internal_allowed_cidrs: Vec::new(),
2727 trust_proxy: false,
2828 system_user_id: None,
29 max_request_bytes: 1 << 20,
29 max_request_bytes: 1 << 20,
3030 }
3131 }
3232}
@@ -55,4 +55,4 @@
5555 }
5656 Ok(())
5757 }
58}
No newline at end of file
58}
Modified lib/config/src/obs.rs lib/config/src/obs.rs +12 −12 View lib/config/src/obs.rs
@@ -7,25 +7,25 @@
77#[derive(Debug, Clone, Serialize, Deserialize)]
88#[serde(default)]
99pub struct ObsSettings {
10 pub enabled: bool,
11 pub service_name: String,
12 pub service_version: String,
13 pub stream_name: String,
14 pub endpoint: String,
15 pub token: Option<String>,
16 pub user: Option<String>,
17 pub password: Option<String>,
10 pub enabled: bool,
11 pub service_name: String,
12 pub service_version: String,
13 pub stream_name: String,
14 pub endpoint: String,
15 pub token: Option<String>,
16 pub user: Option<String>,
17 pub password: Option<String>,
1818 pub enable_logs: bool,
1919 pub enable_metrics: bool,
2020 pub enable_traces: bool,
21 pub traces_sample_ratio: f64,
21 pub traces_sample_ratio: f64,
2222 pub batch_size: usize,
2323 #[serde(default, deserialize_with = "deserialize_std_duration")]
2424 pub batch_interval: Duration,
2525 #[serde(default, deserialize_with = "deserialize_std_duration")]
2626 pub timeout: Duration,
27 pub console_log: bool,
28 pub log_level: String,
27 pub console_log: bool,
28 pub log_level: String,
2929}
3030
3131impl Default for ObsSettings {
@@ -53,7 +53,7 @@
5353}
5454
5555impl ObsSettings {
56 pub fn with_app_defaults(
56 pub fn with_app_defaults(
5757 mut self,
5858 service_name: impl Into<String>,
5959 log_level: impl Into<String>,
Modified lib/config/src/user.rs lib/config/src/user.rs +3 −0 View lib/config/src/user.rs
@@ -59,6 +59,8 @@
5959 pub default_locale: String,
6060 #[serde(default = "default_default_timezone")]
6161 pub default_timezone: String,
62 #[serde(default)]
63 pub totp_encryption_key: Option<String>,
6264}
6365
6466fn default_max_pagination() -> usize {
@@ -150,6 +152,7 @@
150152 default_visibility: ProfileVisibility::default(),
151153 default_locale: default_default_locale(),
152154 default_timezone: default_default_timezone(),
155 totp_encryption_key: None,
153156 }
154157 }
155158}
Modified lib/config/src/webhook.rs lib/config/src/webhook.rs +1 −1 View lib/config/src/webhook.rs
@@ -69,4 +69,4 @@
6969 }
7070 Ok(())
7171 }
72}
No newline at end of file
72}
Modified lib/db/repo/repo_branch_protection_policy.rs lib/db/repo/repo_branch_protection_policy.rs +2 −2 View lib/db/repo/repo_branch_protection_policy.rs
@@ -23,9 +23,9 @@
2323 pub require_signed_commits: bool,
2424 pub protected_file_patterns: String,
2525 pub unprotected_file_patterns: String,
26 pub branch_pattern: Option<String>,
26 pub branch_pattern: Option<String>,
2727 /// If true, non-fast-forward updates to the protected ref are allowed.
28 pub enable_force_push: bool,
28 pub enable_force_push: bool,
2929 pub created_at: OffsetDateTime,
3030 pub updated_at: OffsetDateTime,
3131 #[sea_orm(belongs_to, from = "repo_id", to = "id")]
Modified lib/db/space/space_invite.rs lib/db/space/space_invite.rs +2 −2 View lib/db/space/space_invite.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub space_id: Uuid,
12 pub user_id: Uuid,
12 pub user_id: Option<Uuid>,
1313 pub email: String,
1414 pub role_ids: String,
1515 pub accept_code: String,
@@ -20,7 +20,7 @@
2020 #[sea_orm(belongs_to, from = "space_id", to = "id")]
2121 pub space: BelongsTo<super::spaces::Entity>,
2222 #[sea_orm(belongs_to, from = "user_id", to = "uid")]
23 pub user: BelongsTo<super::super::user::users::Entity>,
23 pub user: BelongsTo<Option<super::super::user::users::Entity>>,
2424}
2525
2626impl ActiveModelBehavior for ActiveModel {}
Modified lib/db/space/space_members.rs lib/db/space/space_members.rs +2 −2 View lib/db/space/space_members.rs
@@ -6,7 +6,7 @@
66#[derive(
77 Debug, Clone, Copy, PartialEq, Eq, Default, EnumIter, DeriveActiveEnum, Serialize, Deserialize,
88)]
9#[sea_orm(rs_type = "i32", db_type = "SmallInteger")]
9#[sea_orm(rs_type = "i16", db_type = "SmallInteger")]
1010pub enum MemberStatus {
1111 #[default]
1212 Active = 0,
@@ -18,7 +18,7 @@
1818#[derive(
1919 Debug, Clone, Copy, PartialEq, Eq, Default, EnumIter, DeriveActiveEnum, Serialize, Deserialize,
2020)]
21#[sea_orm(rs_type = "i32", db_type = "SmallInteger")]
21#[sea_orm(rs_type = "i16", db_type = "SmallInteger")]
2222pub enum MessageNotifications {
2323 #[default]
2424 All = 0,
Modified lib/db/space/spaces.rs lib/db/space/spaces.rs +1 −1 View lib/db/space/spaces.rs
@@ -6,7 +6,7 @@
66#[derive(
77 Debug, Clone, Copy, PartialEq, Eq, Default, EnumIter, DeriveActiveEnum, Serialize, Deserialize,
88)]
9#[sea_orm(rs_type = "i32", db_type = "SmallInteger")]
9#[sea_orm(rs_type = "i16", db_type = "SmallInteger")]
1010pub enum AllowJoinLevel {
1111 #[default]
1212 None = 0,
Modified lib/db/user/mod.rs lib/db/user/mod.rs +1 −0 View lib/db/user/mod.rs
@@ -23,3 +23,4 @@
2323pub mod user_ssh_keys;
2424pub mod user_suspensions;
2525pub mod users;
26pub mod users_loader;
Modified lib/db/schema_extras.sql lib/db/schema_extras.sql +43 −1 View lib/db/schema_extras.sql
@@ -124,4 +124,46 @@
124124ALTER TABLE repo_branch_protection_policy
125125 ADD COLUMN IF NOT EXISTS branch_pattern TEXT NULL;
126126CREATE INDEX IF NOT EXISTS idx_meet_participants_meet_user ON meet_participants (meet_id, user_id);
127CREATE INDEX IF NOT EXISTS idx_meet_participants_user_joined ON meet_participants (user_id, joined_at DESC);
No newline at end of file
127CREATE INDEX IF NOT EXISTS idx_meet_participants_user_joined ON meet_participants (user_id, joined_at DESC);
128
129-- ---------------------------------------------------------------------------
130-- Space ownership invariant.
131-- Every space owner must also have a membership row so owner-managed views,
132-- including the community settings member list, can represent the owner.
133-- This repairs spaces created before owner membership was inserted atomically.
134-- ---------------------------------------------------------------------------
135INSERT INTO space_members (
136 space_id,
137 user_id,
138 status,
139 joined_at,
140 created_at,
141 updated_at,
142 message_notifications,
143 muted,
144 mobile_push,
145 suppress_everyone,
146 suppress_roles,
147 hide_muted_channels
148)
149SELECT
150 spaces.id,
151 spaces.owner_id,
152 0,
153 spaces.created_at,
154 spaces.created_at,
155 spaces.updated_at,
156 0,
157 FALSE,
158 FALSE,
159 FALSE,
160 FALSE,
161 FALSE
162FROM spaces
163WHERE NOT EXISTS (
164 SELECT 1
165 FROM space_members
166 WHERE space_members.space_id = spaces.id
167 AND space_members.user_id = spaces.owner_id
168)
169ON CONFLICT (space_id, user_id) DO NOTHING;
Modified lib/email/runtime.rs lib/email/runtime.rs +3 −1 View lib/email/runtime.rs
@@ -57,7 +57,9 @@
5757async fn ensure_email_topics(queue: &Queue) -> Result<(), EmailError> {
5858 queue.ensure_typed_topic::<SendEmailRequest>().await?;
5959 queue.ensure_typed_topic::<SendBatchEmailRequest>().await?;
60 queue.ensure_typed_topic::<RenderAndSendEmailRequest>().await?;
60 queue
61 .ensure_typed_topic::<RenderAndSendEmailRequest>()
62 .await?;
6163 queue.ensure_typed_topic::<EmailSent>().await?;
6264 queue.ensure_typed_topic::<EmailFailed>().await?;
6365 info!("email topics ensured");
Modified lib/obs/examples/smoke.rs lib/obs/examples/smoke.rs +2 −2 View lib/obs/examples/smoke.rs
@@ -32,8 +32,8 @@
3232 tracing::info!(message = "smoke log line", key = "value");
3333 metrics::counter!("smoke_counter_total", "signal" => "metrics").increment(1);
3434 tracing::info!("smoke span emitted");
35 }
36 drop(span);
35 }
36 drop(span);
3737
3838 guard.flush();
3939 drop(guard);
Modified lib/obs/guard.rs lib/obs/guard.rs +1 −1 View lib/obs/guard.rs
@@ -22,7 +22,7 @@
2222 self.meter = Some(provider);
2323 }
2424
25 pub fn flush(&self) {
25 pub fn flush(&self) {
2626 if let Some(provider) = &self.tracer {
2727 let _ = provider.force_flush();
2828 }
Modified lib/queue/admin.rs lib/queue/admin.rs +2 −1 View lib/queue/admin.rs
@@ -19,7 +19,8 @@
1919 for (k, v) in &cfg.additional_properties {
2020 client_cfg.set(k, v);
2121 }
22 let admin: AdminClient<DefaultClientContext> = client_cfg.create().map_err(QueueError::Kafka)?;
22 let admin: AdminClient<DefaultClientContext> =
23 client_cfg.create().map_err(QueueError::Kafka)?;
2324
2425 let new_topics: Vec<NewTopic<'_>> = topics
2526 .iter()
Modified lib/webhook/dispatcher.rs lib/webhook/dispatcher.rs +22 −26 View lib/webhook/dispatcher.rs
@@ -22,8 +22,14 @@
2222
2323#[derive(Debug, Clone)]
2424enum DeliveryOutcome {
25 Success { status: i32, body: Option<String> },
26 Failed { status: Option<i32>, body: Option<String> },
25 Success {
26 status: i32,
27 body: Option<String>,
28 },
29 Failed {
30 status: Option<i32>,
31 body: Option<String>,
32 },
2733}
2834
2935#[derive(Debug, Clone)]
@@ -54,12 +60,12 @@
5460}
5561
5662impl WebhookDispatcher {
57 #[must_use]
63 #[must_use]
5864 pub fn new(db: Db, http: reqwest::Client, retry: RetryPolicy) -> Self {
5965 Self::with_concurrency(db, http, retry, 32)
6066 }
6167
62 #[must_use]
68 #[must_use]
6369 pub fn with_concurrency(
6470 db: Db,
6571 http: reqwest::Client,
@@ -76,11 +82,11 @@
7682 }
7783 }
7884
79 pub fn from_config(cfg: &WebhookConfig, db: Db) -> Result<Self, reqwest::Error> {
85 pub fn from_config(cfg: &WebhookConfig, db: Db) -> Result<Self, reqwest::Error> {
8086 Self::from_config_shared(cfg, db)
8187 }
8288
83 #[must_use]
89 #[must_use]
8490 pub fn with_defaults(db: Db) -> Self {
8591 let cfg = WebhookConfig::default();
8692 Self::from_config_shared(&cfg, db).expect("default webhook config always builds")
@@ -110,7 +116,7 @@
110116 ))
111117 }
112118
113 pub async fn drain(&self) {
119 pub async fn drain(&self) {
114120 self.draining.store(true, Ordering::Release);
115121 let mut set = {
116122 let mut guard = self.in_flight.lock().await;
@@ -180,13 +186,7 @@
180186 Ok(dispatched)
181187 }
182188
183 fn spawn_delivery(
184 &self,
185 webhook_id: Uuid,
186 url: String,
187 signature: String,
188 payload: Vec<u8>,
189 ) {
189 fn spawn_delivery(&self, webhook_id: Uuid, url: String, signature: String, payload: Vec<u8>) {
190190 let db = self.db.clone();
191191 let http = self.http.clone();
192192 let retry = self.retry.clone();
@@ -308,10 +308,9 @@
308308 if let Ok(secs) = value.trim().parse::<u64>() {
309309 return Some(Duration::from_secs(secs));
310310 }
311 if let Ok(ts) = time::OffsetDateTime::parse(
312 value.trim(),
313 &time::format_description::well_known::Rfc2822,
314 ) {
311 if let Ok(ts) =
312 time::OffsetDateTime::parse(value.trim(), &time::format_description::well_known::Rfc2822)
313 {
315314 let now = time::OffsetDateTime::now_utc();
316315 if let Ok(diff) = (ts - now).try_into() {
317316 return Some(diff);
@@ -328,7 +327,9 @@
328327 if let Some(s) = retry_after {
329328 return s.min(retry.max_delay).max(retry.base_delay);
330329 }
331 let exp = retry.base_delay.saturating_mul(1u32 << (attempt - 1).min(20));
330 let exp = retry
331 .base_delay
332 .saturating_mul(1u32 << (attempt - 1).min(20));
332333 let cap = exp.min(retry.max_delay);
333334 let millis = cap.as_millis().min(u128::from(u64::MAX)) as u64;
334335 if millis == 0 {
@@ -338,12 +339,7 @@
338339 Duration::from_millis(jittered)
339340}
340341
341async fn persist_delivery(
342 db: &Db,
343 webhook_id: Uuid,
344 payload: &[u8],
345 outcome: &DeliveryOutcome,
346) {
342async fn persist_delivery(db: &Db, webhook_id: Uuid, payload: &[u8], outcome: &DeliveryOutcome) {
347343 let (status, response_status, response_body) = match outcome {
348344 DeliveryOutcome::Success { status, body } => (
349345 webhook_deliveries::WebhookDeliveryStatus::Success,
@@ -382,4 +378,4 @@
382378 }
383379 arr.iter()
384380 .any(|e| e.as_str() == Some("push") || e.as_str() == Some("*"))
385}
No newline at end of file
381}