refactor: replace Uuid with i64 for user identifiers across multiple files
ZhenYi committed
This change is very large, so some diffs are not shown. Clone the repository to see the full change.
Modified app/aihost/src/main.rs app/aihost/src/main.rs +1 −1 View app/aihost/src/main.rs
@@ -224,7 +224,7 @@
224224 }
225225 };
226226 info!(%addr, "aihost health listening");
227 let router = export::health::router_with(Some(database), redis);
227 let router = export::health::router_with(Some(database), redis, None);
228228 let _ = axum::serve(listener, router)
229229 .with_graceful_shutdown(async move { cancel.cancelled().await })
230230 .await;
Modified app/apihost/src/main.rs app/apihost/src/main.rs +0 −1 View app/apihost/src/main.rs
@@ -37,7 +37,6 @@
3737 );
3838
3939 tokio::spawn(chat::worker::run_background_tasks(state.clone()));
40 tokio::spawn(auth::worker::run_deletion_executor(state.clone()));
4140
4241 // Web Push 消费者:与其它后台任务一起挂在 apihost 上,不为它单开进程。
4342 // 配置非法(密钥缺失或公私钥不成对)时直接报错退出——静默不投递比启动
Modified app/apihost/Cargo.toml app/apihost/Cargo.toml +1 −1 View app/apihost/Cargo.toml
@@ -10,7 +10,7 @@
1010chat = { workspace = true }
1111push = { workspace = true }
1212export = { workspace = true }
13session-layer = { workspace = true }
13jwt = { workspace = true }
1414config = { workspace = true }
1515axum = { workspace = true }
1616tokio = { workspace = true, features = ["signal", "net", "macros", "rt-multi-thread"] }
Modified app/im/src/frames.rs app/im/src/frames.rs +1 −1 View app/im/src/frames.rs
@@ -29,7 +29,7 @@
2929#[serde(tag = "type", rename_all = "snake_case")]
3030pub enum ServerFrame {
3131 Ready {
32 user_id: Uuid,
32 user_id: i64,
3333 session_id: Uuid,
3434 },
3535 Event {
Modified app/im/src/gateway.rs app/im/src/gateway.rs +39 −32 View app/im/src/gateway.rs
@@ -10,13 +10,13 @@
1010use hyper::{Method, Request, Response, StatusCode};
1111use hyper_util::rt::TokioIo;
1212use sea_orm::{ConnectionTrait, DbBackend, Statement};
13use session_layer::SessionGuard;
1413use tokio_tungstenite::WebSocketStream;
1514use tokio_tungstenite::tungstenite::handshake::derive_accept_key;
1615use tokio_tungstenite::tungstenite::protocol::{Role, WebSocketConfig};
1716use uuid::Uuid;
1817
1918use crate::jwt::consume_jti;
19use std::sync::{Arc, OnceLock};
2020use crate::socket::{ServerWs, handle_socket};
2121
2222const WS_PATH: &str = "/ws/v1";
@@ -166,24 +166,23 @@
166166 let Some(token) = extract_jwt(headers) else {
167167 return bad("missing-protocol", None);
168168 };
169 let secret = service
170 .config()
171 .auth
172 .chat_jwt_sec
173 .clone()
174 .filter(|value| !value.is_empty())
175 .unwrap_or_default();
176 let claims = match export::jwt::verify_app_jwt(&secret, "chat", &token) {
169 let Some(verifier) = verifier(&service.config().oauth) else {
170 tracing::warn!("ws reject: oauth verifier is unavailable");
171 return text_response(StatusCode::FORBIDDEN, "forbidden");
172 };
173 let claims = match verifier
174 .verify_service(&token, verifier.chat_audience())
175 .await
176 {
177177 Ok(claims) => claims,
178178 Err(error) => {
179 tracing::warn!(%error, "ws reject: jwt verify failed");
179 tracing::warn!(%error, "ws reject: service token verify failed");
180180 return text_response(StatusCode::FORBIDDEN, "forbidden");
181181 }
182182 };
183 let (Some(uid), Some(jti)) = (claims.uid(), claims.jti_uuid()) else {
184 tracing::warn!("ws reject: malformed jwt claims");
185 return text_response(StatusCode::FORBIDDEN, "forbidden");
186 };
183 let uid = claims.sub;
184 let jti = claims.jti.clone();
185 let token_for_introspect = token.clone();
187186
188187 let on_upgrade = hyper::upgrade::on(req);
189188 let max_frame_bytes = service.config().chat.ws.max_frame_bytes;
@@ -218,34 +217,42 @@
218217 .duration_since(std::time::UNIX_EPOCH)
219218 .unwrap_or_default()
220219 .as_secs() as i64;
221 if !consume_jti(&service.state.redis, jti, claims.exp - now).await {
220 if !consume_jti(&service.state.redis, &jti, claims.exp - now).await {
222221 tracing::warn!(%jti, "ws reject: jwt already used or redis unavailable");
223222 let _ = ws.close(None).await;
224223 return;
225224 }
226 let session = match service.lookup_session_with_user(&claims.token_hash).await {
227 Ok(Some(session)) => session,
228 _ => {
229 tracing::warn!("ws reject: session lookup failed");
225 if let Some(client) = service.state.kenauth.as_ref() {
226 let active = client
227 .introspect_token(&token_for_introspect)
228 .await
229 .map(|response| {
230 response.active
231 && response
232 .principal
233 .as_ref()
234 .is_some_and(|principal| principal.user_id == uid)
235 })
236 .unwrap_or(false);
237 if !active {
238 tracing::warn!(%uid, "ws reject: service token is not active");
230239 let _ = ws.close(None).await;
231240 return;
232241 }
233 };
234 let authorized = session.user_id == uid
235 && !session.is_revoked
236 && time::OffsetDateTime::from_unix_timestamp(now)
237 .map(|n| session.expires_at > n)
238 .unwrap_or(false)
239 && session.is_active
240 && !session.prohibit_login
241 && !session.is_restricted;
242 if !authorized {
243 tracing::warn!("ws reject: session not authorized");
244 let _ = ws.close(None).await;
245 return;
246242 }
247243 handle_socket(ws, service, uid, Uuid::now_v7()).await;
248244 });
249245
250246 response
251247}
248
249static VERIFIER: OnceLock<Option<Arc<jwt::JwtVerifier>>> = OnceLock::new();
250
251fn verifier(config: &config::oauth::OAuthConfig) -> Option<&'static Arc<jwt::JwtVerifier>> {
252 if !config.enabled {
253 return None;
254 }
255 VERIFIER
256 .get_or_init(|| jwt::JwtVerifier::new(config).ok())
257 .as_ref()
258}
Modified app/im/src/jwt.rs app/im/src/jwt.rs +1 −2 View app/im/src/jwt.rs
@@ -1,7 +1,6 @@
11use export::AppRedis;
2use uuid::Uuid;
32
4pub async fn consume_jti(redis: &AppRedis, jti: Uuid, ttl_secs: i64) -> bool {
3pub async fn consume_jti(redis: &AppRedis, jti: &str, ttl_secs: i64) -> bool {
54 use export::RedisConnection;
65 let mut conn = redis.connection_clone();
76 let result: Result<Option<String>, redis::RedisError> = match &mut conn {
Modified app/im/src/socket.rs app/im/src/socket.rs +4 −4 View app/im/src/socket.rs
@@ -51,7 +51,7 @@
5151 false
5252}
5353
54pub async fn handle_socket(socket: ServerWs, service: ChatService, uid: Uuid, session_id: Uuid) {
54pub async fn handle_socket(socket: ServerWs, service: ChatService, uid: i64, session_id: Uuid) {
5555 let _connection = export::metrics::ImConnectionGuard::new();
5656 let (mut sink, mut stream) = socket.split();
5757
@@ -273,7 +273,7 @@
273273}
274274
275275impl OnlineMarker {
276 async fn claim(service: &ChatService, uid: Uuid) -> Self {
276 async fn claim(service: &ChatService, uid: i64) -> Self {
277277 let ttl_secs = service.config().chat.online_ttl_secs();
278278 let marker = Self {
279279 key: export::push::online_key(uid),
@@ -335,7 +335,7 @@
335335async fn handle_client_frame<S>(
336336 sink: &mut S,
337337 service: &ChatService,
338 uid: Uuid,
338 uid: i64,
339339 frame: ClientFrame,
340340 subscriptions: &mut HashMap<Uuid, Subscription>,
341341 last_seq: &mut HashMap<Uuid, i64>,
@@ -500,7 +500,7 @@
500500
501501async fn service_can_read(
502502 service: &ChatService,
503 uid: Uuid,
503 uid: i64,
504504 room_id: Uuid,
505505) -> Result<(), ChatError> {
506506 chat::share::lookup::require_room_readable(service, room_id, uid)
Modified app/im/Cargo.toml app/im/Cargo.toml +1 −1 View app/im/Cargo.toml
@@ -8,7 +8,7 @@
88chat = { workspace = true }
99config = { workspace = true }
1010export = { workspace = true }
11session-layer = { workspace = true }
11jwt = { workspace = true }
1212anyhow = { workspace = true }
1313bytes = { workspace = true }
1414futures = { workspace = true }
Modified app/mails/src/main.rs app/mails/src/main.rs +1 −1 View app/mails/src/main.rs
@@ -53,7 +53,7 @@
5353 .await
5454 .with_context(|| format!("binding health {addr}"))?;
5555 info!(%addr, "mails health listening");
56 let router = export::health::router_with(None, redis);
56 let router = export::health::router_with(None, redis, None);
5757 health_handle = Some(tokio::spawn(async move {
5858 let _ = axum::serve(listener, router)
5959 .with_graceful_shutdown(async {
Modified app/mcp/src/main.rs app/mcp/src/main.rs +1 −1 View app/mcp/src/main.rs
@@ -31,7 +31,7 @@
3131 warn!(error = %err, "failed to init meter provider; metrics disabled");
3232 }
3333
34 let health = export::health::router_with(Some(state.database.clone()), state.redis.clone());
34 let health = export::health::router_with(Some(state.database.clone()), state.redis.clone(), state.kenauth.clone());
3535 let mcp = mcp_api::router(state).await;
3636
3737 let router: Router = Router::new()
Modified app/synchost/src/main.rs app/synchost/src/main.rs +1 −1 View app/synchost/src/main.rs
@@ -83,7 +83,7 @@
8383 .await
8484 .with_context(|| format!("binding health {health_addr}"))?;
8585 info!(%health_addr, "synchost health listening");
86 let health_router = export::health::router_with(Some(db.clone()), redis.clone());
86 let health_router = export::health::router_with(Some(db.clone()), redis.clone(), None);
8787 let health_cancel = cancel.clone();
8888 tokio::spawn(async move {
8989 let _ = axum::serve(health_listener, health_router)
Modified app/webhook/src/main.rs app/webhook/src/main.rs +1 −0 View app/webhook/src/main.rs
@@ -80,6 +80,7 @@
8080 .merge(export::health::router_with(
8181 Some(service.state.database.clone()),
8282 service.state.redis.clone(),
83 service.state.kenauth.clone(),
8384 ))
8485 .layer(export::telemetry::http_trace_layer())
8586 .layer(export::telemetry::metrics_layer())
Modified charts/core/templates/NOTES.txt charts/core/templates/NOTES.txt +1 −1 View charts/core/templates/NOTES.txt
@@ -19,7 +19,7 @@
1919 kubectl -n {{ .Release.Namespace }} logs job/{{ include "gitdata.fullname" . }}-migrate
2020
2121Reminders:
22 - secret.env 中 APP_SESSION__KEY / APP_POW__MAC_KEY / APP_AUTH__TOTP_ENCRYPTION_KEY 必填;
22 - secret.env 中 APP_OAUTH__RPC_KEY / APP_INTERNAL__SERVICE_KEY / APP_POW__MAC_KEY / APP_AUTH__TOTP_ENCRYPTION_KEY 必填;
2323 生产环境建议 --set secret.existingSecret=<name> 并用外部 Secret 管理。
2424 - repos PVC 需 RWX(NFS/CephFS/EFS);无 RWX 时将 githost/synchost 各缩到 1 副本并固定同节点。
2525 - 磁盘上的 config/local.toml 含真实密钥,部署前请轮换。
Modified charts/core/values.yaml charts/core/values.yaml +19 −13 View charts/core/values.yaml
@@ -43,11 +43,14 @@
4343 APP_PUSH__VAPID_SUBJECT: "mailto:ops@example.com"
4444 # 自建 forge 的内网地址需显式加白,例如:
4545 # APP_IMPORT__ALLOWED_HOSTS: '["gitea.internal","gitlab.svc.cluster.local"]'
46 # 多副本必须置 0:axum_session 默认在每个 pod 内存缓存会话最长 60 分钟,
47 # 副本间内存快照互不可见,会导致跨副本丢失 pow challenge / 2FA / 登录态。
48 APP_SESSION__MEMORY_LIFESPAN_SECS: "0"
49 # 上面的 0 会让无会话请求(/healthz、扫描器)产生一条无害的 warn 噪音,压掉它。
50 RUST_LOG: "info,session::session_store=error"
46 # 认证中心:浏览器走 OIDC,服务间走 gRPC(x-rpc-key)。
47 APP_OAUTH__ENABLED: "true"
48 APP_OAUTH__ISSUER: "https://auth.gitdata.ai"
49 APP_OAUTH__CLIENT_ID: "gitdata-web"
50 APP_OAUTH__REDIRECT_URI: "https://gitdata.ai/api/v1/auth/callback"
51 APP_OAUTH__AUDIENCE: "gitdata.ai"
52 APP_OAUTH__GRPC_ENDPOINT: "http://kenoauth:18082"
53 RUST_LOG: "info"
5154
5255# 敏感配置(渲染为 Secret)。注意 AppConfig 的 Debug 会带出密钥,
5356# 切勿在任何日志里 {:?} 打印配置。
@@ -57,13 +60,13 @@
5760 existingSecret: ""
5861 env:
5962 APP_DATABASE__URL: "postgres://postgres:CHANGE_ME@postgres:5432/gitdata"
60 APP_SESSION__KEY: "" # >=64 字节随机串
63 APP_OAUTH__RPC_KEY: "" # 与 KenOAuth auth.oauth.rpc_key 一致,>=32 字节
64 APP_INTERNAL__SERVICE_KEY: "" # /api/v1/internal/* 服务密钥,>=32 字节
6165 APP_POW__MAC_KEY: "" # base64 >=32 字节
6266 APP_AUTH__TOTP_ENCRYPTION_KEY: "" # base64 >=32 字节(实测必填)
6367 APP_S3__ACCESS_KEY_ID: ""
6468 APP_S3__SECRET_ACCESS_KEY: ""
6569 APP_EMAIL__SMTP_PASSWORD: ""
66 APP_AUTH__CHAT_JWT_SEC: "" # >=32 字节,chat WS 握手 JWT 密钥
6770 APP_SEARCH__API_KEY: ""
6871 APP_IMPORT__TOKEN_SECRET: "" # >=32 字节,加密导入任务里的源站令牌
6972 APP_CI__SECRET_ENCRYPTION_KEY: "" # base64 >=32 字节,CI secrets / webhook 加密密钥
@@ -119,16 +122,19 @@
119122 enabled: true
120123 minAvailable: 1
121124
122adminhost:
123 enabled: true
125oauth:
126 enabled: false
127 image: harbor.gitdata.me/app/kenoauth:latest
128 pullPolicy: IfNotPresent
124129 replicaCount: 1
125 port: 3004
126 grpcPort: 3014
127 grpcTrustedKeys: []
130 httpPort: 18081
131 grpcPort: 18082
132 # Secret containing config.yaml (mounted at /app/config). Empty = env-only config.
133 existingSecret: ""
134 extraEnv: []
128135 resources:
129136 requests: { cpu: 100m, memory: 128Mi }
130137 limits: { memory: 512Mi }
131 extraEnv: []
132138
133139githost:
134140 enabled: true
Modified charts/prod/values/prod.yaml charts/prod/values/prod.yaml +5 −8 View charts/prod/values/prod.yaml
@@ -26,8 +26,7 @@
2626 APP_PUSH__VAPID_SUBJECT: "mailto:bot@mail.gitdata.ai"
2727 APP_WEB__WEB_BASE_URL: "https://gitdata.ai"
2828 APP_WEB__CORS_ALLOW_ORIGINS: '["https://gitdata.ai"]'
29 APP_SESSION__COOKIE_SECURE: "true"
30 APP_SESSION__MEMORY_LIFESPAN_SECS: "0"
29 APP_OAUTH__COOKIE_SECURE: "true"
3130 APP_GIT__BASE_URL: "https://gitdata.ai"
3231 APP_LIVEKIT__RECORDING_ENABLED: "false"
3332 APP_EMAIL__ENABLED: "true"
@@ -71,7 +70,7 @@
7170 APP_SANDBOX_RPC__FANOUT_ENDPOINT: "https://sandbox-agent.gitdata.ai"
7271 APP_SANDBOX_RPC__REQUEST_TIMEOUT_SECS: "30"
7372 APP_SANDBOX_RPC__STREAM_TOKEN_TTL_SECS: "600"
74 RUST_LOG: "info,session::session_store=error"
73 RUST_LOG: "info"
7574 OTEL_TRACES_SAMPLER: "parentbased_traceidratio"
7675 OTEL_TRACES_SAMPLER_ARG: "0.2"
7776
@@ -138,10 +137,8 @@
138137 annotations:
139138 metallb.universe.tf/loadBalancerIPs: "47.239.98.8"
140139
141adminhost:
142 enabled: true
143 grpcTrustedKeys: []
144140
141
145142mcp:
146143 enabled: true
147144 replicaCount: 2
@@ -167,8 +164,8 @@
167164
168165ai:
169166 enabled: true
170 baseUrl: "https://api.deepseek.com"
171 model: "deepseek-flash"
167 baseUrl: "https://api.commandcode.ai/provider/v1"
168 model: "deepseek/deepseek-v4.1-flash"
172169 reviewEnabled: true
173170 summaryEnabled: true
174171 releaseNotesEnabled: true
Modified config/default.toml config/default.toml +29 −35 View config/default.toml
@@ -66,30 +66,6 @@
6666sqlx_logging = false
6767replicas = []
6868
69[session]
70mode = "opt_in"
71cookie_name = "gitdata_session"
72cookie_domain = ""
73cookie_path = "/"
74cookie_http_only = true
75cookie_secure = false
76cookie_same_site = "lax"
77# Long enough to serve the 30-day "keep me signed in" window
78# (max_lifespan_secs); non-remember logins get a session cookie.
79cookie_max_age_secs = 2592000
80cookie_prefix_host = false
81cookie_encode_ip_and_user_agent = false
82lifespan_secs = 604800
83max_lifespan_secs = 2592000
84memory_lifespan_secs = 1800
85table_name = "session"
86key = ""
87database_key = ""
88always_save = false
89db_update_interval_secs = 60
90purge_database_update_secs = 86400
91enable_bloom_filter = true
92
9369[pow]
9470difficulty = 16
9571max_difficulty = 24
@@ -112,7 +88,6 @@
11288max_verify_attempts = 5
11389min_age = 13
11490totp_encryption_key = ""
115chat_jwt_sec = ""
11691
11792[[auth.agreements]]
11893key = "terms"
@@ -285,8 +260,36 @@
285260enabled = true
286261bind_addr = "0.0.0.0"
287262port = 3000
288admin_uids = []
289263
264[internal]
265# /api/v1/internal/* 的服务密钥,至少 32 字符;留空则拒绝所有 internal 请求。
266service_key = ""
267
268[oauth]
269enabled = false
270issuer = "http://127.0.0.1:18081"
271client_id = "gitdata-web"
272client_secret = ""
273redirect_uri = "http://localhost:5173/api/v1/auth/callback"
274token_endpoint = ""
275jwks_url = ""
276audience = "gitdata.ai"
277grpc_endpoint = "http://127.0.0.1:18082"
278rpc_key = ""
279session_cookie = "gitdata_session"
280refresh_cookie = "gitdata_refresh"
281cookie_domain = ""
282cookie_path = "/"
283cookie_secure = false
284cookie_same_site = "lax"
285access_ttl_secs = 600
286refresh_cookie_max_age_secs = 2592000
287recheck_secs = 60
288leeway_secs = 30
289jwks_cache_secs = 3600
290jwks_min_refresh_secs = 30
291http_timeout_ms = 2000
292chat_audience = "chat.gitdata.ai"
290293[githost]
291294enabled = true
292295http_bind_addr = "0.0.0.0"
@@ -309,15 +312,6 @@
309312
310313[health.aihost]
311314port = 3006
312[admin]
313enabled = true
314bind_addr = "0.0.0.0"
315port = 3004
316grpc_port = 3014
317grpc_trusted_keys = []
318cors_allow_origins = []
319cors_allow_credentials = false
320
321315[ci]
322316enabled = false
323317secret_encryption_key = ""
Modified lib/agent/src/bot.rs lib/agent/src/bot.rs +101 −58 View lib/agent/src/bot.rs
@@ -1,66 +1,109 @@
11use config::ai::AiConfig;
2use export::AppState;
23use models::users::user::{self, ProfileVisibility};
3use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, Set};
4use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
45use time::OffsetDateTime;
5use uuid::Uuid;
66
7/// 解析(必要时创建)AI 系统用户。
8///
9/// 该用户以 `bot=true, system=true` 标记,无口令、不可登录,仅用于承载
10/// AI 评审与评论的作者身份。
11pub async fn resolve_bot_user(
12 db: &DatabaseConnection,
13 cfg: &AiConfig,
14) -> Result<Uuid, sea_orm::DbErr> {
7/// Resolve or create the AI bot account in KenOAuth, then project it locally.
8pub async fn resolve_bot_user(state: &AppState, cfg: &AiConfig) -> Result<i64, sea_orm::DbErr> {
159 let username = cfg.bot_username.trim();
16 let lower = username.to_lowercase();
17 if let Some(row) = user::Entity::find()
18 .filter(user::Column::LowerName.eq(&lower))
19 .one(db)
20 .await?
21 {
22 return Ok(row.uid);
10 if username.is_empty() {
11 return Err(sea_orm::DbErr::Custom(
12 "ai.bot_username must not be empty".to_string(),
13 ));
2314 }
24 let uid = Uuid::now_v7();
15 ensure_bot(
16 state,
17 username,
18 cfg.bot_display_name.trim(),
19 true,
20 )
21 .await
22}
23
24pub async fn ensure_bot(
25 state: &AppState,
26 username: &str,
27 displayname: &str,
28 system: bool,
29) -> Result<i64, sea_orm::DbErr> {
30 let client = state
31 .kenauth
32 .as_ref()
33 .ok_or_else(|| sea_orm::DbErr::Custom("kenoauth is not configured".to_string()))?;
34 let public = client
35 .ensure_bot_user(username, displayname, system)
36 .await
37 .map_err(|error| sea_orm::DbErr::Custom(error.to_string()))?;
38 upsert_local_bot(state, &public, system).await?;
39 Ok(public.user_id)
40}
41
42pub async fn upsert_local_bot(
43 state: &AppState,
44 public: &kenauth::proto::common::v1::UserPublic,
45 system: bool,
46) -> Result<user::Model, sea_orm::DbErr> {
47 let db = state.database.write();
2548 let now = OffsetDateTime::now_utc();
26 let created = user::ActiveModel {
27 uid: Set(uid),
28 username: Set(username.to_string()),
29 lower_name: Set(lower.clone()),
30 display_name: Set(cfg.bot_display_name.trim().to_string()),
31 avatar_url: Set(String::new()),
32 is_active: Set(true),
33 is_restricted: Set(false),
34 prohibit_login: Set(true),
35 visibility: Set(ProfileVisibility::Public),
36 bot: Set(true),
37 system: Set(true),
38 last_login_at: Set(None),
39 last_login_ip_hash: Set(None),
40 version: Set(1),
41 passwd_hash: Set(None),
42 password_last_changed_at: Set(None),
43 passwd_version: Set(None),
44 totp_encrypted_secret: Set(None),
45 totp_enabled: Set(false),
46 totp_last_used_at: Set(None),
47 totp_last_counter: Set(None),
48 timezone: Set(None),
49 locale: Set(None),
50 language: Set(None),
51 created_at: Set(now),
52 updated_at: Set(now),
53 }
54 .insert(db)
55 .await;
56 match created {
57 Ok(row) => Ok(row.uid),
58 // 并发创建时另一进程已插入,回查即可。
59 Err(_) => user::Entity::find()
60 .filter(user::Column::LowerName.eq(&lower))
61 .one(db)
49 let lower = public.username.to_lowercase();
50 let display_name = if public.displayname.trim().is_empty() {
51 public.username.clone()
52 } else {
53 public.displayname.clone()
54 };
55
56 let existing = user::Entity::find()
57 .filter(user::Column::LowerName.eq(&lower))
58 .one(db)
59 .await?;
60 let row = match existing {
61 Some(row) => {
62 let mut active: user::ActiveModel = row.into();
63 active.uid = Set(public.user_id);
64 active.username = Set(public.username.clone());
65 active.lower_name = Set(lower);
66 active.display_name = Set(display_name);
67 active.avatar_url = Set(public.avatar_url.clone());
68 active.is_active = Set(true);
69 active.prohibit_login = Set(true);
70 active.bot = Set(true);
71 active.system = Set(system);
72 active.updated_at = Set(now);
73 active.update(db).await?
74 }
75 None => {
76 user::ActiveModel {
77 uid: Set(public.user_id),
78 username: Set(public.username.clone()),
79 lower_name: Set(lower),
80 display_name: Set(display_name),
81 avatar_url: Set(public.avatar_url.clone()),
82 is_active: Set(true),
83 is_restricted: Set(false),
84 prohibit_login: Set(true),
85 visibility: Set(ProfileVisibility::Public),
86 bot: Set(true),
87 system: Set(system),
88 last_login_at: Set(None),
89 last_login_ip_hash: Set(None),
90 version: Set(1),
91 passwd_hash: Set(None),
92 password_last_changed_at: Set(None),
93 passwd_version: Set(None),
94 totp_encrypted_secret: Set(None),
95 totp_enabled: Set(false),
96 totp_last_used_at: Set(None),
97 totp_last_counter: Set(None),
98 timezone: Set(None),
99 locale: Set(None),
100 language: Set(None),
101 created_at: Set(now),
102 updated_at: Set(now),
103 }
104 .insert(db)
62105 .await?
63 .map(|row| row.uid)
64 .ok_or_else(|| sea_orm::DbErr::RecordNotFound("ai bot user".to_string())),
65 }
66}
106 }
107 };
108 Ok(row)
109}
No newline at end of file
Modified lib/agent/src/language_pref.rs lib/agent/src/language_pref.rs +3 −3 View lib/agent/src/language_pref.rs
@@ -10,7 +10,7 @@
1010 /// 按「触发者 → 空间所有者 → 配置缺省」的顺序解析输出语言。
1111 pub(crate) async fn resolve_language(
1212 &self,
13 actor_id: Option<Uuid>,
13 actor_id: Option<i64>,
1414 fallback: Option<&str>,
1515 repo_id: Uuid,
1616 ) -> String {
@@ -33,7 +33,7 @@
3333 }
3434
3535 /// 读取用户偏好语言;`language` 优先,其次 `locale`。
36 async fn user_language(&self, uid: Uuid) -> Option<String> {
36 async fn user_language(&self, uid: i64) -> Option<String> {
3737 use models::users::user;
3838 use sea_orm::EntityTrait;
3939 let row = user::Entity::find_by_id(uid)
@@ -54,7 +54,7 @@
5454 }
5555
5656 /// 仓库所属空间的所有者 uid。
57 async fn repo_owner(&self, repo_id: Uuid) -> Option<Uuid> {
57 async fn repo_owner(&self, repo_id: Uuid) -> Option<i64> {
5858 use models::repo::repos;
5959 use sea_orm::EntityTrait;
6060 let repo = repos::Entity::find_by_id(repo_id)
Modified lib/agent/src/notify.rs lib/agent/src/notify.rs +2 −2 View lib/agent/src/notify.rs
@@ -12,12 +12,12 @@
1212 pub(crate) async fn notify_pull_ai(
1313 &self,
1414 pull: &pull_requests::Model,
15 bot: Uuid,
15 bot: i64,
1616 language: &str,
1717 summary: &str,
1818 findings: usize,
1919 ) {
20 let mut recipients: Vec<Uuid> = vec![pull.author_id];
20 let mut recipients: Vec<i64> = vec![pull.author_id];
2121 match pull_request_reviewer::Entity::find()
2222 .filter(pull_request_reviewer::Column::PullRequestId.eq(pull.id))
2323 .all(self.db_read())
Modified lib/agent/src/service.rs lib/agent/src/service.rs +2 −2 View lib/agent/src/service.rs
@@ -230,7 +230,7 @@
230230 prompt_tokens: Option<i32>,
231231 completion_tokens: Option<i32>,
232232 ) -> AgentResult<()> {
233 let bot = crate::bot::resolve_bot_user(self.db_write(), &self.config().ai)
233 let bot = crate::bot::resolve_bot_user(&self.state, &self.config().ai)
234234 .await
235235 .map_err(db_err)?;
236236 let now = OffsetDateTime::now_utc();
@@ -412,7 +412,7 @@
412412 return;
413413 }
414414 };
415 let bot = match crate::bot::resolve_bot_user(self.db_write(), &self.config().ai).await {
415 let bot = match crate::bot::resolve_bot_user(&self.state, &self.config().ai).await {
416416 Ok(uid) => uid,
417417 Err(error) => {
418418 tracing::warn!(%error, "skip commit status: bot user unavailable");
Modified lib/agent/Cargo.toml lib/agent/Cargo.toml +1 −0 View lib/agent/Cargo.toml
@@ -6,6 +6,7 @@
66[dependencies]
77config = { workspace = true }
88export = { workspace = true }
9kenauth = { workspace = true }
910models = { workspace = true }
1011git = { workspace = true }
1112ci-protocol = { workspace = true }
Modified lib/agents/src/handlers/agents.rs lib/agents/src/handlers/agents.rs +2 −2 View lib/agents/src/handlers/agents.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55
66use crate::errors::ApiError;
77use crate::services::agents::{
Modified lib/agents/src/handlers/avatar.rs lib/agents/src/handlers/avatar.rs +2 −2 View lib/agents/src/handlers/avatar.rs
@@ -4,8 +4,8 @@
44use axum::http::header::CONTENT_TYPE;
55use axum::response::Redirect;
66use export::rest::{AppResponseData, NoData};
7use session_layer::Session;
8use session_layer::share::session::require_uid;
7use jwt::Session;
8use jwt::share::session::require_uid;
99
1010use crate::errors::ApiError;
1111use crate::services::agents::{AgentDto, AgentPath};
Modified lib/agents/src/handlers/builder.rs lib/agents/src/handlers/builder.rs +2 −2 View lib/agents/src/handlers/builder.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55
66use crate::errors::ApiError;
77use crate::services::builder::{BuilderChatCmd, BuilderChatResult};
Modified lib/agents/src/handlers/chat.rs lib/agents/src/handlers/chat.rs +2 −2 View lib/agents/src/handlers/chat.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55
66use crate::errors::ApiError;
77use crate::services::agents::AgentPath;
Modified lib/agents/src/handlers/collections.rs lib/agents/src/handlers/collections.rs +2 −2 View lib/agents/src/handlers/collections.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55
66use crate::errors::ApiError;
77use crate::services::collections::{
Modified lib/agents/src/services/agents.rs lib/agents/src/services/agents.rs +7 −7 View lib/agents/src/services/agents.rs
@@ -20,7 +20,7 @@
2020 pub id: Uuid,
2121 pub collection_id: Uuid,
2222 pub number: i32,
23 pub author_id: Uuid,
23 pub author_id: i64,
2424 pub author: Option<space::services::spaces::UserDto>,
2525 pub name: String,
2626 pub description: Option<String>,
@@ -117,7 +117,7 @@
117117 )]
118118 pub async fn create_agent(
119119 &self,
120 caller: Uuid,
120 caller: i64,
121121 space_name: &str,
122122 collection_id: Uuid,
123123 cmd: CreateAgentCmd,
@@ -218,7 +218,7 @@
218218
219219 pub async fn list_agents(
220220 &self,
221 caller: Uuid,
221 caller: i64,
222222 space_name: &str,
223223 collection_id: Uuid,
224224 q: ListAgentQuery,
@@ -244,7 +244,7 @@
244244 .all(self.db().read())
245245 .await?;
246246
247 let author_ids: Vec<Uuid> = rows.iter().map(|row| row.author_id).collect();
247 let author_ids: Vec<i64> = rows.iter().map(|row| row.author_id).collect();
248248 let users = self.load_user_briefs(&author_ids).await?;
249249 let items = rows
250250 .into_iter()
@@ -264,7 +264,7 @@
264264
265265 pub async fn get_agent(
266266 &self,
267 caller: Uuid,
267 caller: i64,
268268 space_name: &str,
269269 collection_id: Uuid,
270270 agent_id: Uuid,
@@ -288,7 +288,7 @@
288288 )]
289289 pub async fn update_agent(
290290 &self,
291 caller: Uuid,
291 caller: i64,
292292 space_name: &str,
293293 collection_id: Uuid,
294294 agent_id: Uuid,
@@ -336,7 +336,7 @@
336336 )]
337337 pub async fn delete_agent(
338338 &self,
339 caller: Uuid,
339 caller: i64,
340340 space_name: &str,
341341 collection_id: Uuid,
342342 agent_id: Uuid,
Modified lib/agents/src/services/avatar.rs lib/agents/src/services/avatar.rs +2 −2 View lib/agents/src/services/avatar.rs
@@ -33,7 +33,7 @@
3333 )]
3434 pub async fn upload_agent_avatar(
3535 &self,
36 caller: Uuid,
36 caller: i64,
3737 space_name: &str,
3838 collection_id: Uuid,
3939 agent_id: Uuid,
@@ -95,7 +95,7 @@
9595 /// not re-sign on every request.
9696 pub async fn agent_avatar_url(
9797 &self,
98 caller: Uuid,
98 caller: i64,
9999 space_name: &str,
100100 collection_id: Uuid,
101101 agent_id: Uuid,
Modified lib/agents/src/services/builder.rs lib/agents/src/services/builder.rs +1 −1 View lib/agents/src/services/builder.rs
@@ -232,7 +232,7 @@
232232 )]
233233 pub async fn builder_chat(
234234 &self,
235 caller: Uuid,
235 caller: i64,
236236 space_name: &str,
237237 collection_id: Uuid,
238238 cmd: BuilderChatCmd,
Modified lib/agents/src/services/chat.rs lib/agents/src/services/chat.rs +13 −13 View lib/agents/src/services/chat.rs
@@ -315,7 +315,7 @@
315315 )]
316316 pub async fn create_agent_stream_token(
317317 &self,
318 caller: Uuid,
318 caller: i64,
319319 space_name: &str,
320320 collection_id: Uuid,
321321 agent_id: Uuid,
@@ -353,7 +353,7 @@
353353
354354 async fn conversation(
355355 &self,
356 caller: Uuid,
356 caller: i64,
357357 space_name: &str,
358358 collection_id: Uuid,
359359 agent_id: Uuid,
@@ -378,7 +378,7 @@
378378 )]
379379 pub async fn send_agent_message(
380380 &self,
381 caller: Uuid,
381 caller: i64,
382382 space_name: &str,
383383 collection_id: Uuid,
384384 agent_id: Uuid,
@@ -439,7 +439,7 @@
439439 async fn run_mcp_servers(
440440 &self,
441441 space: &spaces::Model,
442 caller: Uuid,
442 caller: i64,
443443 ) -> Result<Vec<proto::McpServerSpecMessage>, AgentError> {
444444 if !crate::share::authz::can_manage_mcp(self, space, caller).await {
445445 return Err(AgentError::Forbidden);
@@ -480,7 +480,7 @@
480480 async fn authorize_repo(
481481 &self,
482482 space: &spaces::Model,
483 caller: Uuid,
483 caller: i64,
484484 repo: &str,
485485 ) -> Result<String, AgentError> {
486486 let git = git::service::GitService::new(self.state.clone());
@@ -496,7 +496,7 @@
496496
497497 pub async fn list_agent_messages(
498498 &self,
499 caller: Uuid,
499 caller: i64,
500500 space_name: &str,
501501 collection_id: Uuid,
502502 agent_id: Uuid,
@@ -522,7 +522,7 @@
522522
523523 pub async fn list_agent_tool_calls(
524524 &self,
525 caller: Uuid,
525 caller: i64,
526526 space_name: &str,
527527 collection_id: Uuid,
528528 agent_id: Uuid,
@@ -541,7 +541,7 @@
541541
542542 pub async fn list_agent_runs(
543543 &self,
544 caller: Uuid,
544 caller: i64,
545545 space_name: &str,
546546 collection_id: Uuid,
547547 agent_id: Uuid,
@@ -569,7 +569,7 @@
569569 )]
570570 pub async fn cancel_agent_run(
571571 &self,
572 caller: Uuid,
572 caller: i64,
573573 space_name: &str,
574574 collection_id: Uuid,
575575 agent_id: Uuid,
@@ -585,7 +585,7 @@
585585
586586 pub async fn list_agent_approvals(
587587 &self,
588 caller: Uuid,
588 caller: i64,
589589 space_name: &str,
590590 collection_id: Uuid,
591591 agent_id: Uuid,
@@ -613,7 +613,7 @@
613613 )]
614614 pub async fn decide_agent_approval(
615615 &self,
616 caller: Uuid,
616 caller: i64,
617617 space_name: &str,
618618 collection_id: Uuid,
619619 agent_id: Uuid,
@@ -643,7 +643,7 @@
643643
644644 pub async fn list_agent_questions(
645645 &self,
646 caller: Uuid,
646 caller: i64,
647647 space_name: &str,
648648 collection_id: Uuid,
649649 agent_id: Uuid,
@@ -671,7 +671,7 @@
671671 )]
672672 pub async fn answer_agent_questions(
673673 &self,
674 caller: Uuid,
674 caller: i64,
675675 space_name: &str,
676676 collection_id: Uuid,
677677 agent_id: Uuid,
Modified lib/agents/src/services/collections.rs lib/agents/src/services/collections.rs +5 −5 View lib/agents/src/services/collections.rs
@@ -118,7 +118,7 @@
118118 )]
119119 pub async fn create_agent_collection(
120120 &self,
121 caller: Uuid,
121 caller: i64,
122122 space_name: &str,
123123 cmd: CreateAgentCollectionCmd,
124124 ) -> Result<AgentCollectionDto, AgentError> {
@@ -204,7 +204,7 @@
204204
205205 pub async fn list_agent_collections(
206206 &self,
207 caller: Uuid,
207 caller: i64,
208208 space_name: &str,
209209 q: ListAgentCollectionQuery,
210210 ) -> Result<AgentCollectionPage, AgentError> {
@@ -274,7 +274,7 @@
274274
275275 pub async fn get_agent_collection(
276276 &self,
277 caller: Uuid,
277 caller: i64,
278278 space_name: &str,
279279 collection_id: Uuid,
280280 ) -> Result<AgentCollectionDto, AgentError> {
@@ -298,7 +298,7 @@
298298 )]
299299 pub async fn update_agent_collection(
300300 &self,
301 caller: Uuid,
301 caller: i64,
302302 space_name: &str,
303303 collection_id: Uuid,
304304 cmd: UpdateAgentCollectionCmd,
@@ -370,7 +370,7 @@
370370 )]
371371 pub async fn delete_agent_collection(
372372 &self,
373 caller: Uuid,
373 caller: i64,
374374 space_name: &str,
375375 collection_id: Uuid,
376376 ) -> Result<(), AgentError> {
Modified lib/agents/src/services/service.rs lib/agents/src/services/service.rs +3 −15 View lib/agents/src/services/service.rs
@@ -1,15 +1,12 @@
11use std::collections::HashMap;
22use std::sync::Arc;
33
4use async_trait::async_trait;
54use config::AppConfig;
65use export::AppState;
76use export::database::AppDatabase;
87use models::users::user;
98use sandbox_rpc::SandboxRpcClient;
109use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
11use session_layer::SessionError;
12use session_layer::share::guard::{SessionGuard, SessionWithUser};
1310use space::services::spaces::UserDto;
1411use uuid::Uuid;
1512
@@ -62,12 +59,12 @@
6259
6360 pub async fn load_user_briefs(
6461 &self,
65 uids: &[Uuid],
66 ) -> Result<HashMap<Uuid, UserDto>, AgentError> {
62 uids: &[i64],
63 ) -> Result<HashMap<i64, UserDto>, AgentError> {
6764 if uids.is_empty() {
6865 return Ok(HashMap::new());
6966 }
70 let mut set: Vec<Uuid> = uids.to_vec();
67 let mut set: Vec<i64> = uids.to_vec();
7168 set.sort();
7269 set.dedup();
7370 let users = user::Entity::find()
@@ -81,12 +78,3 @@
8178 }
8279}
8380
84#[async_trait]
85impl SessionGuard for AgentsService {
86 async fn lookup_session_with_user(
87 &self,
88 token_hash: &str,
89 ) -> Result<Option<SessionWithUser>, SessionError> {
90 session_layer::lookup_session_with_user(self.db().read(), token_hash).await
91 }
92}
Modified lib/agents/src/share/authz.rs lib/agents/src/share/authz.rs +5 −5 View lib/agents/src/share/authz.rs
@@ -8,12 +8,12 @@
88pub async fn require_collection_manage(
99 service: &AgentsService,
1010 space: &spaces::Model,
11 caller: Uuid,
11 caller: i64,
1212) -> Result<(), AgentError> {
1313 if space.owner_id == caller {
1414 return Ok(());
1515 }
16 let perms = effective_permissions(service.db().read(), space, caller)
16 let perms = effective_permissions(&service.state, space, caller)
1717 .await
1818 .map_err(AgentError::from)?;
1919 if perms.has(MANAGE_CHANNELS) {
@@ -26,16 +26,16 @@
2626/// Whether the caller may attach the platform MCP to an agent run: only space
2727/// owners or members with MANAGE_CHANNELS, and only while an MCP endpoint the
2828/// sandbox runtime can reach is configured.
29pub async fn can_enable_mcp(service: &AgentsService, space: &spaces::Model, caller: Uuid) -> bool {
29pub async fn can_enable_mcp(service: &AgentsService, space: &spaces::Model, caller: i64) -> bool {
3030 mcp_configured(service) && can_manage_mcp(service, space, caller).await
3131}
3232
3333/// Permission half of [`can_enable_mcp`], without the configuration check.
34pub async fn can_manage_mcp(service: &AgentsService, space: &spaces::Model, caller: Uuid) -> bool {
34pub async fn can_manage_mcp(service: &AgentsService, space: &spaces::Model, caller: i64) -> bool {
3535 if space.owner_id == caller {
3636 return true;
3737 }
38 match effective_permissions(service.db().read(), space, caller).await {
38 match effective_permissions(&service.state, space, caller).await {
3939 Ok(perms) => perms.has(MANAGE_CHANNELS),
4040 Err(_) => false,
4141 }
Modified lib/agents/src/share/guard.rs lib/agents/src/share/guard.rs +2 −2 View lib/agents/src/share/guard.rs
@@ -1,8 +1,8 @@
11use axum::extract::{Request, State};
22use axum::middleware::Next;
33use axum::response::Response;
4use session_layer::Session;
5use session_layer::share::guard::session_guard;
4use jwt::Session;
5use jwt::share::guard::session_guard;
66
77use crate::services::service::AgentsService;
88
Modified lib/agents/src/share/lookup.rs lib/agents/src/share/lookup.rs +6 −6 View lib/agents/src/share/lookup.rs
@@ -28,9 +28,9 @@
2828pub async fn require_member(
2929 service: &AgentsService,
3030 space: &spaces::Model,
31 caller: Uuid,
31 caller: i64,
3232) -> Result<(), AgentError> {
33 let perms = effective_permissions(service.db().read(), space, caller)
33 let perms = effective_permissions(&service.state, space, caller)
3434 .await
3535 .map_err(AgentError::from)?;
3636 if perms.has(VIEW_SPACE) || perms.has(READ) {
@@ -42,7 +42,7 @@
4242
4343pub async fn space_context(
4444 service: &AgentsService,
45 caller: Uuid,
45 caller: i64,
4646 space_name: &str,
4747) -> Result<spaces::Model, AgentError> {
4848 let space = lookup_space_by_name(service, space_name).await?;
@@ -54,7 +54,7 @@
5454 service: &AgentsService,
5555 space: &spaces::Model,
5656 collection_id: Uuid,
57 caller: Uuid,
57 caller: i64,
5858) -> Result<agents::Model, AgentError> {
5959 let collection = agents::Entity::find_by_id(collection_id)
6060 .one(service.db().read())
@@ -107,7 +107,7 @@
107107
108108pub async fn collection_context(
109109 service: &AgentsService,
110 caller: Uuid,
110 caller: i64,
111111 space_name: &str,
112112 collection_id: Uuid,
113113) -> Result<(spaces::Model, agents::Model), AgentError> {
@@ -118,7 +118,7 @@
118118
119119pub async fn agent_context(
120120 service: &AgentsService,
121 caller: Uuid,
121 caller: i64,
122122 space_name: &str,
123123 collection_id: Uuid,
124124 agent_id: Uuid,
Modified lib/agents/Cargo.toml lib/agents/Cargo.toml +1 −1 View lib/agents/Cargo.toml
@@ -16,7 +16,7 @@
1616sea-orm = { workspace = true }
1717serde = { workspace = true, features = ["derive"] }
1818serde_json = { workspace = true }
19session-layer = { workspace = true }
19jwt = { workspace = true }
2020space = { workspace = true }
2121time = { workspace = true }
2222tracing = { workspace = true }
Modified lib/auth/src/handlers/accept_agreement.rs lib/auth/src/handlers/accept_agreement.rs +2 −2 View lib/auth/src/handlers/accept_agreement.rs
@@ -3,13 +3,13 @@
33
44use export::rest::{AppResponseData, NoData};
55use serde::{Deserialize, Serialize};
6use session_layer::Session;
6use jwt::Session;
77use utoipa::ToSchema;
88
99use crate::errors::ApiError;
1010use crate::services::AuthService;
1111use crate::services::agreements::AcceptAgreementResp;
12use session_layer::share::session::require_uid;
12use jwt::share::session::require_uid;
1313
1414#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1515pub struct AcceptAgreementCmd {
Modified lib/auth/src/handlers/create_report.rs lib/auth/src/handlers/create_report.rs +2 −2 View lib/auth/src/handlers/create_report.rs
@@ -2,12 +2,12 @@
22use axum::extract::State;
33
44use export::rest::{AppResponseData, NoData};
5use session_layer::Session;
5use jwt::Session;
66
77use crate::errors::ApiError;
88use crate::services::AuthService;
99use crate::services::reports::{CreateReportCmd, ReportDto};
10use session_layer::share::session::require_uid;
10use jwt::share::session::require_uid;
1111
1212#[utoipa::path(
1313 post,
Modified lib/auth/src/handlers/delete_notification.rs lib/auth/src/handlers/delete_notification.rs +2 −2 View lib/auth/src/handlers/delete_notification.rs
@@ -1,11 +1,11 @@
11use axum::extract::{Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
3use jwt::Session;
44use uuid::Uuid;
55
66use crate::errors::ApiError;
77use crate::services::AuthService;
8use session_layer::share::session::require_uid;
8use jwt::share::session::require_uid;
99
1010#[utoipa::path(
1111 delete,
Modified lib/auth/src/handlers/get_preferences.rs lib/auth/src/handlers/get_preferences.rs +2 −2 View lib/auth/src/handlers/get_preferences.rs
@@ -1,11 +1,11 @@
11use axum::extract::State;
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::ApiError;
66use crate::services::AuthService;
77use crate::services::preferences::UserPreferencesDto;
8use session_layer::share::session::require_uid;
8use jwt::share::session::require_uid;
99
1010#[utoipa::path(
1111 get,
Modified lib/auth/src/handlers/get_user.rs lib/auth/src/handlers/get_user.rs +4 −4 View lib/auth/src/handlers/get_user.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
@@ -14,7 +14,7 @@
1414 tag = "users",
1515 description = "按用户 ID 返回公开身份资料,并按账号状态与可见性过滤。",
1616 params(
17 ("user_id" = Uuid, Path, description = "user id")
17 ("user_id" = i64, Path, description = "user id")
1818 ),
1919 responses(
2020 (status = 200, description = "public user identity", body = AppResponseData<UserPublic>),
@@ -24,7 +24,7 @@
2424pub async fn get_user(
2525 State(service): State<AuthService>,
2626 session: Session,
27 Path(user_id): Path<Uuid>,
27 Path(user_id): Path<i64>,
2828) -> Result<AppResponseData<UserPublic>, ApiError> {
2929 let caller = require_uid(&session)?;
3030 let user = service
Modified lib/auth/src/handlers/jwt_request.rs lib/auth/src/handlers/jwt_request.rs +42 −22 View lib/auth/src/handlers/jwt_request.rs
@@ -1,32 +1,52 @@
11use axum::extract::{Query, State};
2use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
2use axum::response::Json;
3use serde::{Deserialize, Serialize};
4use utoipa::ToSchema;
55
66use crate::errors::ApiError;
77use crate::services::AuthService;
8use crate::services::jwt::{JwtRequestQuery, JwtRequestResp};
8use jwt::Session;
9use jwt::share::session::require_uid;
10
11#[derive(Debug, Deserialize)]
12pub struct JwtRequestQuery {
13 #[serde(default)]
14 pub appid: Option<String>,
15}
16
17#[derive(Debug, Serialize, ToSchema)]
18pub struct JwtRequestResp {
19 pub jwt: String,
20 pub expires_in_secs: i64,
21}
922
10#[utoipa::path(
11 get,
12 path = "/auth/jwt-request",
13 tag = "auth",
14 description = "为指定应用申请短期 JWT 握手凭证,供独立实时网关建立长连接时验签使用。",
15 params(JwtRequestQuery),
16 responses(
17 (status = 200, description = "jwt issued", body = AppResponseData<JwtRequestResp>),
18 (status = 400, description = "request failed validation", body = AppResponseData<NoData>),
19 (status = 401, description = "authentication required", body = AppResponseData<NoData>)
20 )
21)]
2223pub async fn jwt_request(
2324 State(service): State<AuthService>,
2425 session: Session,
2526 Query(query): Query<JwtRequestQuery>,
26) -> Result<AppResponseData<JwtRequestResp>, ApiError> {
27) -> Result<Json<JwtRequestResp>, ApiError> {
2728 let uid = require_uid(&session)?;
28 let resp = service
29 .jwt_request(uid, &session, &query.appid)
30 .map_err(ApiError::from)?;
31 Ok(AppResponseData::ok(resp))
32}
29 let appid = query.appid.as_deref().unwrap_or("chat");
30 if appid != "chat" {
31 return Err(ApiError::bad_request("unsupported appid"));
32 }
33 let client = service
34 .state
35 .kenauth
36 .as_ref()
37 .ok_or_else(|| ApiError::internal("kenoauth client is not configured"))?;
38 let issued = client
39 .issue_service_token(
40 uid,
41 2,
42 vec![8, 9],
43 900,
44 &uuid::Uuid::now_v7().to_string(),
45 )
46 .await
47 .map_err(|error| ApiError::internal(error.to_string()))?;
48 Ok(Json(JwtRequestResp {
49 jwt: issued.service_token,
50 expires_in_secs: 900,
51 }))
52}
No newline at end of file
Modified lib/auth/src/handlers/list_notifications.rs lib/auth/src/handlers/list_notifications.rs +2 −2 View lib/auth/src/handlers/list_notifications.rs
@@ -1,13 +1,13 @@
11use axum::extract::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use serde::Deserialize;
4use session_layer::Session;
4use jwt::Session;
55use utoipa::IntoParams;
66
77use crate::errors::ApiError;
88use crate::services::AuthService;
99use crate::services::notifications::NotificationDto;
10use session_layer::share::session::require_uid;
10use jwt::share::session::require_uid;
1111
1212#[derive(Debug, Clone, Copy, Default, Deserialize, IntoParams)]
1313pub struct ListNotificationsQuery {
Modified lib/auth/src/handlers/mark_notifications_read.rs lib/auth/src/handlers/mark_notifications_read.rs +2 −2 View lib/auth/src/handlers/mark_notifications_read.rs
@@ -2,12 +2,12 @@
22use axum::extract::State;
33use export::rest::{AppResponseData, NoData};
44use serde::Deserialize;
5use session_layer::Session;
5use jwt::Session;
66use uuid::Uuid;
77
88use crate::errors::ApiError;
99use crate::services::AuthService;
10use session_layer::share::session::require_uid;
10use jwt::share::session::require_uid;
1111
1212#[derive(Debug, Deserialize, utoipa::ToSchema)]
1313pub struct MarkNotificationsReadCmd {
Modified lib/auth/src/handlers/me.rs lib/auth/src/handlers/me.rs +4 −7 View lib/auth/src/handlers/me.rs
@@ -1,13 +1,13 @@
11use axum::extract::State;
22use export::rest::{AppResponseData, NoData};
33use serde::Serialize;
4use session_layer::Session;
54use utoipa::ToSchema;
65
76use crate::errors::ApiError;
87use crate::services::AuthService;
98use crate::services::profile::{ProfileDto, UserMeDto};
10use session_layer::share::session::{require_uid, session_token_key};
9use jwt::Session;
10use jwt::share::session::require_uid;
1111
1212#[derive(Debug, Clone, Serialize, ToSchema)]
1313pub struct MeResp {
@@ -19,7 +19,7 @@
1919 get,
2020 path = "/me",
2121 tag = "account",
22 description = "返回当前登录用户的身份、账号状态与资料,并刷新会话活跃时间。",
22 description = "返回当前登录用户的身份、账号状态与资料。",
2323 responses(
2424 (status = 200, description = "identity and profile of the active user", body = AppResponseData<MeResp>),
2525 (status = 404, description = "resource not found", body = AppResponseData<NoData>),
@@ -31,9 +31,6 @@
3131 session: Session,
3232) -> Result<AppResponseData<MeResp>, ApiError> {
3333 let uid = require_uid(&session)?;
34 if let Some(token_hash) = session.get::<String>(&session_token_key(uid)) {
35 let _ = service.touch_session(&token_hash).await;
36 }
3734 let (user, profile) = service.me(uid).await.map_err(ApiError::from)?;
3835 Ok(AppResponseData::ok(MeResp { user, profile }))
39}
36}
No newline at end of file
Modified lib/auth/src/handlers/mod.rs lib/auth/src/handlers/mod.rs +1 −35 View lib/auth/src/handlers/mod.rs
@@ -1,49 +1,15 @@
11pub mod accept_agreement;
2pub mod add_ssh_key;
32pub mod agreement_page;
4pub mod cancel_deletion;
5pub mod cancel_email_change;
6pub mod change_password;
7pub mod confirm_email_change;
8pub mod create_access_key;
93pub mod create_report;
10pub mod delete_access_key;
114pub mod delete_notification;
12pub mod delete_ssh_key;
13pub mod deletion_status;
14pub mod devices;
15pub mod forgot_password;
165pub mod get_preferences;
176pub mod get_user;
187pub mod jwt_request;
19pub mod list_access_keys;
208pub mod list_agreements;
219pub mod list_notifications;
22pub mod list_ssh_keys;
23pub mod login;
24pub mod login_2fa;
25pub mod logout;
2610pub mod mark_notifications_read;
2711pub mod me;
28pub mod pow_challenge;
29pub mod reclaim;
30pub mod recovery_codes;
31pub mod regenerate_recovery_codes;
32pub mod register;
33pub mod request_deletion;
34pub mod request_email_change;
35pub mod resend_code;
36pub mod reset_password;
37pub mod reset_password_page;
38pub mod rotate_access_key;
39pub mod totp_disable;
40pub mod totp_enable;
41pub mod totp_setup;
42pub mod totp_status;
4312pub mod unread_notifications_count;
44pub mod update_access_key;
4513pub mod update_preferences;
4614pub mod update_profile;
47pub mod users;
48pub mod verify_email;
49pub mod verify_email_page;
15pub mod users;
No newline at end of file
Modified lib/auth/src/handlers/unread_notifications_count.rs lib/auth/src/handlers/unread_notifications_count.rs +2 −2 View lib/auth/src/handlers/unread_notifications_count.rs
@@ -1,12 +1,12 @@
11use axum::extract::State;
22use export::rest::{AppResponseData, NoData};
33use serde::Serialize;
4use session_layer::Session;
4use jwt::Session;
55use utoipa::ToSchema;
66
77use crate::errors::ApiError;
88use crate::services::AuthService;
9use session_layer::share::session::require_uid;
9use jwt::share::session::require_uid;
1010
1111#[derive(Debug, Clone, Serialize, ToSchema)]
1212pub struct UnreadCountResp {
Modified lib/auth/src/handlers/update_preferences.rs lib/auth/src/handlers/update_preferences.rs +2 −2 View lib/auth/src/handlers/update_preferences.rs
@@ -2,12 +2,12 @@
22use axum::extract::State;
33
44use export::rest::{AppResponseData, NoData};
5use session_layer::Session;
5use jwt::Session;
66
77use crate::errors::ApiError;
88use crate::services::AuthService;
99use crate::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
10use session_layer::share::session::require_uid;
10use jwt::share::session::require_uid;
1111
1212#[utoipa::path(
1313 patch,
Modified lib/auth/src/handlers/update_profile.rs lib/auth/src/handlers/update_profile.rs +2 −2 View lib/auth/src/handlers/update_profile.rs
@@ -2,12 +2,12 @@
22use axum::extract::State;
33
44use export::rest::{AppResponseData, NoData};
5use session_layer::Session;
5use jwt::Session;
66
77use crate::errors::ApiError;
88use crate::services::AuthService;
99use crate::services::profile::UpdateProfileCmd;
10use session_layer::share::session::require_uid;
10use jwt::share::session::require_uid;
1111
1212#[utoipa::path(
1313 patch,
Modified lib/auth/src/handlers/users.rs lib/auth/src/handlers/users.rs +2 −2 View lib/auth/src/handlers/users.rs
@@ -2,8 +2,8 @@
22use axum::extract::{Path, Query, State};
33use export::rest::{AppResponseData, NoData};
44use serde::Deserialize;
5use session_layer::Session;
6use session_layer::share::session::require_uid;
5use jwt::Session;
6use jwt::share::session::require_uid;
77use utoipa::IntoParams;
88use uuid::Uuid;
99
Modified lib/auth/src/services/agreements.rs lib/auth/src/services/agreements.rs +1 −1 View lib/auth/src/services/agreements.rs
@@ -30,7 +30,7 @@
3030
3131 pub async fn accept_agreement(
3232 &self,
33 uid: Uuid,
33 uid: i64,
3434 key: &str,
3535 version: &str,
3636 ) -> Result<AcceptAgreementResp, AuthError> {
Modified lib/auth/src/services/mod.rs lib/auth/src/services/mod.rs +28 −29 View lib/auth/src/services/mod.rs
@@ -1,34 +1,24 @@
11use std::sync::Arc;
22
3use async_trait::async_trait;
43use config::AppConfig;
54use export::{AppDatabase, AppRedis, AppState};
6use session_layer::SessionError;
7use session_layer::share::guard::{SessionGuard, SessionWithUser};
5use models::users::access_scope::AccessKeyScope;
86
97use crate::errors::AuthError;
108
11pub mod access_keys;
129pub mod agreements;
13pub mod deletion;
14pub mod devices;
15pub mod email;
16pub mod jwt;
17pub mod login;
18pub mod logout;
1910pub mod notifications;
20pub mod password;
21pub mod pow;
2211pub mod preferences;
2312pub mod profile;
24pub mod register;
2513pub mod reports;
26pub mod sessions;
27pub mod ssh_keys;
2814pub mod suspensions;
29pub mod two_factor;
3015pub mod users;
3116
17pub struct VerifiedAccessKey {
18 pub user_id: i64,
19 pub scope: AccessKeyScope,
20}
21
3222#[derive(Clone)]
3323pub struct AuthService {
3424 pub state: AppState,
@@ -52,13 +42,9 @@
5242 }
5343
5444 /// Pick the mail language for one recipient.
55 ///
56 /// Order: the recipient's own `users.language`/`users.locale`, then the
57 /// request's `Accept-Language`, then `[email].default_locale`. Account
58 /// preference wins because a single request can notify several recipients.
5945 pub async fn mail_locale(
6046 &self,
61 uid: Option<uuid::Uuid>,
47 uid: Option<i64>,
6248 accept_language: Option<&str>,
6349 ) -> i18n::UiLocale {
6450 let fallback = i18n::UiLocale::parse(self.config().email.default_locale())
@@ -82,14 +68,27 @@
8268 .await
8369 .map_err(|error| AuthError::Internal(format!("email send failed: {error}")))
8470 }
85}
8671
87#[async_trait]
88impl SessionGuard for AuthService {
89 async fn lookup_session_with_user(
72 pub async fn verify_access_token(
9073 &self,
91 token_hash: &str,
92 ) -> Result<Option<SessionWithUser>, SessionError> {
93 session_layer::lookup_session_with_user(self.db().read(), token_hash).await
74 token: &str,
75 ) -> Result<Option<VerifiedAccessKey>, AuthError> {
76 let Some(client) = self.state.kenauth.as_ref() else {
77 return Ok(None);
78 };
79 let principal = match client.verify_access_key(token, Vec::new()).await {
80 Ok(Some(principal)) => principal,
81 Ok(None) => return Ok(None),
82 Err(error) => return Err(AuthError::Internal(error.to_string())),
83 };
84 let write = principal.scopes.iter().any(|scope| *scope == 12);
85 Ok(Some(VerifiedAccessKey {
86 user_id: principal.user_id,
87 scope: if write {
88 AccessKeyScope::Write
89 } else {
90 AccessKeyScope::Read
91 },
92 }))
9493 }
95}
94}
No newline at end of file
Modified lib/auth/src/services/notifications.rs lib/auth/src/services/notifications.rs +7 −7 View lib/auth/src/services/notifications.rs
@@ -19,7 +19,7 @@
1919 pub title: String,
2020 pub body: Option<String>,
2121 pub data: Option<JsonValue>,
22 pub actor_id: Option<Uuid>,
22 pub actor_id: Option<i64>,
2323 pub read_at: Option<OffsetDateTime>,
2424 pub created_at: OffsetDateTime,
2525}
@@ -27,7 +27,7 @@
2727impl AuthService {
2828 pub async fn list_notifications(
2929 &self,
30 uid: Uuid,
30 uid: i64,
3131 limit: Option<u64>,
3232 offset: Option<u64>,
3333 notification_type: Option<NotificationType>,
@@ -64,7 +64,7 @@
6464 .collect())
6565 }
6666
67 pub async fn unread_notifications_count(&self, uid: Uuid) -> Result<u64, AuthError> {
67 pub async fn unread_notifications_count(&self, uid: i64) -> Result<u64, AuthError> {
6868 let count = user_notifications::Entity::find()
6969 .filter(user_notifications::Column::UserId.eq(uid))
7070 .filter(user_notifications::Column::ReadAt.is_null())
@@ -73,7 +73,7 @@
7373 Ok(count)
7474 }
7575
76 pub async fn mark_notification_read(&self, uid: Uuid, id: Uuid) -> Result<(), AuthError> {
76 pub async fn mark_notification_read(&self, uid: i64, id: Uuid) -> Result<(), AuthError> {
7777 let db = self.db();
7878 let Some(row) = user_notifications::Entity::find()
7979 .filter(user_notifications::Column::Id.eq(id))
@@ -91,7 +91,7 @@
9191
9292 pub async fn mark_notifications_read(
9393 &self,
94 uid: Uuid,
94 uid: i64,
9595 ids: Vec<Uuid>,
9696 ) -> Result<u64, AuthError> {
9797 let db = self.db();
@@ -108,7 +108,7 @@
108108 Ok(result.rows_affected)
109109 }
110110
111 pub async fn mark_all_notifications_read(&self, uid: Uuid) -> Result<(), AuthError> {
111 pub async fn mark_all_notifications_read(&self, uid: i64) -> Result<(), AuthError> {
112112 let db = self.db();
113113 user_notifications::Entity::update_many()
114114 .col_expr(
@@ -122,7 +122,7 @@
122122 Ok(())
123123 }
124124
125 pub async fn delete_notification(&self, uid: Uuid, id: Uuid) -> Result<(), AuthError> {
125 pub async fn delete_notification(&self, uid: i64, id: Uuid) -> Result<(), AuthError> {
126126 let db = self.db();
127127 let result = user_notifications::Entity::delete_many()
128128 .filter(user_notifications::Column::Id.eq(id))
Modified lib/auth/src/services/preferences.rs lib/auth/src/services/preferences.rs +2 −2 View lib/auth/src/services/preferences.rs
@@ -66,7 +66,7 @@
6666}
6767
6868impl AuthService {
69 pub async fn preferences(&self, uid: Uuid) -> Result<UserPreferencesDto, AuthError> {
69 pub async fn preferences(&self, uid: i64) -> Result<UserPreferencesDto, AuthError> {
7070 let Some(row) = user_preferences::Entity::find_by_id(uid)
7171 .one(self.db().read())
7272 .await?
@@ -90,7 +90,7 @@
9090
9191 pub async fn update_preferences(
9292 &self,
93 uid: Uuid,
93 uid: i64,
9494 cmd: UpdatePreferencesCmd,
9595 ) -> Result<UserPreferencesDto, AuthError> {
9696 let db = self.db();
Modified lib/auth/src/services/profile.rs lib/auth/src/services/profile.rs +4 −4 View lib/auth/src/services/profile.rs
@@ -14,7 +14,7 @@
1414
1515#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1616pub struct UserMeDto {
17 pub user_id: Uuid,
17 pub user_id: i64,
1818 pub username: String,
1919 pub display_name: String,
2020 pub avatar_url: String,
@@ -68,7 +68,7 @@
6868}
6969
7070impl AuthService {
71 pub async fn me(&self, uid: Uuid) -> Result<(UserMeDto, Option<ProfileDto>), AuthError> {
71 pub async fn me(&self, uid: i64) -> Result<(UserMeDto, Option<ProfileDto>), AuthError> {
7272 let db = self.db();
7373 let Some(user) = user::Entity::find_by_id(uid).one(db.read()).await? else {
7474 return Err(AuthError::NotFound);
@@ -112,7 +112,7 @@
112112 Ok((me, profile))
113113 }
114114
115 pub async fn update_profile(&self, uid: Uuid, cmd: UpdateProfileCmd) -> Result<(), AuthError> {
115 pub async fn update_profile(&self, uid: i64, cmd: UpdateProfileCmd) -> Result<(), AuthError> {
116116 let db = self.db();
117117 let Some(user) = user::Entity::find_by_id(uid).one(db.write()).await? else {
118118 return Err(AuthError::NotFound);
@@ -225,7 +225,7 @@
225225
226226 pub async fn update_username(
227227 &self,
228 uid: Uuid,
228 uid: i64,
229229 new_username: &str,
230230 ) -> Result<UserPublic, AuthError> {
231231 let username = crate::share::validate::validate_username(new_username.trim())?;
Modified lib/auth/src/services/reports.rs lib/auth/src/services/reports.rs +3 −3 View lib/auth/src/services/reports.rs
@@ -10,7 +10,7 @@
1010
1111#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1212pub struct CreateReportCmd {
13 pub reported_id: Uuid,
13 pub reported_id: i64,
1414 pub reason: String,
1515 pub evidence: Option<String>,
1616}
@@ -18,7 +18,7 @@
1818#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1919pub struct ReportDto {
2020 pub id: Uuid,
21 pub reported_id: Uuid,
21 pub reported_id: i64,
2222 pub reason: String,
2323 pub evidence: Option<String>,
2424 pub status: ReportStatus,
@@ -28,7 +28,7 @@
2828impl AuthService {
2929 pub async fn create_report(
3030 &self,
31 reporter_uid: Uuid,
31 reporter_uid: i64,
3232 cmd: CreateReportCmd,
3333 ) -> Result<ReportDto, AuthError> {
3434 if reporter_uid == cmd.reported_id {
Modified lib/auth/src/services/suspensions.rs lib/auth/src/services/suspensions.rs +2 −2 View lib/auth/src/services/suspensions.rs
@@ -17,7 +17,7 @@
1717}
1818
1919impl AuthService {
20 pub async fn active_suspension(&self, uid: Uuid) -> Result<Option<SuspensionDto>, AuthError> {
20 pub async fn active_suspension(&self, uid: i64) -> Result<Option<SuspensionDto>, AuthError> {
2121 let now = OffsetDateTime::now_utc();
2222 let Some(row) = user_suspensions::Entity::find()
2323 .filter(user_suspensions::Column::UserId.eq(uid))
@@ -47,7 +47,7 @@
4747 }))
4848 }
4949
50 pub async fn assert_not_suspended(&self, uid: Uuid) -> Result<(), AuthError> {
50 pub async fn assert_not_suspended(&self, uid: i64) -> Result<(), AuthError> {
5151 if self.active_suspension(uid).await?.is_some() {
5252 return Err(AuthError::Forbidden);
5353 }
Modified lib/auth/src/services/users.rs lib/auth/src/services/users.rs +4 −4 View lib/auth/src/services/users.rs
@@ -10,7 +10,7 @@
1010const MAX_BATCH_USERS: usize = 100;
1111
1212impl AuthService {
13 fn filter_public_user(&self, caller: Uuid, user: user::Model) -> Result<UserPublic, AuthError> {
13 fn filter_public_user(&self, caller: i64, user: user::Model) -> Result<UserPublic, AuthError> {
1414 if !user.is_active || user.system {
1515 return Err(AuthError::NotFound);
1616 }
@@ -28,7 +28,7 @@
2828 })
2929 }
3030
31 pub async fn public_user(&self, caller: Uuid, uid: Uuid) -> Result<UserPublic, AuthError> {
31 pub async fn public_user(&self, caller: i64, uid: i64) -> Result<UserPublic, AuthError> {
3232 let Some(user) = user::Entity::find_by_id(uid).one(self.db().read()).await? else {
3333 return Err(AuthError::NotFound);
3434 };
@@ -37,7 +37,7 @@
3737
3838 pub async fn public_users(
3939 &self,
40 caller: Uuid,
40 caller: i64,
4141 ids: Vec<Uuid>,
4242 ) -> Result<Vec<UserPublic>, AuthError> {
4343 if ids.len() > MAX_BATCH_USERS {
@@ -60,7 +60,7 @@
6060
6161 pub async fn public_user_by_name(
6262 &self,
63 caller: Uuid,
63 caller: i64,
6464 username: &str,
6565 ) -> Result<UserPublic, AuthError> {
6666 let lower = username.trim().to_lowercase();
Modified lib/auth/src/share/guard.rs lib/auth/src/share/guard.rs +4 −22 View lib/auth/src/share/guard.rs
@@ -1,10 +1,8 @@
11use axum::extract::{Request, State};
2use axum::http::StatusCode;
32use axum::middleware::Next;
4use axum::response::{IntoResponse, Response};
5use session_layer::Session;
6use session_layer::SessionUser;
7use session_layer::share::guard::session_guard;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
86
97use crate::services::AuthService;
108
@@ -15,20 +13,4 @@
1513 next: Next,
1614) -> Response {
1715 session_guard::<AuthService>(State(service), session, request, next).await
18}
19
20pub async fn admin_guard(
21 State(service): State<AuthService>,
22 session: Session,
23 request: Request,
24 next: Next,
25) -> Response {
26 match session.uid() {
27 Some(uid) if service.config().apihost.is_platform_admin(&uid.to_string()) => {}
28 Some(_) => {
29 return (StatusCode::FORBIDDEN, "platform admin required").into_response();
30 }
31 None => {}
32 }
33 session_guard::<AuthService>(State(service), session, request, next).await
34}
16}
No newline at end of file
Modified lib/auth/src/share/mod.rs lib/auth/src/share/mod.rs +0 −2 View lib/auth/src/share/mod.rs
@@ -1,7 +1,5 @@
11pub mod crypto;
22pub mod guard;
3pub mod rate;
4pub mod totp;
53pub mod types;
64pub mod validate;
75
Modified lib/auth/src/share/types.rs lib/auth/src/share/types.rs +1 −1 View lib/auth/src/share/types.rs
@@ -15,7 +15,7 @@
1515
1616#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, utoipa::ToSchema)]
1717pub struct UserPublic {
18 pub user_id: Uuid,
18 pub user_id: i64,
1919 pub username: String,
2020 pub display_name: String,
2121 pub avatar_url: String,
Modified lib/auth/src/errors.rs lib/auth/src/errors.rs +0 −22 View lib/auth/src/errors.rs
@@ -1,6 +1,5 @@
11use std::fmt;
22
3use crate::pow::PowError;
43
54#[derive(Debug)]
65pub enum AuthError {
@@ -11,7 +10,6 @@
1110 NotFound,
1211 Conflict(String),
1312 RateLimited,
14 PowFailed,
1513 Internal(String),
1614 TwoFactorExpired,
1715 TwoFactorCodeInvalid,
@@ -31,7 +29,6 @@
3129 Self::NotFound => write!(f, "not found"),
3230 Self::Conflict(message) => write!(f, "{message}"),
3331 Self::RateLimited => write!(f, "rate limit exceeded"),
34 Self::PowFailed => write!(f, "proof of work failed"),
3532 Self::Internal(message) => write!(f, "{message}"),
3633 Self::TwoFactorExpired => write!(f, "two-factor challenge expired"),
3734 Self::TwoFactorCodeInvalid => write!(f, "invalid two-factor code"),
@@ -50,21 +47,6 @@
5047export::export_error_conversion!(AuthError);
5148export::json_payload_conversion!(AuthError);
5249
53impl From<PowError> for AuthError {
54 fn from(error: PowError) -> Self {
55 match error {
56 PowError::NotConfigured | PowError::InvalidKey => {
57 Self::Internal("pow service is not configured".to_string())
58 }
59 PowError::Missing => Self::BadRequest("pow challenge missing".to_string()),
60 PowError::Expired => Self::BadRequest("pow challenge expired".to_string()),
61 PowError::Malformed | PowError::InvalidSolution => {
62 Self::BadRequest("invalid pow solution".to_string())
63 }
64 PowError::InvalidSignature => Self::BadRequest("invalid pow challenge".to_string()),
65 }
66 }
67}
6850
6951export::api_error! {
7052 log: "auth",
@@ -92,10 +74,6 @@
9274 AuthError::NotFound => Self::not_found("auth resource not found"),
9375 AuthError::Conflict(msg) => Self::conflict(msg),
9476 AuthError::RateLimited => Self::rate_limited_exceeded(),
95 AuthError::PowFailed => Self::build(
96 400,
97 Self::payload(400, "proof of work failed").with_error_code("pow_failed"),
98 ),
9977 AuthError::TwoFactorExpired => Self::build(
10078 400,
10179 Self::payload(400, "two-factor challenge expired")
Modified lib/auth/src/lib.rs lib/auth/src/lib.rs +1 −2 View lib/auth/src/lib.rs
@@ -1,9 +1,8 @@
11pub mod errors;
22pub mod handlers;
3pub mod oauth;
34pub mod openapi;
4pub mod pow;
55pub mod routes;
66pub mod services;
77pub mod share;
88pub mod templates;
9pub mod worker;
Modified lib/auth/src/openapi.rs lib/auth/src/openapi.rs +2 −111 View lib/auth/src/openapi.rs
@@ -1,45 +1,16 @@
11use export::rest::NoData;
2use models::users::user::{Audience, ProfileVisibility};
3use models::users::user_access_keys::AccessKeyScope;
4use models::users::user_email_verification_codes::EmailVerificationPurpose;
52use models::users::user_notifications::NotificationType;
6use models::users::user_reports::ReportStatus;
7use models::users::user_ssh_keys::SshKeyType;
83use utoipa::OpenApi;
94
105use crate::handlers;
116use crate::handlers::accept_agreement::AcceptAgreementCmd;
12use crate::handlers::confirm_email_change::ConfirmEmailChangeCmd;
137use crate::handlers::me::MeResp;
14use crate::handlers::pow_challenge::PowChallengeResp;
15use crate::handlers::request_deletion::RequestDeletionCmd;
168use crate::handlers::unread_notifications_count::UnreadCountResp;
17use crate::services::access_keys::{
18 AccessKeyDto, CreateAccessKeyCmd, CreateAccessKeyResp, UpdateAccessKeyCmd,
19};
209use crate::services::agreements::AcceptAgreementResp;
21use crate::services::deletion::DeletionDto;
22use crate::services::devices::{DeviceDto, UpdateDeviceCmd};
23use crate::services::email::{
24 EmailChangeTicketDto, ReclaimCmd, ReclaimResp, RequestEmailChangeCmd, ResendCodeCmd,
25 ResendCodeResp, VerifyEmailCmd, VerifyEmailResp,
26};
27use crate::services::jwt::{JwtRequestQuery, JwtRequestResp};
28use crate::services::login::{LoginCmd, LoginResp, TwoFactorLoginCmd};
29use crate::services::logout::LogoutResp;
3010use crate::services::notifications::NotificationDto;
31use crate::services::password::{
32 ChangePasswordCmd, ForgotPasswordCmd, ForgotPasswordResp, ResetPasswordCmd,
33};
3411use crate::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
3512use crate::services::profile::{ProfileDto, UpdateProfileCmd, UserMeDto};
36use crate::services::register::{RegisterCmd, RegisterResp};
3713use crate::services::reports::{CreateReportCmd, ReportDto};
38use crate::services::ssh_keys::{AddSshKeyCmd, SshKeyDto};
39use crate::services::two_factor::{
40 RecoveryCodesResp, RegenerateRecoveryCodesCmd, TotpCodeCmd, TotpEnableCmd, TotpSetupResp,
41 TotpStatusResp,
42};
4314use crate::share::types::{AgreementDoc, UserPublic};
4415
4516#[derive(OpenApi)]
@@ -47,100 +18,29 @@
4718 info(title = "gitdata auth api", version = "1.0"),
4819 paths(
4920 handlers::accept_agreement::accept_agreement,
50 handlers::add_ssh_key::add_ssh_key,
51 handlers::cancel_deletion::cancel_deletion,
52 handlers::cancel_email_change::cancel_email_change,
53 handlers::change_password::change_password,
54 handlers::confirm_email_change::confirm_email_change,
55 handlers::create_access_key::create_access_key,
5621 handlers::create_report::create_report,
57 handlers::delete_access_key::delete_access_key,
58 handlers::update_access_key::update_access_key,
59 handlers::rotate_access_key::rotate_access_key,
60 handlers::devices::list_devices,
61 handlers::devices::update_device,
62 handlers::devices::delete_device,
6322 handlers::delete_notification::delete_notification,
64 handlers::delete_ssh_key::delete_ssh_key,
65 handlers::deletion_status::deletion_status,
66 handlers::forgot_password::forgot_password,
6723 handlers::get_preferences::get_preferences,
6824 handlers::get_user::get_user,
6925 handlers::users::list_users_batch,
7026 handlers::users::get_user_by_username,
7127 handlers::users::update_username,
72 handlers::list_access_keys::list_access_keys,
7328 handlers::list_agreements::list_agreements,
7429 handlers::list_notifications::list_notifications,
75 handlers::list_ssh_keys::list_ssh_keys,
76 handlers::login::login,
77 handlers::login_2fa::login_2fa,
78 handlers::jwt_request::jwt_request,
79 handlers::logout::logout,
8030 handlers::mark_notifications_read::mark_notifications_read,
8131 handlers::me::me,
82 handlers::pow_challenge::pow_challenge,
83 handlers::reclaim::reclaim,
84 handlers::recovery_codes::recovery_codes,
85 handlers::regenerate_recovery_codes::regenerate_recovery_codes,
86 handlers::register::register,
87 handlers::request_deletion::request_deletion,
88 handlers::request_email_change::request_email_change,
89 handlers::resend_code::resend_code,
90 handlers::reset_password::reset_password,
91 handlers::totp_disable::totp_disable,
92 handlers::totp_enable::totp_enable,
93 handlers::totp_setup::totp_setup,
94 handlers::totp_status::totp_status,
9532 handlers::unread_notifications_count::unread_notifications_count,
9633 handlers::update_preferences::update_preferences,
9734 handlers::update_profile::update_profile,
98 handlers::verify_email::verify_email,
9935 ),
10036 components(schemas(
10137 export::pagination::PageQuery,
102 export::pagination::Page<crate::services::notifications::NotificationDto>,
103
38 export::pagination::Page<NotificationDto>,
10439 handlers::mark_notifications_read::MarkNotificationsReadCmd,
10540 handlers::mark_notifications_read::MarkedCountResp,
10641 NoData,
10742 UserPublic,
10843 AgreementDoc,
109 RegisterCmd,
110 RegisterResp,
111 LoginCmd,
112 LoginResp,
113 TwoFactorLoginCmd,
114 LogoutResp,
115 JwtRequestQuery,
116 JwtRequestResp,
117 VerifyEmailCmd,
118 VerifyEmailResp,
119 ResendCodeCmd,
120 ResendCodeResp,
121 ReclaimCmd,
122 ReclaimResp,
123 EmailChangeTicketDto,
124 RequestEmailChangeCmd,
125 ConfirmEmailChangeCmd,
126 ForgotPasswordCmd,
127 ForgotPasswordResp,
128 ResetPasswordCmd,
129 ChangePasswordCmd,
130 TotpStatusResp,
131 TotpSetupResp,
132 TotpCodeCmd,
133 TotpEnableCmd,
134 RegenerateRecoveryCodesCmd,
135 RecoveryCodesResp,
136 CreateAccessKeyCmd,
137 AccessKeyDto,
138 CreateAccessKeyResp,
139 UpdateAccessKeyCmd,
140 DeviceDto,
141 UpdateDeviceCmd,
142 SshKeyDto,
143 AddSshKeyCmd,
14444 UserMeDto,
14545 ProfileDto,
14646 UpdateProfileCmd,
@@ -149,20 +49,11 @@
14949 AcceptAgreementCmd,
15050 AcceptAgreementResp,
15151 MeResp,
152 PowChallengeResp,
15352 NotificationDto,
15453 CreateReportCmd,
15554 ReportDto,
15655 UnreadCountResp,
157 DeletionDto,
158 RequestDeletionCmd,
159 ProfileVisibility,
160 Audience,
161 AccessKeyScope,
162 SshKeyType,
16356 NotificationType,
164 ReportStatus,
165 EmailVerificationPurpose,
16657 ))
16758)]
168pub struct AuthApiDoc;
59pub struct AuthApiDoc;
No newline at end of file
Modified lib/auth/src/routes.rs lib/auth/src/routes.rs +7 −88 View lib/auth/src/routes.rs
@@ -3,65 +3,28 @@
33use axum::routing::{delete, get, patch, post, put};
44
55use crate::handlers::accept_agreement::accept_agreement;
6use crate::handlers::add_ssh_key::add_ssh_key;
76use crate::handlers::agreement_page::agreement_page;
8use crate::handlers::cancel_deletion::cancel_deletion;
9use crate::handlers::cancel_email_change::cancel_email_change;
10use crate::handlers::change_password::change_password;
11use crate::handlers::confirm_email_change::confirm_email_change;
12use crate::handlers::create_access_key::create_access_key;
137use crate::handlers::create_report::create_report;
14use crate::handlers::delete_access_key::delete_access_key;
158use crate::handlers::delete_notification::delete_notification;
16use crate::handlers::delete_ssh_key::delete_ssh_key;
17use crate::handlers::deletion_status::deletion_status;
18use crate::handlers::forgot_password::forgot_password;
199use crate::handlers::get_preferences::get_preferences;
2010use crate::handlers::get_user::get_user;
21use crate::handlers::jwt_request::jwt_request;
22use crate::handlers::list_access_keys::list_access_keys;
2311use crate::handlers::list_agreements::list_agreements;
2412use crate::handlers::list_notifications::list_notifications;
25use crate::handlers::list_ssh_keys::list_ssh_keys;
26use crate::handlers::login::login;
27use crate::handlers::login_2fa::login_2fa;
28use crate::handlers::logout::logout;
2913use crate::handlers::me::me;
30use crate::handlers::pow_challenge::pow_challenge;
31use crate::handlers::reclaim::reclaim;
32use crate::handlers::recovery_codes::recovery_codes;
33use crate::handlers::regenerate_recovery_codes::regenerate_recovery_codes;
34use crate::handlers::register::register;
35use crate::handlers::request_deletion::request_deletion;
36use crate::handlers::request_email_change::request_email_change;
37use crate::handlers::resend_code::resend_code;
38use crate::handlers::reset_password::reset_password;
39use crate::handlers::reset_password_page::reset_password_page;
40use crate::handlers::totp_disable::totp_disable;
41use crate::handlers::totp_enable::totp_enable;
42use crate::handlers::totp_setup::totp_setup;
43use crate::handlers::totp_status::totp_status;
4414use crate::handlers::unread_notifications_count::unread_notifications_count;
4515use crate::handlers::update_preferences::update_preferences;
4616use crate::handlers::update_profile::update_profile;
4717use crate::handlers::users;
48use crate::handlers::verify_email::verify_email;
49use crate::handlers::verify_email_page::verify_email_page;
18use crate::oauth;
5019use crate::services::AuthService;
5120use crate::share::guard::auth_guard;
5221
5322pub fn router(service: AuthService) -> Router<AuthService> {
5423 let public = Router::new()
55 .route("/auth/pow/challenge", get(pow_challenge))
56 .route("/auth/register", post(register))
57 .route("/auth/login", post(login))
58 .route("/auth/login/2fa", post(login_2fa))
59 .route("/auth/logout", post(logout))
60 .route("/auth/email/verify", post(verify_email))
61 .route("/auth/email/resend", post(resend_code))
62 .route("/auth/email/reclaim", post(reclaim))
63 .route("/auth/password/forgot", post(forgot_password))
64 .route("/auth/password/reset", post(reset_password))
24 .route("/auth/login", get(oauth::login::login))
25 .route("/auth/callback", get(oauth::callback::callback))
26 .route("/auth/refresh", post(oauth::refresh::refresh))
27 .route("/auth/logout", post(oauth::logout::logout))
6528 .route("/auth/agreements", get(list_agreements));
6629
6730 let authed = Router::new()
@@ -71,38 +34,8 @@
7134 get(users::get_user_by_username),
7235 )
7336 .route("/users/{user_id}", get(get_user))
74 .route("/auth/email/change", post(request_email_change))
75 .route("/auth/email/change/confirm", post(confirm_email_change))
76 .route(
77 "/auth/email/change/{ticket_id}",
78 delete(cancel_email_change),
79 )
80 .route("/auth/password/change", post(change_password))
81 .route("/auth/jwt-request", get(jwt_request))
82 .route("/auth/2fa/status", get(totp_status))
83 .route("/auth/2fa/setup", post(totp_setup))
84 .route("/auth/2fa/enable", post(totp_enable))
85 .route("/auth/2fa/disable", post(totp_disable))
86 .route(
87 "/auth/2fa/recovery-codes",
88 get(recovery_codes).put(regenerate_recovery_codes),
89 )
90 .route(
91 "/auth/access-keys",
92 get(list_access_keys).post(create_access_key),
93 )
94 .route("/auth/access-keys/{key_id}", delete(delete_access_key))
95 .route(
96 "/auth/access-keys/{key_id}",
97 patch(crate::handlers::update_access_key::update_access_key),
98 )
99 .route(
100 "/auth/access-keys/{key_id}/rotate",
101 post(crate::handlers::rotate_access_key::rotate_access_key),
102 )
103 .route("/auth/ssh-keys", get(list_ssh_keys).post(add_ssh_key))
104 .route("/auth/ssh-keys/{key_id}", delete(delete_ssh_key))
10537 .route("/me", get(me).patch(update_profile))
38 .route("/auth/jwt-request", get(crate::handlers::jwt_request::jwt_request))
10639 .route("/me/username", put(users::update_username))
10740 .route(
10841 "/me/preferences",
@@ -122,19 +55,7 @@
12255 "/me/notifications/read",
12356 put(crate::handlers::mark_notifications_read::mark_notifications_read),
12457 )
125 .route("/me/devices", get(crate::handlers::devices::list_devices))
126 .route(
127 "/me/devices/{device_id}",
128 patch(crate::handlers::devices::update_device)
129 .delete(crate::handlers::devices::delete_device),
130 )
13158 .route("/me/reports", post(create_report))
132 .route(
133 "/me/deletion",
134 get(deletion_status)
135 .post(request_deletion)
136 .delete(cancel_deletion),
137 )
13859 .route_layer(middleware::from_fn_with_state(service, auth_guard));
13960
14061 public.merge(authed)
@@ -142,8 +63,6 @@
14263
14364pub fn pages_router(service: AuthService) -> Router {
14465 Router::new()
145 .route("/email/verify", get(verify_email_page))
146 .route("/password/reset", get(reset_password_page))
14766 .route("/agreements/{key}", get(agreement_page))
14867 .with_state(service)
149}
68}
No newline at end of file
Modified lib/auth/Cargo.toml lib/auth/Cargo.toml +5 −5 View lib/auth/Cargo.toml
@@ -6,11 +6,10 @@
66[dependencies]
77async-trait = { workspace = true }
88config = { workspace = true }
9session-layer = { workspace = true }
9jwt = { workspace = true }
1010export = { workspace = true }
1111models = { workspace = true }
1212space = { workspace = true }
13pow-solver = { workspace = true }
1413axum = { workspace = true }
1514sea-orm = { workspace = true }
1615tokio = { workspace = true }
@@ -21,16 +20,17 @@
2120base64 = { workspace = true }
2221hmac = { workspace = true }
2322sha2 = { workspace = true }
24sha1 = { workspace = true }
2523uuid = { workspace = true }
2624hkdf = { workspace = true }
2725time = { workspace = true }
2826argon2 = { workspace = true }
2927aes-gcm = { workspace = true }
3028rand = { workspace = true }
31ssh-key = { workspace = true }
3229tracing = { workspace = true }
3330email = { workspace = true }
31kenauth = { workspace = true }
32reqwest = { workspace = true }
33url = { workspace = true }
3434i18n = { workspace = true }
3535maud = { workspace = true }
36pulldown-cmark = { workspace = true }
No newline at end of file
36pulldown-cmark = { workspace = true }
Modified lib/chat/src/handlers/attachments.rs lib/chat/src/handlers/attachments.rs +2 −2 View lib/chat/src/handlers/attachments.rs
@@ -4,8 +4,8 @@
44use export::rest::{AppResponseData, NoData};
55use imaging::layout::ImageVariant;
66use serde::Deserialize;
7use session_layer::Session;
8use session_layer::share::session::require_uid;
7use jwt::Session;
8use jwt::share::session::require_uid;
99use utoipa::{IntoParams, ToSchema};
1010use uuid::Uuid;
1111
Modified lib/chat/src/handlers/bookmarks.rs lib/chat/src/handlers/bookmarks.rs +2 −2 View lib/chat/src/handlers/bookmarks.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/dms.rs lib/chat/src/handlers/dms.rs +2 −2 View lib/chat/src/handlers/dms.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/drafts.rs lib/chat/src/handlers/drafts.rs +2 −2 View lib/chat/src/handlers/drafts.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/emojis.rs lib/chat/src/handlers/emojis.rs +2 −2 View lib/chat/src/handlers/emojis.rs
@@ -2,8 +2,8 @@
22use axum::http::StatusCode;
33use axum::response::{IntoResponse, Response};
44use export::rest::{AppResponseData, NoData};
5use session_layer::Session;
6use session_layer::share::session::require_uid;
5use jwt::Session;
6use jwt::share::session::require_uid;
77use uuid::Uuid;
88
99use crate::errors::ApiError;
Modified lib/chat/src/handlers/messages.rs lib/chat/src/handlers/messages.rs +2 −2 View lib/chat/src/handlers/messages.rs
@@ -1,8 +1,8 @@
11use axum::extract::{Json, Path, Query, State};
22use axum::response::IntoResponse;
33use export::rest::{AppResponseData, NoData};
4use session_layer::Session;
5use session_layer::share::session::require_uid;
4use jwt::Session;
5use jwt::share::session::require_uid;
66use uuid::Uuid;
77
88use crate::errors::ApiError;
Modified lib/chat/src/handlers/presence.rs lib/chat/src/handlers/presence.rs +4 −4 View lib/chat/src/handlers/presence.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
@@ -35,7 +35,7 @@
3535 path = "/chat/users/{user_id}/presence",
3636 tag = "chat",
3737 description = "读取指定用户的在线状态;仅在可见关系成立时返回。",
38 params(("user_id" = Uuid, Path, description = "user id")),
38 params(("user_id" = i64, Path, description = "user id")),
3939 responses(
4040 (status = 200, description = "presence", body = AppResponseData<PresenceDto>),
4141 (status = 401, description = "unauthorized", body = AppResponseData<NoData>)
@@ -44,7 +44,7 @@
4444pub async fn get_presence(
4545 State(service): State<ChatService>,
4646 session: Session,
47 Path(user_id): Path<Uuid>,
47 Path(user_id): Path<i64>,
4848) -> Result<AppResponseData<PresenceDto>, ApiError> {
4949 let uid = require_uid(&session)?;
5050 let dto = service.get_presence_for(uid, user_id).await?;
Modified lib/chat/src/handlers/reactions.rs lib/chat/src/handlers/reactions.rs +2 −2 View lib/chat/src/handlers/reactions.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/read_state.rs lib/chat/src/handlers/read_state.rs +2 −2 View lib/chat/src/handlers/read_state.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/reminders.rs lib/chat/src/handlers/reminders.rs +2 −2 View lib/chat/src/handlers/reminders.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/room_admin.rs lib/chat/src/handlers/room_admin.rs +11 −11 View lib/chat/src/handlers/room_admin.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
@@ -14,7 +14,7 @@
1414 path = "/chat/rooms/{room_id}/members/{user_id}",
1515 tag = "chat",
1616 description = "读取指定房间成员的昵称、角色与加入信息。",
17 params(("room_id" = Uuid, Path), ("user_id" = Uuid, Path)),
17 params(("room_id" = Uuid, Path), ("user_id" = i64, Path)),
1818 responses(
1919 (status = 200, description = "room member", body = AppResponseData<RoomMemberDto>),
2020 (status = 404, description = "not found", body = AppResponseData<NoData>),
@@ -24,7 +24,7 @@
2424pub async fn get_room_member(
2525 State(service): State<ChatService>,
2626 session: Session,
27 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
27 Path((room_id, user_id)): Path<(Uuid, i64)>,
2828) -> Result<AppResponseData<RoomMemberDto>, ApiError> {
2929 let uid = require_uid(&session)?;
3030 let member = service.get_room_member(uid, room_id, user_id).await?;
@@ -36,7 +36,7 @@
3636 path = "/chat/rooms/{room_id}/members/{user_id}",
3737 tag = "chat",
3838 description = "更新房间成员昵称或角色;管理员修改角色受层级限制。",
39 params(("room_id" = Uuid, Path), ("user_id" = Uuid, Path)),
39 params(("room_id" = Uuid, Path), ("user_id" = i64, Path)),
4040 request_body = UpdateRoomMemberCmd,
4141 responses(
4242 (status = 200, description = "room member updated", body = AppResponseData<RoomMemberDto>),
@@ -48,7 +48,7 @@
4848pub async fn update_room_member(
4949 State(service): State<ChatService>,
5050 session: Session,
51 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
51 Path((room_id, user_id)): Path<(Uuid, i64)>,
5252 Json(cmd): Json<UpdateRoomMemberCmd>,
5353) -> Result<AppResponseData<RoomMemberDto>, ApiError> {
5454 let uid = require_uid(&session)?;
@@ -63,7 +63,7 @@
6363 path = "/chat/rooms/{room_id}/members/{user_id}",
6464 tag = "chat",
6565 description = "将成员移出房间;调用者权限须高于目标且不能移除所有者。",
66 params(("room_id" = Uuid, Path), ("user_id" = Uuid, Path)),
66 params(("room_id" = Uuid, Path), ("user_id" = i64, Path)),
6767 responses(
6868 (status = 200, description = "member removed", body = AppResponseData<NoData>),
6969 (status = 403, description = "forbidden", body = AppResponseData<NoData>),
@@ -73,7 +73,7 @@
7373pub async fn kick_room_member(
7474 State(service): State<ChatService>,
7575 session: Session,
76 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
76 Path((room_id, user_id)): Path<(Uuid, i64)>,
7777) -> Result<AppResponseData<NoData>, ApiError> {
7878 let uid = require_uid(&session)?;
7979 service.kick_room_member(uid, room_id, user_id).await?;
@@ -134,7 +134,7 @@
134134 path = "/chat/rooms/{room_id}/bans/{user_id}",
135135 tag = "chat",
136136 description = "删除指定用户的房间封禁记录。",
137 params(("room_id" = Uuid, Path), ("user_id" = Uuid, Path)),
137 params(("room_id" = Uuid, Path), ("user_id" = i64, Path)),
138138 responses(
139139 (status = 200, description = "member unbanned", body = AppResponseData<NoData>),
140140 (status = 404, description = "not banned", body = AppResponseData<NoData>),
@@ -144,7 +144,7 @@
144144pub async fn unban_room_member(
145145 State(service): State<ChatService>,
146146 session: Session,
147 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
147 Path((room_id, user_id)): Path<(Uuid, i64)>,
148148) -> Result<AppResponseData<NoData>, ApiError> {
149149 let uid = require_uid(&session)?;
150150 service.unban_room_member(uid, room_id, user_id).await?;
@@ -153,7 +153,7 @@
153153
154154#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
155155pub struct TransferOwnerCmd {
156 pub user_id: Uuid,
156 pub user_id: i64,
157157}
158158
159159#[utoipa::path(
Modified lib/chat/src/handlers/rooms.rs lib/chat/src/handlers/rooms.rs +2 −2 View lib/chat/src/handlers/rooms.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/search.rs lib/chat/src/handlers/search.rs +2 −2 View lib/chat/src/handlers/search.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55
66use crate::errors::ApiError;
77use crate::services::search::{SearchPage, SearchQuery};
Modified lib/chat/src/handlers/settings.rs lib/chat/src/handlers/settings.rs +2 −2 View lib/chat/src/handlers/settings.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/threads.rs lib/chat/src/handlers/threads.rs +2 −2 View lib/chat/src/handlers/threads.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/handlers/voice.rs lib/chat/src/handlers/voice.rs +12 −12 View lib/chat/src/handlers/voice.rs
@@ -5,8 +5,8 @@
55use livekit_api::access_token::TokenVerifier;
66use livekit_api::webhooks::WebhookReceiver;
77use serde::Deserialize;
8use session_layer::Session;
9use session_layer::share::session::require_uid;
8use jwt::Session;
9use jwt::share::session::require_uid;
1010use uuid::Uuid;
1111
1212use crate::errors::ApiError;
@@ -224,7 +224,7 @@
224224 description = "主持人批准发言:翻转该成员 can_publish,无需重连。",
225225 params(
226226 ("room_id" = Uuid, Path, description = "room id"),
227 ("user_id" = Uuid, Path, description = "target user id")
227 ("user_id" = i64, Path, description = "target user id")
228228 ),
229229 responses(
230230 (status = 200, description = "approved", body = AppResponseData<VoiceModerationDto>),
@@ -235,7 +235,7 @@
235235pub async fn approve_hand(
236236 State(service): State<ChatService>,
237237 session: Session,
238 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
238 Path((room_id, user_id)): Path<(Uuid, i64)>,
239239) -> Result<AppResponseData<VoiceModerationDto>, ApiError> {
240240 let uid = require_uid(&session)?;
241241 Ok(AppResponseData::ok(
@@ -250,7 +250,7 @@
250250 description = "主持人拒绝发言申请。",
251251 params(
252252 ("room_id" = Uuid, Path, description = "room id"),
253 ("user_id" = Uuid, Path, description = "target user id")
253 ("user_id" = i64, Path, description = "target user id")
254254 ),
255255 responses(
256256 (status = 200, description = "denied", body = AppResponseData<VoiceModerationDto>),
@@ -260,7 +260,7 @@
260260pub async fn deny_hand(
261261 State(service): State<ChatService>,
262262 session: Session,
263 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
263 Path((room_id, user_id)): Path<(Uuid, i64)>,
264264) -> Result<AppResponseData<VoiceModerationDto>, ApiError> {
265265 let uid = require_uid(&session)?;
266266 Ok(AppResponseData::ok(
@@ -275,7 +275,7 @@
275275 description = "主持人收回某人的发言权:can_publish=false 并静音其麦克风。",
276276 params(
277277 ("room_id" = Uuid, Path, description = "room id"),
278 ("user_id" = Uuid, Path, description = "target user id")
278 ("user_id" = i64, Path, description = "target user id")
279279 ),
280280 responses(
281281 (status = 200, description = "revoked", body = AppResponseData<VoiceModerationDto>),
@@ -285,7 +285,7 @@
285285pub async fn revoke_hand(
286286 State(service): State<ChatService>,
287287 session: Session,
288 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
288 Path((room_id, user_id)): Path<(Uuid, i64)>,
289289) -> Result<AppResponseData<VoiceModerationDto>, ApiError> {
290290 let uid = require_uid(&session)?;
291291 Ok(AppResponseData::ok(
@@ -306,7 +306,7 @@
306306 description = "主持人强制静音或解除静音某人的麦克风(?muted=false 解除)。",
307307 params(
308308 ("room_id" = Uuid, Path, description = "room id"),
309 ("user_id" = Uuid, Path, description = "target user id"),
309 ("user_id" = i64, Path, description = "target user id"),
310310 ("muted" = Option<bool>, Query, description = "false to unmute")
311311 ),
312312 responses(
@@ -317,7 +317,7 @@
317317pub async fn moderate_mute(
318318 State(service): State<ChatService>,
319319 session: Session,
320 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
320 Path((room_id, user_id)): Path<(Uuid, i64)>,
321321 Query(query): Query<ModerateQuery>,
322322) -> Result<AppResponseData<VoiceModerationDto>, ApiError> {
323323 let uid = require_uid(&session)?;
@@ -335,7 +335,7 @@
335335 description = "主持人把某人移出通话;该用户可以再次加入。",
336336 params(
337337 ("room_id" = Uuid, Path, description = "room id"),
338 ("user_id" = Uuid, Path, description = "target user id")
338 ("user_id" = i64, Path, description = "target user id")
339339 ),
340340 responses(
341341 (status = 200, description = "removed", body = AppResponseData<VoiceModerationDto>),
@@ -345,7 +345,7 @@
345345pub async fn kick_participant(
346346 State(service): State<ChatService>,
347347 session: Session,
348 Path((room_id, user_id)): Path<(Uuid, Uuid)>,
348 Path((room_id, user_id)): Path<(Uuid, i64)>,
349349) -> Result<AppResponseData<VoiceModerationDto>, ApiError> {
350350 let uid = require_uid(&session)?;
351351 Ok(AppResponseData::ok(
Modified lib/chat/src/handlers/webhooks.rs lib/chat/src/handlers/webhooks.rs +2 −2 View lib/chat/src/handlers/webhooks.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
3use session_layer::Session;
4use session_layer::share::session::require_uid;
3use jwt::Session;
4use jwt::share::session::require_uid;
55use uuid::Uuid;
66
77use crate::errors::ApiError;
Modified lib/chat/src/services/attachments.rs lib/chat/src/services/attachments.rs +6 −6 View lib/chat/src/services/attachments.rs
@@ -21,7 +21,7 @@
2121#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2222pub struct AttachmentDto {
2323 pub id: Uuid,
24 pub user_id: Uuid,
24 pub user_id: i64,
2525 pub room_id: Option<Uuid>,
2626 pub message_id: Option<Uuid>,
2727 pub filename: String,
@@ -163,7 +163,7 @@
163163impl ChatService {
164164 pub async fn create_attachment(
165165 &self,
166 caller: Uuid,
166 caller: i64,
167167 cmd: CreateAttachmentCmd,
168168 ) -> Result<UploadIntentDto, ChatError> {
169169 let max_size = self.config().chat.upload.max_size;
@@ -241,7 +241,7 @@
241241
242242 pub async fn finalize_attachment(
243243 &self,
244 caller: Uuid,
244 caller: i64,
245245 attachment_id: Uuid,
246246 cmd: FinalizeAttachmentCmd,
247247 ) -> Result<AttachmentDto, ChatError> {
@@ -431,7 +431,7 @@
431431
432432 pub async fn get_attachment_meta(
433433 &self,
434 caller: Uuid,
434 caller: i64,
435435 attachment_id: Uuid,
436436 ) -> Result<AttachmentDto, ChatError> {
437437 let row = attachments::Entity::find_by_id(attachment_id)
@@ -448,7 +448,7 @@
448448 /// `ImageVariant::Preview` is what the transcript grid loads.
449449 pub async fn download_attachment(
450450 &self,
451 caller: Uuid,
451 caller: i64,
452452 attachment_id: Uuid,
453453 variant: ImageVariant,
454454 ) -> Result<(attachments::Model, Body, String), ChatError> {
@@ -471,7 +471,7 @@
471471
472472 async fn check_attachment_access(
473473 &self,
474 caller: Uuid,
474 caller: i64,
475475 row: &attachments::Model,
476476 ) -> Result<(), ChatError> {
477477 if row.user_id == caller {
Modified lib/chat/src/services/bookmarks.rs lib/chat/src/services/bookmarks.rs +6 −6 View lib/chat/src/services/bookmarks.rs
@@ -21,7 +21,7 @@
2121 pub url: Option<String>,
2222 pub emoji: Option<String>,
2323 pub position: i64,
24 pub created_by_id: Uuid,
24 pub created_by_id: i64,
2525 pub created_at: OffsetDateTime,
2626}
2727
@@ -64,7 +64,7 @@
6464impl ChatService {
6565 pub async fn list_bookmarks(
6666 &self,
67 caller: Uuid,
67 caller: i64,
6868 room_id: Uuid,
6969 ) -> Result<Vec<BookmarkDto>, ChatError> {
7070 let _ctx = require_room_readable(self, room_id, caller).await?;
@@ -79,7 +79,7 @@
7979
8080 pub async fn create_bookmark(
8181 &self,
82 caller: Uuid,
82 caller: i64,
8383 room_id: Uuid,
8484 cmd: CreateBookmarkCmd,
8585 ) -> Result<BookmarkDto, ChatError> {
@@ -123,7 +123,7 @@
123123
124124 pub async fn update_bookmark(
125125 &self,
126 caller: Uuid,
126 caller: i64,
127127 room_id: Uuid,
128128 bookmark_id: Uuid,
129129 cmd: UpdateBookmarkCmd,
@@ -162,7 +162,7 @@
162162
163163 pub async fn delete_bookmark(
164164 &self,
165 caller: Uuid,
165 caller: i64,
166166 room_id: Uuid,
167167 bookmark_id: Uuid,
168168 ) -> Result<(), ChatError> {
@@ -184,7 +184,7 @@
184184 async fn bookmark_manager(
185185 &self,
186186 ctx: &crate::share::lookup::RoomContext,
187 caller: Uuid,
187 caller: i64,
188188 ) -> Result<bool, ChatError> {
189189 match &ctx.space {
190190 Some(space) => crate::share::authz::can_manage_space(self, space, caller).await,
Modified lib/chat/src/services/dms.rs lib/chat/src/services/dms.rs +17 −17 View lib/chat/src/services/dms.rs
@@ -35,21 +35,21 @@
3535#[derive(Debug, Clone, Deserialize, ToSchema)]
3636pub struct OpenDmCmd {
3737 #[serde(default)]
38 pub peer_user_id: Option<Uuid>,
38 pub peer_user_id: Option<i64>,
3939 #[serde(default)]
40 pub peer_user_ids: Option<Vec<Uuid>>,
40 pub peer_user_ids: Option<Vec<i64>>,
4141}
4242
43fn dm_pair_key(a: Uuid, b: Uuid) -> String {
43fn dm_pair_key(a: i64, b: i64) -> String {
4444 let (lo, hi) = if a < b { (a, b) } else { (b, a) };
4545 format!("dm:{lo}:{hi}")
4646}
4747
48fn group_dm_key(mut uids: Vec<Uuid>) -> String {
48fn group_dm_key(mut uids: Vec<i64>) -> String {
4949 uids.sort();
5050 let mut hasher = Sha256::new();
5151 for uid in &uids {
52 hasher.update(uid.as_bytes());
52 hasher.update(uid.to_le_bytes());
5353 }
5454 let hex: String = hasher
5555 .finalize()
@@ -60,15 +60,15 @@
6060}
6161
6262impl ChatService {
63 pub async fn open_dm(&self, caller: Uuid, cmd: OpenDmCmd) -> Result<DmDto, ChatError> {
64 let mut peers: Vec<Uuid> = Vec::new();
63 pub async fn open_dm(&self, caller: i64, cmd: OpenDmCmd) -> Result<DmDto, ChatError> {
64 let mut peers: Vec<i64> = Vec::new();
6565 if let Some(peer) = cmd.peer_user_id {
6666 peers.push(peer);
6767 }
6868 if let Some(list) = cmd.peer_user_ids {
6969 peers.extend(list);
7070 }
71 let mut uniq: Vec<Uuid> = Vec::new();
71 let mut uniq: Vec<i64> = Vec::new();
7272 for peer in peers {
7373 if peer != caller && !uniq.contains(&peer) {
7474 uniq.push(peer);
@@ -85,7 +85,7 @@
8585 self.open_group_dm(caller, uniq).await
8686 }
8787
88 async fn check_dm_quota(&self, caller: Uuid) -> Result<(), ChatError> {
88 async fn check_dm_quota(&self, caller: i64) -> Result<(), ChatError> {
8989 if !self
9090 .state
9191 .redis
@@ -103,7 +103,7 @@
103103 Ok(())
104104 }
105105
106 async fn open_direct_dm(&self, caller: Uuid, peer_user_id: Uuid) -> Result<DmDto, ChatError> {
106 async fn open_direct_dm(&self, caller: i64, peer_user_id: i64) -> Result<DmDto, ChatError> {
107107 self.check_dm_quota(caller).await?;
108108 let peer = user::Entity::find_by_id(peer_user_id)
109109 .one(self.db().read())
@@ -156,7 +156,7 @@
156156 .await
157157 }
158158
159 async fn open_group_dm(&self, caller: Uuid, peers: Vec<Uuid>) -> Result<DmDto, ChatError> {
159 async fn open_group_dm(&self, caller: i64, peers: Vec<i64>) -> Result<DmDto, ChatError> {
160160 self.check_dm_quota(caller).await?;
161161 if peers.len() + 1 > MAX_GROUP_DM_MEMBERS {
162162 return Err(ChatError::BadRequest(format!(
@@ -240,8 +240,8 @@
240240 async fn ensure_dm_members(
241241 &self,
242242 room_id: Uuid,
243 caller: Uuid,
244 uids: &[Uuid],
243 caller: i64,
244 uids: &[i64],
245245 ) -> Result<(), ChatError> {
246246 let now = OffsetDateTime::now_utc();
247247 for &uid in uids {
@@ -279,7 +279,7 @@
279279 Ok(())
280280 }
281281
282 pub async fn list_dms(&self, caller: Uuid) -> Result<Vec<DmDto>, ChatError> {
282 pub async fn list_dms(&self, caller: i64) -> Result<Vec<DmDto>, ChatError> {
283283 let memberships = room_members::Entity::find()
284284 .filter(room_members::Column::UserId.eq(caller))
285285 .filter(room_members::Column::LeftAt.is_null())
@@ -303,7 +303,7 @@
303303 .filter(room_members::Column::LeftAt.is_null())
304304 .all(self.db().read())
305305 .await?;
306 let peer_uids: Vec<Uuid> = peer_rows.iter().map(|m| m.user_id).collect();
306 let peer_uids: Vec<i64> = peer_rows.iter().map(|m| m.user_id).collect();
307307 let user_map = self.load_user_briefs(&peer_uids).await?;
308308 let mut peers_by_room: std::collections::HashMap<Uuid, Vec<UserDto>> =
309309 std::collections::HashMap::new();
@@ -361,7 +361,7 @@
361361
362362 async fn build_dm_dto(
363363 &self,
364 caller: Uuid,
364 caller: i64,
365365 room: rooms::Model,
366366 peer: Option<UserDto>,
367367 members: Vec<UserDto>,
@@ -390,7 +390,7 @@
390390 })
391391 }
392392
393 pub async fn close_dm(&self, caller: Uuid, room_id: Uuid) -> Result<(), ChatError> {
393 pub async fn close_dm(&self, caller: i64, room_id: Uuid) -> Result<(), ChatError> {
394394 let room = rooms::Entity::find_by_id(room_id)
395395 .one(self.db().read())
396396 .await?
Modified lib/chat/src/services/drafts.rs lib/chat/src/services/drafts.rs +4 −4 View lib/chat/src/services/drafts.rs
@@ -27,7 +27,7 @@
2727impl ChatService {
2828 pub async fn upsert_draft(
2929 &self,
30 caller: Uuid,
30 caller: i64,
3131 room_id: Uuid,
3232 cmd: UpsertDraftCmd,
3333 ) -> Result<DraftDto, ChatError> {
@@ -95,7 +95,7 @@
9595
9696 pub async fn get_draft(
9797 &self,
98 caller: Uuid,
98 caller: i64,
9999 room_id: Uuid,
100100 thread_id: Option<Uuid>,
101101 ) -> Result<Option<DraftDto>, ChatError> {
@@ -118,7 +118,7 @@
118118
119119 pub async fn delete_draft(
120120 &self,
121 caller: Uuid,
121 caller: i64,
122122 room_id: Uuid,
123123 thread_id: Option<Uuid>,
124124 ) -> Result<(), ChatError> {
@@ -136,7 +136,7 @@
136136 Ok(())
137137 }
138138
139 pub async fn list_my_drafts(&self, caller: Uuid) -> Result<Vec<DraftDto>, ChatError> {
139 pub async fn list_my_drafts(&self, caller: i64) -> Result<Vec<DraftDto>, ChatError> {
140140 let rows = user_drafts::Entity::find()
141141 .filter(user_drafts::Column::UserId.eq(caller))
142142 .all(self.db().read())
Modified lib/chat/src/services/emojis.rs lib/chat/src/services/emojis.rs +4 −4 View lib/chat/src/services/emojis.rs
@@ -37,7 +37,7 @@
3737impl ChatService {
3838 pub async fn get_emoji(
3939 &self,
40 caller: Uuid,
40 caller: i64,
4141 space_name: &str,
4242 emoji_id: Uuid,
4343 ) -> Result<EmojiDto, ChatError> {
@@ -60,7 +60,7 @@
6060
6161 pub async fn list_emojis(
6262 &self,
63 caller: Uuid,
63 caller: i64,
6464 space_name: &str,
6565 ) -> Result<Vec<EmojiDto>, ChatError> {
6666 let space = lookup_space_by_name(self, space_name).await?;
@@ -85,7 +85,7 @@
8585
8686 pub async fn upload_emoji(
8787 &self,
88 caller: Uuid,
88 caller: i64,
8989 space_name: &str,
9090 cmd: UploadEmojiCmd,
9191 ) -> Result<EmojiDto, ChatError> {
@@ -173,7 +173,7 @@
173173
174174 pub async fn delete_emoji(
175175 &self,
176 caller: Uuid,
176 caller: i64,
177177 space_name: &str,
178178 emoji_id: Uuid,
179179 ) -> Result<(), ChatError> {
Modified lib/chat/src/services/messages.rs lib/chat/src/services/messages.rs +30 −30 View lib/chat/src/services/messages.rs
@@ -76,9 +76,9 @@
7676 room_id: Uuid,
7777 space_id: Option<Uuid>,
7878 message_id: Uuid,
79 actor: Uuid,
79 actor: i64,
8080 body: String,
81 recipients: &[Uuid],
81 recipients: &[i64],
8282) {
8383 for &uid in recipients {
8484 match notification_decision(service, room_id, space_id, uid, false, false).await {
@@ -117,7 +117,7 @@
117117 pub id: Uuid,
118118 pub room_id: Uuid,
119119 pub author: Option<UserDto>,
120 pub author_id: Uuid,
120 pub author_id: i64,
121121 pub author_type: AuthorType,
122122 pub webhook_id: Option<Uuid>,
123123 pub thread_id: Option<Uuid>,
@@ -126,14 +126,14 @@
126126 pub forwarded_from_id: Option<Uuid>,
127127 pub message_type: ChatMessageType,
128128 pub content: String,
129 pub mentions: Vec<Uuid>,
129 pub mentions: Vec<i64>,
130130 pub mention_everyone: bool,
131131 pub attachments: Vec<AttachmentDto>,
132132 #[serde(default)]
133133 pub embeds: Vec<MessageEmbedDto>,
134134 pub has_thread: bool,
135135 pub pinned: bool,
136 pub pinned_by_id: Option<Uuid>,
136 pub pinned_by_id: Option<i64>,
137137 pub pinned_at: Option<OffsetDateTime>,
138138 pub edited_at: Option<OffsetDateTime>,
139139 pub deleted_at: Option<OffsetDateTime>,
@@ -233,7 +233,7 @@
233233 pub after_seq: Option<i64>,
234234 pub limit: Option<u64>,
235235 pub thread_id: Option<Uuid>,
236 pub author_id: Option<Uuid>,
236 pub author_id: Option<i64>,
237237}
238238
239239#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
@@ -257,12 +257,12 @@
257257 pub(crate) async fn build_message_dto(
258258 &self,
259259 rows: Vec<messages::Model>,
260 viewer: Uuid,
260 viewer: i64,
261261 ) -> Result<Vec<MessageDto>, ChatError> {
262262 if rows.is_empty() {
263263 return Ok(Vec::new());
264264 }
265 let author_ids: Vec<Uuid> = rows.iter().map(|m| m.author_id).collect();
265 let author_ids: Vec<i64> = rows.iter().map(|m| m.author_id).collect();
266266 let user_map = self.load_user_briefs(&author_ids).await?;
267267
268268 let message_ids: Vec<Uuid> = rows.iter().map(|m| m.id).collect();
@@ -298,7 +298,7 @@
298298 let items = rows
299299 .into_iter()
300300 .map(|m| {
301 let mentions: Vec<Uuid> =
301 let mentions: Vec<i64> =
302302 serde_json::from_value(m.mentions.clone()).unwrap_or_default();
303303 MessageDto {
304304 id: m.id,
@@ -340,7 +340,7 @@
340340 Ok(items)
341341 }
342342
343 async fn check_slow_mode(&self, ctx: &RoomContext, caller: Uuid) -> Result<(), ChatError> {
343 async fn check_slow_mode(&self, ctx: &RoomContext, caller: i64) -> Result<(), ChatError> {
344344 let Some(limit) = ctx.room.rate_limit_per_user else {
345345 return Ok(());
346346 };
@@ -377,7 +377,7 @@
377377)]
378378 pub async fn send_message(
379379 &self,
380 caller: Uuid,
380 caller: i64,
381381 room_id: Uuid,
382382 cmd: SendMessageCmd,
383383 ) -> Result<MessageDto, ChatError> {
@@ -387,7 +387,7 @@
387387
388388 pub(crate) async fn send_message_internal(
389389 &self,
390 caller: Uuid,
390 caller: i64,
391391 room_id: Uuid,
392392 cmd: SendMessageCmd,
393393 attachment_copies: Vec<attachment_rows::Model>,
@@ -670,7 +670,7 @@
670670 // following the thread. A person who was also @-mentioned
671671 // gets one row, not two — the mention is the more specific
672672 // address, so it wins.
673 let mut reply_targets: Vec<Uuid> = Vec::new();
673 let mut reply_targets: Vec<i64> = Vec::new();
674674 if let Some(author) = reply_parent_for_txn
675675 && author != caller
676676 && !mention_uids_for_txn.contains(&author)
@@ -795,7 +795,7 @@
795795 }
796796 crate::services::unfurl::spawn_unfurl(self, room_id, message_id, content_for_email.clone());
797797
798 let recipients: Vec<Uuid> = mention_uids
798 let recipients: Vec<i64> = mention_uids
799799 .iter()
800800 .copied()
801801 .filter(|uid| *uid != caller)
@@ -840,7 +840,7 @@
840840
841841 if !mention_uids.is_empty() {
842842 let room_name = ctx.room.name.clone();
843 let email_recipients: Vec<Uuid> = mention_uids
843 let email_recipients: Vec<i64> = mention_uids
844844 .iter()
845845 .copied()
846846 .filter(|uid| *uid != caller)
@@ -891,7 +891,7 @@
891891
892892 pub async fn list_messages(
893893 &self,
894 caller: Uuid,
894 caller: i64,
895895 room_id: Uuid,
896896 q: ListMessageQuery,
897897 ) -> Result<MessagePage, ChatError> {
@@ -959,7 +959,7 @@
959959
960960 pub async fn get_message(
961961 &self,
962 caller: Uuid,
962 caller: i64,
963963 room_id: Uuid,
964964 message_id: Uuid,
965965 ) -> Result<MessageDto, ChatError> {
@@ -976,7 +976,7 @@
976976
977977 pub async fn update_message(
978978 &self,
979 caller: Uuid,
979 caller: i64,
980980 room_id: Uuid,
981981 message_id: Uuid,
982982 cmd: UpdateMessageCmd,
@@ -1025,9 +1025,9 @@
10251025 } else {
10261026 false
10271027 };
1028 let old_mentions: Vec<Uuid> =
1028 let old_mentions: Vec<i64> =
10291029 serde_json::from_value(row.mentions.clone()).unwrap_or_default();
1030 let new_mentions: Vec<Uuid> = parsed
1030 let new_mentions: Vec<i64> = parsed
10311031 .users
10321032 .iter()
10331033 .copied()
@@ -1154,7 +1154,7 @@
11541154
11551155 pub async fn delete_message(
11561156 &self,
1157 caller: Uuid,
1157 caller: i64,
11581158 room_id: Uuid,
11591159 message_id: Uuid,
11601160 ) -> Result<(), ChatError> {
@@ -1164,7 +1164,7 @@
11641164
11651165 pub async fn bulk_delete_messages(
11661166 &self,
1167 caller: Uuid,
1167 caller: i64,
11681168 room_id: Uuid,
11691169 cmd: BulkDeleteCmd,
11701170 ) -> Result<(), ChatError> {
@@ -1179,7 +1179,7 @@
11791179
11801180 async fn delete_messages_internal(
11811181 &self,
1182 caller: Uuid,
1182 caller: i64,
11831183 room_id: Uuid,
11841184 message_ids: Vec<Uuid>,
11851185 ) -> Result<(), ChatError> {
@@ -1303,7 +1303,7 @@
13031303
13041304 pub async fn set_message_pin(
13051305 &self,
1306 caller: Uuid,
1306 caller: i64,
13071307 room_id: Uuid,
13081308 message_id: Uuid,
13091309 cmd: PinCmd,
@@ -1340,7 +1340,7 @@
13401340 .col_expr(messages::Column::PinnedAt, Expr::value(now));
13411341 } else {
13421342 update = update
1343 .col_expr(messages::Column::PinnedBy, Expr::value(None::<Uuid>))
1343 .col_expr(messages::Column::PinnedBy, Expr::value(None::<i64>))
13441344 .col_expr(
13451345 messages::Column::PinnedAt,
13461346 Expr::value(None::<OffsetDateTime>),
@@ -1367,7 +1367,7 @@
13671367
13681368 pub async fn list_pins(
13691369 &self,
1370 caller: Uuid,
1370 caller: i64,
13711371 room_id: Uuid,
13721372 ) -> Result<Vec<MessageDto>, ChatError> {
13731373 let _ctx = require_room_readable(self, room_id, caller).await?;
@@ -1383,7 +1383,7 @@
13831383
13841384 pub async fn forward_message(
13851385 &self,
1386 caller: Uuid,
1386 caller: i64,
13871387 room_id: Uuid,
13881388 message_id: Uuid,
13891389 cmd: ForwardMessageCmd,
@@ -1418,7 +1418,7 @@
14181418
14191419 pub async fn list_message_history(
14201420 &self,
1421 caller: Uuid,
1421 caller: i64,
14221422 room_id: Uuid,
14231423 message_id: Uuid,
14241424 ) -> Result<Vec<MessageEditDto>, ChatError> {
@@ -1435,7 +1435,7 @@
14351435 .order_by_desc(message_edits::Column::CreatedAt)
14361436 .all(self.db().read())
14371437 .await?;
1438 let editor_ids: Vec<Uuid> = rows.iter().map(|r| r.editor_id).collect();
1438 let editor_ids: Vec<i64> = rows.iter().map(|r| r.editor_id).collect();
14391439 let user_map = self.load_user_briefs(&editor_ids).await?;
14401440 Ok(rows
14411441 .into_iter()
@@ -1451,7 +1451,7 @@
14511451
14521452 pub(crate) async fn sender_mark_read(
14531453 &self,
1454 caller: Uuid,
1454 caller: i64,
14551455 room_id: Uuid,
14561456 message_id: Uuid,
14571457 seq: i64,
Modified lib/chat/src/services/outbound.rs lib/chat/src/services/outbound.rs +5 −5 View lib/chat/src/services/outbound.rs
@@ -267,7 +267,7 @@
267267async fn require_room_manager(
268268 service: &ChatService,
269269 room_id: Uuid,
270 caller: Uuid,
270 caller: i64,
271271) -> Result<(), ChatError> {
272272 let ctx = require_room_readable(service, room_id, caller).await?;
273273 match &ctx.space {
@@ -293,7 +293,7 @@
293293impl ChatService {
294294 pub async fn create_outbound_webhook(
295295 &self,
296 caller: Uuid,
296 caller: i64,
297297 room_id: Uuid,
298298 cmd: CreateOutboundWebhookCmd,
299299 ) -> Result<OutboundWebhookSecretDto, ChatError> {
@@ -331,7 +331,7 @@
331331
332332 pub async fn list_outbound_webhooks(
333333 &self,
334 caller: Uuid,
334 caller: i64,
335335 room_id: Uuid,
336336 ) -> Result<Vec<OutboundWebhookDto>, ChatError> {
337337 require_room_manager(self, room_id, caller).await?;
@@ -344,7 +344,7 @@
344344
345345 pub async fn update_outbound_webhook(
346346 &self,
347 caller: Uuid,
347 caller: i64,
348348 room_id: Uuid,
349349 webhook_id: Uuid,
350350 cmd: UpdateOutboundWebhookCmd,
@@ -377,7 +377,7 @@
377377
378378 pub async fn delete_outbound_webhook(
379379 &self,
380 caller: Uuid,
380 caller: i64,
381381 room_id: Uuid,
382382 webhook_id: Uuid,
383383 ) -> Result<(), ChatError> {
Modified lib/chat/src/services/presence.rs lib/chat/src/services/presence.rs +17 −17 View lib/chat/src/services/presence.rs
@@ -17,7 +17,7 @@
1717
1818#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1919pub struct PresenceDto {
20 pub user_id: Uuid,
20 pub user_id: i64,
2121 pub user: Option<space::services::spaces::UserDto>,
2222 pub status: PresenceStatus,
2323 pub activities: serde_json::Value,
@@ -32,7 +32,7 @@
3232 pub activities: Option<serde_json::Value>,
3333}
3434
35fn presence_key(uid: Uuid) -> String {
35fn presence_key(uid: i64) -> String {
3636 format!("chat:presence:{uid}")
3737}
3838
@@ -64,7 +64,7 @@
6464impl ChatService {
6565 pub async fn set_presence(
6666 &self,
67 caller: Uuid,
67 caller: i64,
6868 cmd: SetPresenceCmd,
6969 ) -> Result<PresenceDto, ChatError> {
7070 let now = OffsetDateTime::now_utc();
@@ -136,7 +136,7 @@
136136 /// 已有键(手动 Away/Dnd/隐身)原样保留、只续期;没有键才写 Active 并
137137 /// 广播。多标签页/多实例共享同一份 Redis 状态:谁先连谁写,后来者续期。
138138 /// 生命周期 TTL 取两倍心跳,崩溃后由 TTL 兜底回离线。
139 pub async fn mark_online(&self, uid: Uuid) {
139 pub async fn mark_online(&self, uid: i64) {
140140 let ttl = self.config().chat.online_ttl_secs();
141141 let key = presence_key(uid);
142142 let Some(value) = encode_presence(PresenceStatus::Active, serde_json::json!([])) else {
@@ -164,7 +164,7 @@
164164 /// 心跳续期:键在只续期;键已丢失(休眠唤醒、网络割接)时补写 Active
165165 /// ——连接还活着就应当在线。补写走 `SET NX`,不会覆盖用户此刻手动设置
166166 /// 的状态。
167 pub async fn touch_presence(&self, uid: Uuid) {
167 pub async fn touch_presence(&self, uid: i64) {
168168 let ttl = self.config().chat.online_ttl_secs();
169169 let key = presence_key(uid);
170170 match export::redis::touch_expire(&self.state.redis, &key, ttl).await {
@@ -188,7 +188,7 @@
188188 ///
189189 /// 键的消失就是"离线"的唯一真相;DB 行只保留最后一次状态与时间,供
190190 /// Redis 缺失时回退读取。
191 pub async fn mark_offline(&self, uid: Uuid) {
191 pub async fn mark_offline(&self, uid: i64) {
192192 let now = OffsetDateTime::now_utc();
193193 let key = presence_key(uid);
194194 // 先读再删:DB 里留下的是用户最后处在的状态(Active/Away/Dnd/隐身)。
@@ -241,7 +241,7 @@
241241 }
242242
243243 /// `show_online_status` 开关:没有偏好行(从未保存过设置)按公开处理。
244 async fn show_online_map(&self, uids: &[Uuid]) -> Result<HashMap<Uuid, bool>, ChatError> {
244 async fn show_online_map(&self, uids: &[i64]) -> Result<HashMap<i64, bool>, ChatError> {
245245 if uids.is_empty() {
246246 return Ok(HashMap::new());
247247 }
@@ -255,14 +255,14 @@
255255 .collect())
256256 }
257257
258 pub async fn get_presence(&self, uid: Uuid) -> Result<PresenceDto, ChatError> {
258 pub async fn get_presence(&self, uid: i64) -> Result<PresenceDto, ChatError> {
259259 let mut dto = self.get_presence_raw(uid).await?;
260260 let map = self.load_user_briefs(&[uid]).await?;
261261 dto.user = map.get(&uid).cloned();
262262 Ok(dto)
263263 }
264264
265 async fn get_presence_raw(&self, uid: Uuid) -> Result<PresenceDto, ChatError> {
265 async fn get_presence_raw(&self, uid: i64) -> Result<PresenceDto, ChatError> {
266266 let mut conn = self.state.redis.connection_clone();
267267 let key = presence_key(uid);
268268 let value: Result<Option<String>, redis::RedisError> = match &mut conn {
@@ -312,8 +312,8 @@
312312 /// who turned off `show_online_status` always reads as offline.
313313 pub async fn get_presence_for(
314314 &self,
315 caller: Uuid,
316 target: Uuid,
315 caller: i64,
316 target: i64,
317317 ) -> Result<PresenceDto, ChatError> {
318318 if caller == target || self.shares_room_with(caller, target).await? {
319319 let mut dto = self.get_presence(target).await?;
@@ -333,7 +333,7 @@
333333 Err(ChatError::NotFound)
334334 }
335335
336 async fn shares_room_with(&self, caller: Uuid, target: Uuid) -> Result<bool, ChatError> {
336 async fn shares_room_with(&self, caller: i64, target: i64) -> Result<bool, ChatError> {
337337 use models::room::room_members;
338338 use models::space::space_members as sm;
339339 let my_spaces: Vec<Uuid> = sm::Entity::find()
@@ -388,7 +388,7 @@
388388 Ok(dm_peer > 0)
389389 }
390390
391 pub async fn get_presences(&self, uids: &[Uuid]) -> Result<Vec<PresenceDto>, ChatError> {
391 pub async fn get_presences(&self, uids: &[i64]) -> Result<Vec<PresenceDto>, ChatError> {
392392 if uids.is_empty() {
393393 return Ok(Vec::new());
394394 }
@@ -410,7 +410,7 @@
410410 values
411411 }
412412 };
413 let mut by_uid: HashMap<Uuid, PresencePayload> = HashMap::new();
413 let mut by_uid: HashMap<i64, PresencePayload> = HashMap::new();
414414 for (uid, value) in uids.iter().zip(values) {
415415 if let Some(value) = value
416416 && let Ok(payload) = serde_json::from_str::<PresencePayload>(&value)
@@ -418,7 +418,7 @@
418418 by_uid.insert(*uid, payload);
419419 }
420420 }
421 let missing: Vec<Uuid> = uids
421 let missing: Vec<i64> = uids
422422 .iter()
423423 .filter(|u| !by_uid.contains_key(u))
424424 .copied()
@@ -466,7 +466,7 @@
466466
467467 pub async fn list_space_presences(
468468 &self,
469 caller: Uuid,
469 caller: i64,
470470 space_name: &str,
471471 ) -> Result<Vec<PresenceDto>, ChatError> {
472472 let space = lookup_space_by_name(self, space_name).await?;
@@ -476,7 +476,7 @@
476476 .filter(space_members::Column::Status.eq(MemberStatus::Active))
477477 .all(self.db().read())
478478 .await?;
479 let mut uids: Vec<Uuid> = members.into_iter().map(|m| m.user_id).collect();
479 let mut uids: Vec<i64> = members.into_iter().map(|m| m.user_id).collect();
480480 if !uids.contains(&space.owner_id) {
481481 uids.push(space.owner_id);
482482 }
Modified lib/chat/src/services/reactions.rs lib/chat/src/services/reactions.rs +5 −5 View lib/chat/src/services/reactions.rs
@@ -33,7 +33,7 @@
3333impl ChatService {
3434 pub async fn add_reaction(
3535 &self,
36 caller: Uuid,
36 caller: i64,
3737 room_id: Uuid,
3838 message_id: Uuid,
3939 cmd: ReactionCmd,
@@ -75,7 +75,7 @@
7575
7676 pub async fn remove_reaction(
7777 &self,
78 caller: Uuid,
78 caller: i64,
7979 room_id: Uuid,
8080 message_id: Uuid,
8181 cmd: ReactionCmd,
@@ -93,7 +93,7 @@
9393
9494 pub async fn list_message_reactions(
9595 &self,
96 caller: Uuid,
96 caller: i64,
9797 room_id: Uuid,
9898 message_id: Uuid,
9999 ) -> Result<Vec<ReactionUserDto>, ChatError> {
@@ -104,7 +104,7 @@
104104 .order_by_asc(reactions::Column::CreatedAt)
105105 .all(self.db().read())
106106 .await?;
107 let uids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
107 let uids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
108108 let user_map = self.load_user_briefs(&uids).await?;
109109 Ok(rows
110110 .into_iter()
@@ -122,7 +122,7 @@
122122 &self,
123123 room_id: Uuid,
124124 message_id: Uuid,
125 viewer: Uuid,
125 viewer: i64,
126126 ) -> Result<Vec<ReactionAggDto>, ChatError> {
127127 let rows = reactions::Entity::find()
128128 .filter(reactions::Column::MessageId.eq(message_id))
Modified lib/chat/src/services/read_state.rs lib/chat/src/services/read_state.rs +4 −4 View lib/chat/src/services/read_state.rs
@@ -46,7 +46,7 @@
4646impl ChatService {
4747 pub async fn mark_read(
4848 &self,
49 caller: Uuid,
49 caller: i64,
5050 room_id: Uuid,
5151 cmd: MarkReadCmd,
5252 ) -> Result<ReadStateDto, ChatError> {
@@ -103,7 +103,7 @@
103103
104104 pub async fn get_read_state(
105105 &self,
106 caller: Uuid,
106 caller: i64,
107107 room_id: Uuid,
108108 ) -> Result<ReadStateDto, ChatError> {
109109 require_room_readable(self, room_id, caller).await?;
@@ -147,7 +147,7 @@
147147
148148 pub async fn list_space_read_states(
149149 &self,
150 caller: Uuid,
150 caller: i64,
151151 space_name: &str,
152152 ) -> Result<SpaceReadStatePage, ChatError> {
153153 let space = lookup_space_by_name(self, space_name).await?;
@@ -173,7 +173,7 @@
173173
174174 pub async fn mark_thread_read(
175175 &self,
176 caller: Uuid,
176 caller: i64,
177177 thread_id: Uuid,
178178 message_id: Option<Uuid>,
179179 ) -> Result<(), ChatError> {
Modified lib/chat/src/services/reminders.rs lib/chat/src/services/reminders.rs +4 −4 View lib/chat/src/services/reminders.rs
@@ -65,7 +65,7 @@
6565impl ChatService {
6666 pub async fn create_reminder(
6767 &self,
68 caller: Uuid,
68 caller: i64,
6969 room_id: Uuid,
7070 message_id: Uuid,
7171 cmd: CreateReminderCmd,
@@ -97,7 +97,7 @@
9797 Ok(ReminderDto::from(row))
9898 }
9999
100 pub async fn list_reminders(&self, caller: Uuid) -> Result<Vec<ReminderDto>, ChatError> {
100 pub async fn list_reminders(&self, caller: i64) -> Result<Vec<ReminderDto>, ChatError> {
101101 let rows = message_reminders::Entity::find()
102102 .filter(message_reminders::Column::UserId.eq(caller))
103103 .filter(message_reminders::Column::Status.eq(ReminderStatus::Pending))
@@ -133,7 +133,7 @@
133133
134134 pub async fn update_reminder(
135135 &self,
136 caller: Uuid,
136 caller: i64,
137137 reminder_id: Uuid,
138138 cmd: UpdateReminderCmd,
139139 ) -> Result<ReminderDto, ChatError> {
@@ -163,7 +163,7 @@
163163 Ok(ReminderDto::from(updated))
164164 }
165165
166 pub async fn cancel_reminder(&self, caller: Uuid, reminder_id: Uuid) -> Result<(), ChatError> {
166 pub async fn cancel_reminder(&self, caller: i64, reminder_id: Uuid) -> Result<(), ChatError> {
167167 let row = message_reminders::Entity::find_by_id(reminder_id)
168168 .filter(message_reminders::Column::UserId.eq(caller))
169169 .one(self.db().write())
Modified lib/chat/src/services/room_admin.rs lib/chat/src/services/room_admin.rs +16 −16 View lib/chat/src/services/room_admin.rs
@@ -26,7 +26,7 @@
2626
2727#[derive(Debug, Clone, Deserialize, ToSchema)]
2828pub struct BanRoomMemberCmd {
29 pub user_id: Uuid,
29 pub user_id: i64,
3030 #[serde(default)]
3131 pub reason: Option<String>,
3232 #[serde(
@@ -47,7 +47,7 @@
4747pub async fn active_room_role(
4848 service: &ChatService,
4949 room_id: Uuid,
50 uid: Uuid,
50 uid: i64,
5151) -> Result<Option<RoomMemberRole>, ChatError> {
5252 Ok(room_members::Entity::find_by_id((room_id, uid))
5353 .one(service.db().read())
@@ -59,7 +59,7 @@
5959async fn require_room_role(
6060 service: &ChatService,
6161 room_id: Uuid,
62 uid: Uuid,
62 uid: i64,
6363 min: RoomMemberRole,
6464) -> Result<RoomMemberRole, ChatError> {
6565 let Some(role) = active_room_role(service, room_id, uid).await? else {
@@ -71,7 +71,7 @@
7171 Ok(role)
7272}
7373
74async fn set_left_at(service: &ChatService, room_id: Uuid, uid: Uuid) -> Result<(), ChatError> {
74async fn set_left_at(service: &ChatService, room_id: Uuid, uid: i64) -> Result<(), ChatError> {
7575 let Some(row) = room_members::Entity::find_by_id((room_id, uid))
7676 .one(service.db().write())
7777 .await?
@@ -102,9 +102,9 @@
102102
103103 pub async fn get_room_member(
104104 &self,
105 caller: Uuid,
105 caller: i64,
106106 room_id: Uuid,
107 uid: Uuid,
107 uid: i64,
108108 ) -> Result<RoomMemberDto, ChatError> {
109109 crate::share::lookup::require_room_readable(self, room_id, caller).await?;
110110 let Some(row) = room_members::Entity::find_by_id((room_id, uid))
@@ -128,9 +128,9 @@
128128
129129 pub async fn kick_room_member(
130130 &self,
131 caller: Uuid,
131 caller: i64,
132132 room_id: Uuid,
133 uid: Uuid,
133 uid: i64,
134134 ) -> Result<(), ChatError> {
135135 let room = lookup_room(self, room_id).await?;
136136 if caller == uid {
@@ -158,7 +158,7 @@
158158
159159 pub async fn ban_room_member(
160160 &self,
161 caller: Uuid,
161 caller: i64,
162162 room_id: Uuid,
163163 cmd: BanRoomMemberCmd,
164164 ) -> Result<RoomBanDto, ChatError> {
@@ -202,9 +202,9 @@
202202
203203 pub async fn unban_room_member(
204204 &self,
205 caller: Uuid,
205 caller: i64,
206206 room_id: Uuid,
207 uid: Uuid,
207 uid: i64,
208208 ) -> Result<(), ChatError> {
209209 require_room_role(self, room_id, caller, RoomMemberRole::Moderator).await?;
210210 let deleted = room_bans::Entity::delete_by_id((room_id, uid))
@@ -218,7 +218,7 @@
218218
219219 pub async fn list_room_bans(
220220 &self,
221 caller: Uuid,
221 caller: i64,
222222 room_id: Uuid,
223223 ) -> Result<Vec<RoomBanDto>, ChatError> {
224224 require_room_role(self, room_id, caller, RoomMemberRole::Moderator).await?;
@@ -236,9 +236,9 @@
236236
237237 pub async fn transfer_room_owner(
238238 &self,
239 caller: Uuid,
239 caller: i64,
240240 room_id: Uuid,
241 new_owner: Uuid,
241 new_owner: i64,
242242 ) -> Result<(), ChatError> {
243243 let room = lookup_room(self, room_id).await?;
244244 if room.owner_id != caller {
@@ -280,9 +280,9 @@
280280
281281 pub async fn update_room_member(
282282 &self,
283 caller: Uuid,
283 caller: i64,
284284 room_id: Uuid,
285 uid: Uuid,
285 uid: i64,
286286 cmd: UpdateRoomMemberCmd,
287287 ) -> Result<RoomMemberDto, ChatError> {
288288 let row = room_members::Entity::find_by_id((room_id, uid))
Modified lib/chat/src/services/rooms.rs lib/chat/src/services/rooms.rs +25 −25 View lib/chat/src/services/rooms.rs
@@ -34,7 +34,7 @@
3434pub struct RoomBriefDto {
3535 pub id: Uuid,
3636 pub space_id: Option<Uuid>,
37 pub owner_id: Uuid,
37 pub owner_id: i64,
3838 pub owner: Option<UserDto>,
3939 pub kind: RoomKind,
4040 pub name: String,
@@ -81,7 +81,7 @@
8181
8282#[derive(Debug, Clone, Deserialize, ToSchema)]
8383pub struct AddMembersCmd {
84 pub user_ids: Vec<Uuid>,
84 pub user_ids: Vec<i64>,
8585}
8686
8787#[derive(Debug, Clone, Serialize, ToSchema)]
@@ -101,7 +101,7 @@
101101#[derive(Debug, Clone, Deserialize, ToSchema)]
102102pub struct CreateRoomInviteCmd {
103103 #[serde(default)]
104 pub invitee_id: Option<Uuid>,
104 pub invitee_id: Option<i64>,
105105 #[serde(default)]
106106 pub expires_secs: Option<i64>,
107107}
@@ -110,9 +110,9 @@
110110pub struct RoomInviteDto {
111111 pub id: Uuid,
112112 pub room_id: Uuid,
113 pub inviter_id: Uuid,
113 pub inviter_id: i64,
114114 pub inviter: Option<UserDto>,
115 pub invitee_id: Option<Uuid>,
115 pub invitee_id: Option<i64>,
116116 pub invitee: Option<UserDto>,
117117 pub accept_code: String,
118118 pub created_at: OffsetDateTime,
@@ -123,9 +123,9 @@
123123pub struct RoomInviteListItemDto {
124124 pub id: Uuid,
125125 pub room_id: Uuid,
126 pub inviter_id: Uuid,
126 pub inviter_id: i64,
127127 pub inviter: Option<UserDto>,
128 pub invitee_id: Option<Uuid>,
128 pub invitee_id: Option<i64>,
129129 pub invitee: Option<UserDto>,
130130 pub created_at: OffsetDateTime,
131131 pub expires_at: Option<OffsetDateTime>,
@@ -144,7 +144,7 @@
144144impl ChatService {
145145 pub async fn list_space_rooms(
146146 &self,
147 caller: Uuid,
147 caller: i64,
148148 space_name: &str,
149149 ) -> Result<Vec<RoomBriefDto>, ChatError> {
150150 let space = lookup_space_by_name(self, space_name).await?;
@@ -224,7 +224,7 @@
224224 .await?;
225225 let count_map: std::collections::HashMap<Uuid, i64> = member_counts.into_iter().collect();
226226
227 let owner_ids: Vec<Uuid> = rooms
227 let owner_ids: Vec<i64> = rooms
228228 .iter()
229229 .map(|r| r.owner_id)
230230 .collect::<Vec<_>>()
@@ -278,7 +278,7 @@
278278 Ok(result)
279279 }
280280
281 pub async fn get_room(&self, caller: Uuid, room_id: Uuid) -> Result<RoomBriefDto, ChatError> {
281 pub async fn get_room(&self, caller: i64, room_id: Uuid) -> Result<RoomBriefDto, ChatError> {
282282 let ctx = require_room_readable(self, room_id, caller).await?;
283283 let room = ctx.room;
284284 let tip = room_seq::Entity::find_by_id(room.id)
@@ -322,7 +322,7 @@
322322
323323 pub async fn update_room(
324324 &self,
325 caller: Uuid,
325 caller: i64,
326326 room_id: Uuid,
327327 cmd: UpdateRoomCmd,
328328 ) -> Result<RoomBriefDto, ChatError> {
@@ -445,7 +445,7 @@
445445
446446 pub(crate) async fn post_system_message(
447447 &self,
448 author: Uuid,
448 author: i64,
449449 room_id: Uuid,
450450 mtype: ChatMessageType,
451451 content: String,
@@ -510,7 +510,7 @@
510510 Ok(())
511511 }
512512
513 pub async fn join_room(&self, caller: Uuid, room_id: Uuid) -> Result<(), ChatError> {
513 pub async fn join_room(&self, caller: i64, room_id: Uuid) -> Result<(), ChatError> {
514514 let room = lookup_room(self, room_id).await?;
515515 let ctx = load_room_context(self, room).await?;
516516 match &ctx.space {
@@ -558,7 +558,7 @@
558558 Ok(())
559559 }
560560
561 pub async fn leave_room(&self, caller: Uuid, room_id: Uuid) -> Result<(), ChatError> {
561 pub async fn leave_room(&self, caller: i64, room_id: Uuid) -> Result<(), ChatError> {
562562 let room = lookup_room(self, room_id).await?;
563563 if room.space_id.is_none() {
564564 return Err(ChatError::BadRequest("use DM close instead".to_string()));
@@ -618,7 +618,7 @@
618618
619619 pub async fn add_room_members(
620620 &self,
621 caller: Uuid,
621 caller: i64,
622622 room_id: Uuid,
623623 cmd: AddMembersCmd,
624624 ) -> Result<(), ChatError> {
@@ -668,7 +668,7 @@
668668 pub(crate) async fn ensure_room_membership(
669669 &self,
670670 room_id: Uuid,
671 uid: Uuid,
671 uid: i64,
672672 ) -> Result<(), ChatError> {
673673 let now = OffsetDateTime::now_utc();
674674 let existing = room_members::Entity::find_by_id((room_id, uid))
@@ -713,7 +713,7 @@
713713
714714 pub async fn list_room_members(
715715 &self,
716 caller: Uuid,
716 caller: i64,
717717 room_id: Uuid,
718718 q: RoomMemberQuery,
719719 ) -> Result<Vec<RoomMemberDto>, ChatError> {
@@ -734,7 +734,7 @@
734734 )
735735 .all(self.db().read())
736736 .await?;
737 let uids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
737 let uids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
738738 let user_map = self.load_user_briefs(&uids).await?;
739739 Ok(rows
740740 .into_iter()
@@ -751,7 +751,7 @@
751751
752752 pub async fn create_room_invite(
753753 &self,
754 caller: Uuid,
754 caller: i64,
755755 room_id: Uuid,
756756 cmd: CreateRoomInviteCmd,
757757 ) -> Result<RoomInviteDto, ChatError> {
@@ -760,7 +760,7 @@
760760 return Err(ChatError::BadRequest("cannot invite to a DM".to_string()));
761761 };
762762 if !can_manage_space(self, space, caller).await?
763 && !effective_permissions(self.db().read(), space, caller)
763 && !effective_permissions(&self.state, space, caller)
764764 .await?
765765 .has(MANAGE_INVITES)
766766 {
@@ -845,7 +845,7 @@
845845 /// already-accepted invite cannot be walked back from here.
846846 pub async fn reject_room_invite(
847847 &self,
848 caller: Uuid,
848 caller: i64,
849849 accept_code: &str,
850850 ) -> Result<(), ChatError> {
851851 let invite = room_invites::Entity::find()
@@ -877,7 +877,7 @@
877877
878878 pub async fn list_room_invites(
879879 &self,
880 caller: Uuid,
880 caller: i64,
881881 room_id: Uuid,
882882 ) -> Result<Vec<RoomInviteListItemDto>, ChatError> {
883883 require_room_readable(self, room_id, caller).await?;
@@ -901,7 +901,7 @@
901901 revoked_at: r.revoked_at,
902902 })
903903 .collect();
904 let mut uids: Vec<Uuid> = items
904 let mut uids: Vec<i64> = items
905905 .iter()
906906 .flat_map(|i| {
907907 let mut v = vec![i.inviter_id];
@@ -925,7 +925,7 @@
925925
926926 pub async fn revoke_room_invite(
927927 &self,
928 caller: Uuid,
928 caller: i64,
929929 room_id: Uuid,
930930 invite_id: Uuid,
931931 ) -> Result<(), ChatError> {
@@ -958,7 +958,7 @@
958958
959959 pub async fn accept_room_invite(
960960 &self,
961 caller: Uuid,
961 caller: i64,
962962 accept_code: &str,
963963 ) -> Result<RoomBriefDto, ChatError> {
964964 if !self
Modified lib/chat/src/services/search.rs lib/chat/src/services/search.rs +5 −5 View lib/chat/src/services/search.rs
@@ -12,7 +12,7 @@
1212pub struct SearchHitDto {
1313 pub message_id: Uuid,
1414 pub room_id: Uuid,
15 pub author_id: Uuid,
15 pub author_id: i64,
1616 pub author: Option<space::services::spaces::UserDto>,
1717 pub seq: i64,
1818 pub content: String,
@@ -29,7 +29,7 @@
2929pub struct SearchQuery {
3030 pub q: String,
3131 pub room_id: Option<Uuid>,
32 pub author_id: Option<Uuid>,
32 pub author_id: Option<i64>,
3333 pub limit: Option<u64>,
3434 pub offset: Option<u64>,
3535}
@@ -38,7 +38,7 @@
3838struct SearchRow {
3939 message_id: Uuid,
4040 room_id: Uuid,
41 author_id: Uuid,
41 author_id: i64,
4242 seq: i64,
4343 content: String,
4444 created_at: OffsetDateTime,
@@ -52,7 +52,7 @@
5252impl ChatService {
5353 pub async fn search_messages(
5454 &self,
55 caller: Uuid,
55 caller: i64,
5656 space_name: &str,
5757 q: SearchQuery,
5858 ) -> Result<SearchPage, ChatError> {
@@ -147,7 +147,7 @@
147147 .all(self.db().read())
148148 .await?;
149149
150 let author_ids: Vec<Uuid> = rows.iter().map(|r| r.author_id).collect();
150 let author_ids: Vec<i64> = rows.iter().map(|r| r.author_id).collect();
151151 let author_map = self.load_user_briefs(&author_ids).await?;
152152 let items = rows
153153 .into_iter()
Modified lib/chat/src/services/service.rs lib/chat/src/services/service.rs +6 −18 View lib/chat/src/services/service.rs
@@ -1,15 +1,12 @@
11use std::collections::HashMap;
22use std::sync::Arc;
33
4use async_trait::async_trait;
54use config::AppConfig;
65use export::AppState;
76use export::database::AppDatabase;
87use export::storage::AppStorage;
98use models::users::user;
109use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
11use session_layer::SessionError;
12use session_layer::share::guard::{SessionGuard, SessionWithUser};
1310use space::services::spaces::UserDto;
1411use uuid::Uuid;
1512
@@ -42,12 +39,12 @@
4239
4340 pub async fn load_user_briefs(
4441 &self,
45 uids: &[Uuid],
46 ) -> Result<HashMap<Uuid, UserDto>, ChatError> {
42 uids: &[i64],
43 ) -> Result<HashMap<i64, UserDto>, ChatError> {
4744 if uids.is_empty() {
4845 return Ok(HashMap::new());
4946 }
50 let mut set: Vec<Uuid> = uids.to_vec();
47 let mut set: Vec<i64> = uids.to_vec();
5148 set.sort();
5249 set.dedup();
5350 let users = user::Entity::find()
@@ -66,15 +63,15 @@
6663 /// Account preference only: a fan-out event has no single ambient request
6764 /// language, and writing one language for every recipient is the bug this
6865 /// replaces.
69 pub(crate) async fn recipient_locale(&self, uid: Uuid) -> i18n::UiLocale {
66 pub(crate) async fn recipient_locale(&self, uid: i64) -> i18n::UiLocale {
7067 let fallback = i18n::config_fallback(self.config().email.default_locale());
7168 i18n::resolve_user_locale(self.db().read(), uid, fallback).await
7269 }
7370
7471 pub(crate) async fn primary_emails(
7572 &self,
76 uids: &[Uuid],
77 ) -> Result<HashMap<Uuid, String>, ChatError> {
73 uids: &[i64],
74 ) -> Result<HashMap<i64, String>, ChatError> {
7875 if uids.is_empty() {
7976 return Ok(HashMap::new());
8077 }
@@ -106,12 +103,3 @@
106103 }
107104}
108105
109#[async_trait]
110impl SessionGuard for ChatService {
111 async fn lookup_session_with_user(
112 &self,
113 token_hash: &str,
114 ) -> Result<Option<SessionWithUser>, SessionError> {
115 session_layer::lookup_session_with_user(self.db().read(), token_hash).await
116 }
117}
Modified lib/chat/src/services/settings.rs lib/chat/src/services/settings.rs +2 −2 View lib/chat/src/services/settings.rs
@@ -49,7 +49,7 @@
4949impl ChatService {
5050 pub async fn get_room_setting(
5151 &self,
52 caller: Uuid,
52 caller: i64,
5353 room_id: Uuid,
5454 ) -> Result<RoomSettingDto, ChatError> {
5555 let _ctx = require_room_readable(self, room_id, caller).await?;
@@ -71,7 +71,7 @@
7171
7272 pub async fn update_room_setting(
7373 &self,
74 caller: Uuid,
74 caller: i64,
7575 room_id: Uuid,
7676 cmd: UpdateRoomSettingCmd,
7777 ) -> Result<RoomSettingDto, ChatError> {
Modified lib/chat/src/services/threads.rs lib/chat/src/services/threads.rs +11 −11 View lib/chat/src/services/threads.rs
@@ -25,7 +25,7 @@
2525 pub room_id: Uuid,
2626 pub root_message_id: Uuid,
2727 pub owner: Option<UserDto>,
28 pub owner_id: Uuid,
28 pub owner_id: i64,
2929 pub name: Option<String>,
3030 pub reply_count: i64,
3131 pub participant_count: i64,
@@ -72,7 +72,7 @@
7272 txn: &impl ConnectionTrait,
7373 thread: &threads::Model,
7474 message_id: Uuid,
75 author: Uuid,
75 author: i64,
7676 now: OffsetDateTime,
7777) -> Result<(), ChatError> {
7878 threads::Entity::update_many()
@@ -122,7 +122,7 @@
122122 &self,
123123 rows: Vec<threads::Model>,
124124 ) -> Result<Vec<ThreadDto>, ChatError> {
125 let owner_ids: Vec<Uuid> = rows.iter().map(|t| t.owner_id).collect();
125 let owner_ids: Vec<i64> = rows.iter().map(|t| t.owner_id).collect();
126126 let user_map = self.load_user_briefs(&owner_ids).await?;
127127 Ok(rows
128128 .into_iter()
@@ -147,7 +147,7 @@
147147
148148 pub async fn create_thread(
149149 &self,
150 caller: Uuid,
150 caller: i64,
151151 room_id: Uuid,
152152 message_id: Uuid,
153153 cmd: CreateThreadCmd,
@@ -244,7 +244,7 @@
244244
245245 pub async fn list_threads(
246246 &self,
247 caller: Uuid,
247 caller: i64,
248248 room_id: Uuid,
249249 q: ListThreadQuery,
250250 ) -> Result<ThreadPage, ChatError> {
@@ -279,7 +279,7 @@
279279 })
280280 }
281281
282 pub async fn list_my_threads(&self, caller: Uuid) -> Result<Vec<ThreadDto>, ChatError> {
282 pub async fn list_my_threads(&self, caller: i64) -> Result<Vec<ThreadDto>, ChatError> {
283283 let memberships = thread_members::Entity::find()
284284 .filter(thread_members::Column::UserId.eq(caller))
285285 .order_by_desc(thread_members::Column::UpdatedAt)
@@ -297,7 +297,7 @@
297297 self.build_thread_dto(rows).await
298298 }
299299
300 pub async fn get_thread(&self, caller: Uuid, thread_id: Uuid) -> Result<ThreadDto, ChatError> {
300 pub async fn get_thread(&self, caller: i64, thread_id: Uuid) -> Result<ThreadDto, ChatError> {
301301 let thread = threads::Entity::find_by_id(thread_id)
302302 .one(self.db().read())
303303 .await?
@@ -309,7 +309,7 @@
309309
310310 pub async fn update_thread(
311311 &self,
312 caller: Uuid,
312 caller: i64,
313313 thread_id: Uuid,
314314 cmd: UpdateThreadCmd,
315315 ) -> Result<ThreadDto, ChatError> {
@@ -361,7 +361,7 @@
361361
362362 pub async fn set_thread_membership(
363363 &self,
364 caller: Uuid,
364 caller: i64,
365365 thread_id: Uuid,
366366 cmd: ThreadMemberCmd,
367367 ) -> Result<(), ChatError> {
@@ -416,7 +416,7 @@
416416 Ok(())
417417 }
418418
419 pub async fn leave_thread(&self, caller: Uuid, thread_id: Uuid) -> Result<(), ChatError> {
419 pub async fn leave_thread(&self, caller: i64, thread_id: Uuid) -> Result<(), ChatError> {
420420 let thread = threads::Entity::find_by_id(thread_id)
421421 .one(self.db().read())
422422 .await?
@@ -449,7 +449,7 @@
449449
450450 pub(crate) async fn upsert_thread_read(
451451 &self,
452 caller: Uuid,
452 caller: i64,
453453 thread_id: Uuid,
454454 message_id: Uuid,
455455 seq: i64,
Modified lib/chat/src/services/unfurl.rs lib/chat/src/services/unfurl.rs +1 −1 View lib/chat/src/services/unfurl.rs
@@ -507,7 +507,7 @@
507507 else {
508508 return;
509509 };
510 let Ok(items) = self.build_message_dto(vec![updated], Uuid::nil()).await else {
510 let Ok(items) = self.build_message_dto(vec![updated], 0).await else {
511511 return;
512512 };
513513 let Some(dto) = items.into_iter().next() else {
Modified lib/chat/src/services/voice.rs lib/chat/src/services/voice.rs +43 −43 View lib/chat/src/services/voice.rs
@@ -30,12 +30,12 @@
3030pub struct HandEntry {
3131 pub state: HandState,
3232 pub requested_at: i64,
33 pub decided_by: Option<Uuid>,
33 pub decided_by: Option<i64>,
3434}
3535
3636#[derive(Debug, Clone, Serialize, ToSchema)]
3737pub struct HandDto {
38 pub user_id: Uuid,
38 pub user_id: i64,
3939 pub display_name: String,
4040 pub avatar_url: Option<String>,
4141 pub state: HandState,
@@ -123,7 +123,7 @@
123123/// 因为本接口不是事件流,返回值天然是快照。
124124#[derive(Debug, Clone, Serialize, ToSchema)]
125125pub struct VoiceParticipantDto {
126 pub user_id: Uuid,
126 pub user_id: i64,
127127 pub display_name: String,
128128 pub avatar_url: Option<String>,
129129 pub joined_at: i64,
@@ -157,7 +157,7 @@
157157pub struct VoiceModerationDto {
158158 pub ok: bool,
159159 pub action: String,
160 pub user_id: Uuid,
160 pub user_id: i64,
161161}
162162
163163#[derive(Debug, Clone, Serialize, ToSchema)]
@@ -169,7 +169,7 @@
169169/// Redis 中一位参与者的在场记录。
170170#[derive(Debug, Clone, Serialize, Deserialize)]
171171pub struct VoiceMember {
172 pub user_id: Uuid,
172 pub user_id: i64,
173173 pub display_name: String,
174174 pub avatar_url: Option<String>,
175175 pub joined_at: i64,
@@ -254,7 +254,7 @@
254254pub async fn require_voice_room(
255255 service: &ChatService,
256256 room_id: Uuid,
257 uid: Uuid,
257 uid: i64,
258258) -> Result<models::room::rooms::Model, ChatError> {
259259 let ctx = require_room_readable(service, room_id, uid).await?;
260260 if !matches!(
@@ -272,7 +272,7 @@
272272pub async fn can_moderate_voice(
273273 service: &ChatService,
274274 room_id: Uuid,
275 uid: Uuid,
275 uid: i64,
276276) -> Result<bool, ChatError> {
277277 if let Some(role) = active_room_role(service, room_id, uid).await?
278278 && role >= models::room::room_members::RoomMemberRole::Moderator
@@ -290,7 +290,7 @@
290290pub async fn has_publish_grant(
291291 service: &ChatService,
292292 room_id: Uuid,
293 uid: Uuid,
293 uid: i64,
294294) -> Result<bool, ChatError> {
295295 if can_moderate_voice(service, room_id, uid).await? {
296296 return Ok(true);
@@ -315,14 +315,14 @@
315315}
316316
317317/// 参与者 id 的解析:identity 是 uid 字符串。
318pub fn identity_to_uid(identity: &str) -> Option<Uuid> {
319 Uuid::parse_str(identity).ok()
318pub fn identity_to_uid(identity: &str) -> Option<i64> {
319 identity.parse::<i64>().ok()
320320}
321321
322322pub async fn display_names(
323323 service: &ChatService,
324 uids: &[Uuid],
325) -> HashMap<Uuid, (String, Option<String>)> {
324 uids: &[i64],
325) -> HashMap<i64, (String, Option<String>)> {
326326 let mut map = HashMap::new();
327327 let users = match service.load_user_briefs(uids).await {
328328 Ok(users) => users,
@@ -391,7 +391,7 @@
391391pub async fn remove_member(
392392 service: &ChatService,
393393 room_id: Uuid,
394 uid: Uuid,
394 uid: i64,
395395) -> Result<(), ChatError> {
396396 let mut conn = service.state.redis.connection_clone();
397397 use export::RedisConnection;
@@ -469,7 +469,7 @@
469469pub async fn read_hand(
470470 service: &ChatService,
471471 room_id: Uuid,
472 uid: Uuid,
472 uid: i64,
473473) -> Result<Option<HandEntry>, ChatError> {
474474 let mut conn = service.state.redis.connection_clone();
475475 use export::RedisConnection;
@@ -500,7 +500,7 @@
500500pub async fn read_hands(
501501 service: &ChatService,
502502 room_id: Uuid,
503) -> Result<HashMap<Uuid, HandEntry>, ChatError> {
503) -> Result<HashMap<i64, HandEntry>, ChatError> {
504504 let mut conn = service.state.redis.connection_clone();
505505 use export::RedisConnection;
506506 let key = hands_key(room_id);
@@ -511,7 +511,7 @@
511511 let mut map = HashMap::new();
512512 for (field, value) in raw.unwrap_or_default() {
513513 if let (Ok(uid), Ok(entry)) = (
514 Uuid::parse_str(&field),
514 field.parse::<i64>(),
515515 serde_json::from_str::<HandEntry>(&value),
516516 ) {
517517 map.insert(uid, entry);
@@ -523,7 +523,7 @@
523523pub async fn write_hand(
524524 service: &ChatService,
525525 room_id: Uuid,
526 uid: Uuid,
526 uid: i64,
527527 entry: &HandEntry,
528528) -> Result<(), ChatError> {
529529 let ttl = service.config().livekit.hand_ttl_secs.max(60);
@@ -577,7 +577,7 @@
577577 /// 为调用者签发 LiveKit 访问令牌。
578578 pub async fn voice_token(
579579 &self,
580 caller: Uuid,
580 caller: i64,
581581 room_id: Uuid,
582582 ) -> Result<VoiceTokenDto, ChatError> {
583583 let cfg = self.config().livekit.clone();
@@ -666,7 +666,7 @@
666666 /// 以 LiveKit 为权威源对账,并回写 Redis。
667667 pub async fn voice_participants(
668668 &self,
669 caller: Uuid,
669 caller: i64,
670670 room_id: Uuid,
671671 ) -> Result<Vec<VoiceParticipantDto>, ChatError> {
672672 require_voice_room(self, room_id, caller).await?;
@@ -678,12 +678,12 @@
678678 .await
679679 .map_err(|error| ChatError::Internal(format!("livekit list participants: {error}")))?;
680680
681 let uids: Vec<Uuid> = participants
681 let uids: Vec<i64> = participants
682682 .iter()
683683 .filter_map(|p| identity_to_uid(&p.identity))
684684 .collect();
685685 let names = display_names(self, &uids).await;
686 let stored: HashMap<Uuid, VoiceMember> = read_members(self, room_id)
686 let stored: HashMap<i64, VoiceMember> = read_members(self, room_id)
687687 .await?
688688 .into_iter()
689689 .map(|m| (m.user_id, m))
@@ -747,7 +747,7 @@
747747 /// 侧栏用的整空间语音人数(纯 Redis 读,缺房间不报错)。
748748 pub async fn voice_space_states(
749749 &self,
750 caller: Uuid,
750 caller: i64,
751751 space_name: &str,
752752 ) -> Result<VoiceSpaceStatesDto, ChatError> {
753753 let space = lookup_space_by_name(self, space_name).await?;
@@ -775,7 +775,7 @@
775775 /// 读取房间共享的窗口布局;没有存过就是空布局(前端回落到自动网格)。
776776 pub async fn voice_layout(
777777 &self,
778 caller: Uuid,
778 caller: i64,
779779 room_id: Uuid,
780780 ) -> Result<VoiceLayoutDto, ChatError> {
781781 require_voice_room(self, room_id, caller).await?;
@@ -802,7 +802,7 @@
802802 /// 不可能发生;返回写入时刻,让客户端能分辨“我发的”和“比我新的”。
803803 pub async fn save_voice_layout(
804804 &self,
805 caller: Uuid,
805 caller: i64,
806806 room_id: Uuid,
807807 patch: VoiceLayoutPatchDto,
808808 ) -> Result<VoiceLayoutSavedDto, ChatError> {
@@ -851,7 +851,7 @@
851851 }
852852
853853 /// 举手。Stage 频道之外、已有发言权都不允许。
854 pub async fn raise_hand(&self, caller: Uuid, room_id: Uuid) -> Result<HandDto, ChatError> {
854 pub async fn raise_hand(&self, caller: i64, room_id: Uuid) -> Result<HandDto, ChatError> {
855855 let room = require_voice_room(self, room_id, caller).await?;
856856 if room.kind != models::room::rooms::RoomKind::Stage {
857857 return Err(ChatError::BadRequest(
@@ -884,7 +884,7 @@
884884 })
885885 }
886886
887 pub async fn cancel_hand(&self, caller: Uuid, room_id: Uuid) -> Result<(), ChatError> {
887 pub async fn cancel_hand(&self, caller: i64, room_id: Uuid) -> Result<(), ChatError> {
888888 require_voice_room(self, room_id, caller).await?;
889889 let existing = read_hand(self, room_id, caller).await?;
890890 if matches!(existing.map(|h| h.state), Some(HandState::Approved)) {
@@ -920,14 +920,14 @@
920920
921921 pub async fn list_hands(
922922 &self,
923 caller: Uuid,
923 caller: i64,
924924 room_id: Uuid,
925925 ) -> Result<VoiceHandsDto, ChatError> {
926926 require_voice_room(self, room_id, caller).await?;
927927 let can_moderate = can_moderate_voice(self, room_id, caller).await?;
928928 let stored = read_hands(self, room_id).await?;
929929 let my_state = stored.get(&caller).map(|h| h.state);
930 let uids: Vec<Uuid> = stored.keys().copied().collect();
930 let uids: Vec<i64> = stored.keys().copied().collect();
931931 let names = display_names(self, &uids).await;
932932 let mut hands: Vec<HandDto> = stored
933933 .into_iter()
@@ -956,9 +956,9 @@
956956
957957 async fn decide_hand(
958958 &self,
959 caller: Uuid,
959 caller: i64,
960960 room_id: Uuid,
961 target: Uuid,
961 target: i64,
962962 approve: bool,
963963 ) -> Result<VoiceModerationDto, ChatError> {
964964 require_voice_room(self, room_id, caller).await?;
@@ -1008,18 +1008,18 @@
10081008
10091009 pub async fn approve_hand(
10101010 &self,
1011 caller: Uuid,
1011 caller: i64,
10121012 room_id: Uuid,
1013 target: Uuid,
1013 target: i64,
10141014 ) -> Result<VoiceModerationDto, ChatError> {
10151015 self.decide_hand(caller, room_id, target, true).await
10161016 }
10171017
10181018 pub async fn deny_hand(
10191019 &self,
1020 caller: Uuid,
1020 caller: i64,
10211021 room_id: Uuid,
1022 target: Uuid,
1022 target: i64,
10231023 ) -> Result<VoiceModerationDto, ChatError> {
10241024 self.decide_hand(caller, room_id, target, false).await
10251025 }
@@ -1027,9 +1027,9 @@
10271027 /// 收回发言权:翻转 can_publish 并静音已发布的麦克风。
10281028 pub async fn revoke_hand(
10291029 &self,
1030 caller: Uuid,
1030 caller: i64,
10311031 room_id: Uuid,
1032 target: Uuid,
1032 target: i64,
10331033 ) -> Result<VoiceModerationDto, ChatError> {
10341034 require_voice_room(self, room_id, caller).await?;
10351035 if !can_moderate_voice(self, room_id, caller).await? {
@@ -1073,7 +1073,7 @@
10731073 pub async fn apply_publish_permission(
10741074 &self,
10751075 room_id: Uuid,
1076 target: Uuid,
1076 target: i64,
10771077 can_publish: bool,
10781078 ) -> Result<(), ChatError> {
10791079 let api = livekit(self)?;
@@ -1120,9 +1120,9 @@
11201120 /// 主持人强制静音/解除静音某人的麦克风。
11211121 pub async fn moderate_mute(
11221122 &self,
1123 caller: Uuid,
1123 caller: i64,
11241124 room_id: Uuid,
1125 target: Uuid,
1125 target: i64,
11261126 muted: bool,
11271127 ) -> Result<VoiceModerationDto, ChatError> {
11281128 require_voice_room(self, room_id, caller).await?;
@@ -1191,9 +1191,9 @@
11911191 /// 强制移出通话(可再次加入)。
11921192 pub async fn kick_participant(
11931193 &self,
1194 caller: Uuid,
1194 caller: i64,
11951195 room_id: Uuid,
1196 target: Uuid,
1196 target: i64,
11971197 ) -> Result<VoiceModerationDto, ChatError> {
11981198 require_voice_room(self, room_id, caller).await?;
11991199 if !can_moderate_voice(self, room_id, caller).await? {
@@ -1224,7 +1224,7 @@
12241224 /// 录制预留:需要 egress 组件,本次只做接口占位。
12251225 pub async fn start_recording(
12261226 &self,
1227 caller: Uuid,
1227 caller: i64,
12281228 room_id: Uuid,
12291229 ) -> Result<VoiceRecordingDto, ChatError> {
12301230 require_voice_room(self, room_id, caller).await?;
@@ -1243,7 +1243,7 @@
12431243
12441244 pub async fn stop_recording(
12451245 &self,
1246 caller: Uuid,
1246 caller: i64,
12471247 room_id: Uuid,
12481248 ) -> Result<VoiceRecordingDto, ChatError> {
12491249 require_voice_room(self, room_id, caller).await?;
Modified lib/chat/src/services/webhooks.rs lib/chat/src/services/webhooks.rs +4 −4 View lib/chat/src/services/webhooks.rs
@@ -92,7 +92,7 @@
9292impl ChatService {
9393 pub async fn create_webhook(
9494 &self,
95 caller: Uuid,
95 caller: i64,
9696 room_id: Uuid,
9797 cmd: CreateWebhookCmd,
9898 ) -> Result<WebhookSecretDto, ChatError> {
@@ -153,7 +153,7 @@
153153
154154 pub async fn list_webhooks(
155155 &self,
156 caller: Uuid,
156 caller: i64,
157157 room_id: Uuid,
158158 ) -> Result<Vec<WebhookDto>, ChatError> {
159159 let ctx = require_room_readable(self, room_id, caller).await?;
@@ -174,7 +174,7 @@
174174
175175 pub async fn update_webhook(
176176 &self,
177 caller: Uuid,
177 caller: i64,
178178 room_id: Uuid,
179179 webhook_id: Uuid,
180180 cmd: UpdateWebhookCmd,
@@ -214,7 +214,7 @@
214214
215215 pub async fn delete_webhook(
216216 &self,
217 caller: Uuid,
217 caller: i64,
218218 room_id: Uuid,
219219 webhook_id: Uuid,
220220 ) -> Result<(), ChatError> {
Modified lib/chat/src/share/authz.rs lib/chat/src/share/authz.rs +3 −3 View lib/chat/src/share/authz.rs
@@ -8,19 +8,19 @@
88pub async fn can_manage_space(
99 service: &ChatService,
1010 space: &spaces::Model,
11 uid: Uuid,
11 uid: i64,
1212) -> Result<bool, ChatError> {
1313 if space.owner_id == uid {
1414 return Ok(true);
1515 }
16 let perms = effective_permissions(service.db().read(), space, uid).await?;
16 let perms = effective_permissions(&service.state, space, uid).await?;
1717 Ok(perms.has(MANAGE_CHANNELS))
1818}
1919
2020pub async fn require_space_manager(
2121 service: &ChatService,
2222 space: &spaces::Model,
23 uid: Uuid,
23 uid: i64,
2424) -> Result<(), ChatError> {
2525 if can_manage_space(service, space, uid).await? {
2626 Ok(())
Modified lib/chat/src/share/guard.rs lib/chat/src/share/guard.rs +2 −2 View lib/chat/src/share/guard.rs
@@ -1,8 +1,8 @@
11use axum::extract::{Request, State};
22use axum::middleware::Next;
33use axum::response::Response;
4use session_layer::Session;
5use session_layer::share::guard::session_guard;
4use jwt::Session;
5use jwt::share::guard::session_guard;
66
77use crate::services::service::ChatService;
88
Modified lib/chat/src/share/lookup.rs lib/chat/src/share/lookup.rs +12 −5 View lib/chat/src/share/lookup.rs
@@ -71,7 +71,7 @@
7171pub async fn is_room_member(
7272 service: &ChatService,
7373 room_id: Uuid,
74 uid: Uuid,
74 uid: i64,
7575) -> Result<bool, ChatError> {
7676 Ok(room_members::Entity::find_by_id((room_id, uid))
7777 .one(service.db().read())
@@ -95,7 +95,7 @@
9595pub async fn require_space_member(
9696 service: &ChatService,
9797 space: &spaces::Model,
98 uid: Uuid,
98 uid: i64,
9999) -> Result<space_members::Model, ChatError> {
100100 if space.owner_id == uid {
101101 return Ok(space_members::Model {
@@ -118,6 +118,13 @@
118118 suppress_everyone: false,
119119 suppress_roles: false,
120120 hide_muted_channels: false,
121 banned_at: None,
122 ban_reason: None,
123 ban_expires_at: None,
124 ban_allow_agent_join: false,
125 ban_allow_speak: false,
126 ban_allow_push_repo: false,
127 ban_allow_comment_any: false,
121128 });
122129 }
123130 let member = space_members::Entity::find_by_id((space.id, uid))
@@ -133,11 +140,11 @@
133140pub async fn require_room_writable(
134141 service: &ChatService,
135142 room_id: Uuid,
136 uid: Uuid,
143 uid: i64,
137144) -> Result<RoomContext, ChatError> {
138145 let ctx = require_room_readable(service, room_id, uid).await?;
139146 if let Some(space) = &ctx.space {
140 let perms = effective_permissions(service.db().read(), space, uid).await?;
147 let perms = effective_permissions(&service.state, space, uid).await?;
141148 if !perms.has(MESSAGE) {
142149 return Err(ChatError::Forbidden);
143150 }
@@ -148,7 +155,7 @@
148155pub async fn require_room_readable(
149156 service: &ChatService,
150157 room_id: Uuid,
151 uid: Uuid,
158 uid: i64,
152159) -> Result<RoomContext, ChatError> {
153160 let ctx = lookup_room_context(service, room_id).await?;
154161 if let Some(ban) = room_bans::Entity::find_by_id((room_id, uid))
Modified lib/chat/src/share/mention.rs lib/chat/src/share/mention.rs +4 −4 View lib/chat/src/share/mention.rs
@@ -11,7 +11,7 @@
1111
1212#[derive(Debug, Default)]
1313pub struct ParsedMentions {
14 pub users: Vec<Uuid>,
14 pub users: Vec<i64>,
1515 pub groups: Vec<Uuid>,
1616 pub everyone: bool,
1717}
@@ -20,7 +20,7 @@
2020 service: &ChatService,
2121 ctx: &RoomContext,
2222 content: &str,
23 exclude: Uuid,
23 exclude: i64,
2424) -> Result<ParsedMentions, ChatError> {
2525 if !content.contains('@') {
2626 return Ok(ParsedMentions::default());
@@ -42,7 +42,7 @@
4242 parsed.everyone = true;
4343 continue;
4444 }
45 if let Ok(uid) = Uuid::parse_str(clean) {
45 if let Ok(uid) = clean.parse::<i64>() {
4646 if uid != exclude && !parsed.users.contains(&uid) && parsed.users.len() < max {
4747 parsed.users.push(uid);
4848 }
@@ -64,7 +64,7 @@
6464}
6565
6666enum Resolved {
67 User(Uuid),
67 User(i64),
6868 Group(Uuid),
6969 None,
7070}
Modified lib/chat/src/share/notify.rs lib/chat/src/share/notify.rs +10 −10 View lib/chat/src/share/notify.rs
@@ -17,7 +17,7 @@
1717
1818/// The recipient set of a DM / group DM message: current room members minus the
1919/// author. Used for OS pushes, which (unlike mentions) do not write inbox rows.
20pub async fn dm_recipients(service: &ChatService, room_id: Uuid, author: Uuid) -> Vec<Uuid> {
20pub async fn dm_recipients(service: &ChatService, room_id: Uuid, author: i64) -> Vec<i64> {
2121 room_members::Entity::find()
2222 .filter(room_members::Column::RoomId.eq(room_id))
2323 .filter(room_members::Column::LeftAt.is_null())
@@ -64,7 +64,7 @@
6464 db: &impl ConnectionTrait,
6565 room_id: Uuid,
6666 message_id: Uuid,
67 uids: &[Uuid],
67 uids: &[i64],
6868) -> Result<(), ChatError> {
6969 let now = OffsetDateTime::now_utc();
7070 for &uid in uids {
@@ -85,12 +85,12 @@
8585pub async fn notify_user(
8686 db: &impl ConnectionTrait,
8787 pending: &export::notify::PendingNotifications,
88 recipient: Uuid,
88 recipient: i64,
8989 ntype: NotificationType,
9090 title: String,
9191 body: Option<String>,
9292 data: Option<serde_json::Value>,
93 actor_uid: Option<Uuid>,
93 actor_uid: Option<i64>,
9494) -> Result<(), ChatError> {
9595 // The row stores already-rendered text: there is no schema column for the
9696 // language, so callers resolve it per recipient and pass finished copy in.
@@ -125,7 +125,7 @@
125125 service: &ChatService,
126126 room_id: Uuid,
127127 space_id: Option<Uuid>,
128 uid: Uuid,
128 uid: i64,
129129 mentioned: bool,
130130 everyone: bool,
131131) -> Result<NotifyDecision, ChatError> {
@@ -184,8 +184,8 @@
184184 pub is_private: bool,
185185 pub message_id: Uuid,
186186 pub body: String,
187 pub actor: Uuid,
188 pub explicit: &'a [Uuid],
187 pub actor: i64,
188 pub explicit: &'a [i64],
189189}
190190
191191/// Fan out an @everyone mention to room/space members that were not explicitly
@@ -201,7 +201,7 @@
201201 actor,
202202 explicit,
203203 } = scope;
204 let candidates: Vec<Uuid> = if is_private || space_id.is_none() {
204 let candidates: Vec<i64> = if is_private || space_id.is_none() {
205205 room_members::Entity::find()
206206 .filter(room_members::Column::RoomId.eq(room_id))
207207 .filter(room_members::Column::LeftAt.is_null())
@@ -279,7 +279,7 @@
279279 service: &ChatService,
280280 room_id: Uuid,
281281 space_id: Option<Uuid>,
282 uid: Uuid,
282 uid: i64,
283283) -> Result<bool, ChatError> {
284284 if let Some(setting) = room_user_settings::Entity::find_by_id((uid, room_id))
285285 .one(service.db().read())
@@ -335,7 +335,7 @@
335335
336336pub async fn advance_read_state(
337337 db: &impl ConnectionTrait,
338 uid: Uuid,
338 uid: i64,
339339 room_id: Uuid,
340340 message_id: Option<Uuid>,
341341 seq: i64,
Modified lib/chat/src/share/realtime.rs lib/chat/src/share/realtime.rs +9 −9 View lib/chat/src/share/realtime.rs
@@ -12,7 +12,7 @@
1212 format!("chat:room:{room_id}")
1313}
1414
15pub fn user_channel(uid: Uuid) -> String {
15pub fn user_channel(uid: i64) -> String {
1616 export::notify::user_channel(uid)
1717}
1818
@@ -52,7 +52,7 @@
5252 },
5353 Typing {
5454 room_id: Uuid,
55 user_id: Uuid,
55 user_id: i64,
5656 thread_id: Option<Uuid>,
5757 ttl_secs: u64,
5858 },
@@ -61,11 +61,11 @@
6161 },
6262 MemberJoined {
6363 room_id: Uuid,
64 user_id: Uuid,
64 user_id: i64,
6565 },
6666 MemberLeft {
6767 room_id: Uuid,
68 user_id: Uuid,
68 user_id: i64,
6969 },
7070 ThreadUpdated {
7171 room_id: Uuid,
@@ -86,7 +86,7 @@
8686 /// 主持人动作(静音、解除静音、移出、授权)。
8787 VoiceModeration {
8888 room_id: Uuid,
89 user_id: Uuid,
89 user_id: i64,
9090 action: String,
9191 },
9292}
@@ -133,7 +133,7 @@
133133 redis: export::AppRedis,
134134 pubsub_client: redis::Client,
135135 rooms: Mutex<HashMap<Uuid, ChannelEntry>>,
136 users: Mutex<HashMap<Uuid, ChannelEntry>>,
136 users: Mutex<HashMap<i64, ChannelEntry>>,
137137}
138138
139139#[derive(Clone)]
@@ -164,7 +164,7 @@
164164 self.publish(&room_channel(event.room_id()), event).await;
165165 }
166166
167 pub async fn publish_user<T: Serialize>(&self, uid: Uuid, event: &T) {
167 pub async fn publish_user<T: Serialize>(&self, uid: i64, event: &T) {
168168 self.publish(&user_channel(uid), event).await;
169169 }
170170
@@ -225,7 +225,7 @@
225225
226226 pub async fn subscribe_user(
227227 &self,
228 uid: Uuid,
228 uid: i64,
229229 ) -> Result<broadcast::Receiver<String>, ChatError> {
230230 let channel = user_channel(uid);
231231 let mut map = self.inner.users.lock().await;
@@ -238,7 +238,7 @@
238238 Ok(rx)
239239 }
240240
241 pub async fn unsubscribe_user(&self, uid: Uuid) {
241 pub async fn unsubscribe_user(&self, uid: i64) {
242242 let mut map = self.inner.users.lock().await;
243243 let remove = map
244244 .get(&uid)
Modified lib/chat/Cargo.toml lib/chat/Cargo.toml +1 −1 View lib/chat/Cargo.toml
@@ -12,7 +12,7 @@
1212models = { workspace = true }
1313search = { workspace = true }
1414social = { workspace = true }
15session-layer = { workspace = true }
15jwt = { workspace = true }
1616space = { workspace = true }
1717storage = { workspace = true }
1818axum = { workspace = true }
Modified lib/ci/src/handlers/actions.rs lib/ci/src/handlers/actions.rs +1 −1 View lib/ci/src/handlers/actions.rs
@@ -5,7 +5,7 @@
55};
66use export::rest::AppResponseData;
77use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
8use session_layer::{Session, SessionUser};
8use jwt::{Session, SessionUser};
99use uuid::Uuid;
1010
1111use crate::{
Modified lib/ci/src/handlers/artifacts.rs lib/ci/src/handlers/artifacts.rs +1 −1 View lib/ci/src/handlers/artifacts.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Path, State};
22use axum::response::Redirect;
33use export::rest::AppResponseData;
4use session_layer::Session;
4use jwt::Session;
55
66use crate::errors::ApiError;
77use crate::handlers::runs::RepoPath;
Modified lib/ci/src/handlers/logs.rs lib/ci/src/handlers/logs.rs +1 −1 View lib/ci/src/handlers/logs.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44use utoipa::IntoParams;
55
66use crate::errors::ApiError;
Modified lib/ci/src/handlers/runs.rs lib/ci/src/handlers/runs.rs +1 −1 View lib/ci/src/handlers/runs.rs
@@ -1,7 +1,7 @@
11use axum::Json;
22use axum::extract::{Path, Query, State};
33use export::rest::AppResponseData;
4use session_layer::Session;
4use jwt::Session;
55use utoipa::IntoParams;
66
77use crate::errors::ApiError;
Modified lib/ci/src/handlers/space.rs lib/ci/src/handlers/space.rs +1 −1 View lib/ci/src/handlers/space.rs
@@ -3,7 +3,7 @@
33use axum::Json;
44use axum::extract::{Path, State};
55use export::rest::AppResponseData;
6use session_layer::Session;
6use jwt::Session;
77use utoipa::ToSchema;
88use uuid::Uuid;
99
Modified lib/ci/src/share/access.rs lib/ci/src/share/access.rs +6 −6 View lib/ci/src/share/access.rs
@@ -2,8 +2,8 @@
22use git::share::errors::GitError;
33use models::repo::repos;
44use models::space::spaces;
5use session_layer::Session;
6use session_layer::SessionUser;
5use jwt::Session;
6use jwt::SessionUser;
77use uuid::Uuid;
88
99use crate::errors::CiApiError;
@@ -25,7 +25,7 @@
2525 &self,
2626 session: &Session,
2727 repo: &repos::Model,
28 ) -> AccessResult<Option<Uuid>> {
28 ) -> AccessResult<Option<i64>> {
2929 match session.uid() {
3030 Some(uid) => {
3131 let ctx = self
@@ -52,7 +52,7 @@
5252 &self,
5353 session: &Session,
5454 repo: &repos::Model,
55 ) -> AccessResult<Uuid> {
55 ) -> AccessResult<i64> {
5656 let uid = session.uid().ok_or(CiApiError::Unauthorized)?;
5757 let ctx = self
5858 .git_context(repo)
@@ -74,7 +74,7 @@
7474 &self,
7575 session: &Session,
7676 space: &spaces::Model,
77 ) -> AccessResult<Option<Uuid>> {
77 ) -> AccessResult<Option<i64>> {
7878 match session.uid() {
7979 Some(uid) => {
8080 if space.owner_id != uid && !is_member(self, space.id, uid).await? {
@@ -88,7 +88,7 @@
8888 }
8989}
9090
91async fn is_member(service: &CiService, space_id: Uuid, uid: Uuid) -> AccessResult<bool> {
91async fn is_member(service: &CiService, space_id: Uuid, uid: i64) -> AccessResult<bool> {
9292 use models::space::space_members;
9393 use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
9494
Modified lib/ci/src/actions_environments.rs lib/ci/src/actions_environments.rs +4 −4 View lib/ci/src/actions_environments.rs
@@ -17,7 +17,7 @@
1717#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1818#[serde(default)]
1919pub struct EnvironmentRules {
20 pub reviewers: Vec<Uuid>,
20 pub reviewers: Vec<i64>,
2121 pub wait_minutes: u32,
2222 #[serde(default = "default_prevent_self_review")]
2323 pub prevent_self_review: bool,
@@ -51,8 +51,8 @@
5151 pub environment: String,
5252 pub state: String,
5353 pub eligible_at: OffsetDateTime,
54 pub reviewer_id: Option<Uuid>,
55 pub reviewers: Vec<Uuid>,
54 pub reviewer_id: Option<i64>,
55 pub reviewers: Vec<i64>,
5656 pub prevent_self_review: bool,
5757}
5858
@@ -295,7 +295,7 @@
295295 repo_id: Uuid,
296296 run_id: Uuid,
297297 review_id: Uuid,
298 caller: Uuid,
298 caller: i64,
299299 approved: bool,
300300 comment: Option<String>,
301301 ) -> CiResult<()> {
Modified lib/ci/src/actions_events.rs lib/ci/src/actions_events.rs +1 −1 View lib/ci/src/actions_events.rs
@@ -26,7 +26,7 @@
2626 &self,
2727 repo: &models::repo::repos::Model,
2828 space: &models::space::spaces::Model,
29 caller: Uuid,
29 caller: i64,
3030 command: &DispatchCmd,
3131 ) -> CiResult<Vec<Uuid>> {
3232 self.ensure_enabled()?;
Modified lib/ci/src/environments.rs lib/ci/src/environments.rs +5 −5 View lib/ci/src/environments.rs
@@ -47,7 +47,7 @@
4747 pub commit_sha: String,
4848 pub git_ref: String,
4949 pub state: String,
50 pub actor_id: Option<Uuid>,
50 pub actor_id: Option<i64>,
5151 pub created_at: OffsetDateTime,
5252 pub updated_at: OffsetDateTime,
5353}
@@ -122,7 +122,7 @@
122122 repo_id: Uuid,
123123 name: &str,
124124 value: &str,
125 actor: Option<Uuid>,
125 actor: Option<i64>,
126126 ) -> CiResult<SecretDto> {
127127 self.upsert_secret(SecretScopeTarget::Repo(repo_id), name, value, actor)
128128 .await
@@ -133,7 +133,7 @@
133133 space_id: Uuid,
134134 name: &str,
135135 value: &str,
136 actor: Option<Uuid>,
136 actor: Option<i64>,
137137 ) -> CiResult<SecretDto> {
138138 self.upsert_secret(SecretScopeTarget::Space(space_id), name, value, actor)
139139 .await
@@ -144,7 +144,7 @@
144144 environment_id: Uuid,
145145 name: &str,
146146 value: &str,
147 actor: Option<Uuid>,
147 actor: Option<i64>,
148148 ) -> CiResult<SecretDto> {
149149 self.upsert_secret(
150150 SecretScopeTarget::Environment(environment_id),
@@ -160,7 +160,7 @@
160160 target: SecretScopeTarget,
161161 name: &str,
162162 value: &str,
163 actor: Option<Uuid>,
163 actor: Option<i64>,
164164 ) -> CiResult<SecretDto> {
165165 let name = validate_secret_name(name)?;
166166 if value.is_empty() {
Modified lib/ci/src/runs.rs lib/ci/src/runs.rs +2 −2 View lib/ci/src/runs.rs
@@ -29,7 +29,7 @@
2929 pub base_sha: Option<String>,
3030 pub base_ref: Option<String>,
3131 pub pr_number: Option<i64>,
32 pub trigger_user_id: Option<Uuid>,
32 pub trigger_user_id: Option<i64>,
3333}
3434
3535impl RunSeed {
@@ -357,7 +357,7 @@
357357 Ok(run_id)
358358 }
359359
360 pub async fn rerun(&self, run_id: Uuid, caller: Uuid) -> CiResult<Uuid> {
360 pub async fn rerun(&self, run_id: Uuid, caller: i64) -> CiResult<Uuid> {
361361 let run = workflow_run::Entity::find_by_id(run_id)
362362 .one(self.db_read())
363363 .await?
Modified lib/ci/src/triggers.rs lib/ci/src/triggers.rs +2 −2 View lib/ci/src/triggers.rs
@@ -29,7 +29,7 @@
2929 pub base_ref: Option<String>,
3030 pub pr_number: Option<i64>,
3131 pub state: String,
32 pub trigger_user_id: Option<Uuid>,
32 pub trigger_user_id: Option<i64>,
3333 pub started_at: Option<OffsetDateTime>,
3434 pub finished_at: Option<OffsetDateTime>,
3535 pub created_at: OffsetDateTime,
@@ -331,7 +331,7 @@
331331 &self,
332332 space: &spaces::Model,
333333 repo: &repos::Model,
334 caller: Uuid,
334 caller: i64,
335335 cmd: &TriggerRunCmd,
336336 ) -> CiResult<Uuid> {
337337 self.ensure_enabled()?;
Modified lib/ci/Cargo.toml lib/ci/Cargo.toml +1 −2 View lib/ci/Cargo.toml
@@ -4,14 +4,13 @@
44edition.workspace = true
55
66[dependencies]
7auth = { workspace = true }
87ci-protocol = { workspace = true }
98config = { workspace = true }
109export = { workspace = true }
1110git = { workspace = true }
1211gitbaby = { workspace = true }
1312models = { workspace = true }
14session-layer = { workspace = true }
13jwt = { workspace = true }
1514space = { workspace = true }
1615storage = { workspace = true }
1716axum = { workspace = true }
Modified lib/ci-protocol/src/events.rs lib/ci-protocol/src/events.rs +3 −3 View lib/ci-protocol/src/events.rs
@@ -70,7 +70,7 @@
7070 pub id: Uuid,
7171 pub repo_id: Uuid,
7272 pub space_id: Uuid,
73 pub actor_id: Option<Uuid>,
73 pub actor_id: Option<i64>,
7474 pub refs: Vec<RefUpdate>,
7575 /// Producing trace context, carried across the queue to webhook consumers.
7676 #[serde(default)]
@@ -84,7 +84,7 @@
8484 pub id: Uuid,
8585 pub repo_id: Uuid,
8686 pub space_id: Uuid,
87 pub actor_id: Option<Uuid>,
87 pub actor_id: Option<i64>,
8888 pub number: i64,
8989 pub action: PullRequestAction,
9090 pub head_sha: String,
@@ -104,7 +104,7 @@
104104 pub id: Uuid,
105105 pub repo_id: Uuid,
106106 pub space_id: Uuid,
107 pub actor_id: Option<Uuid>,
107 pub actor_id: Option<i64>,
108108 pub event: String,
109109 pub payload: serde_json::Value,
110110 #[serde(default)]
Modified lib/ci-protocol/Cargo.toml lib/ci-protocol/Cargo.toml +0 −2 View lib/ci-protocol/Cargo.toml
@@ -7,8 +7,6 @@
77globset = { workspace = true }
88serde = { workspace = true, features = ["derive"] }
99serde_json = { workspace = true }
10serde_yaml_ng = { workspace = true }
11thiserror = { workspace = true }
1210utoipa = { workspace = true }
1311time = { workspace = true }
1412uuid = { workspace = true }
Modified lib/config/src/apihost.rs lib/config/src/apihost.rs +0 −5 View lib/config/src/apihost.rs
@@ -8,14 +8,9 @@
88 pub enabled: Option<bool>,
99 pub bind_addr: Option<String>,
1010 pub port: Option<u16>,
11 pub admin_uids: Vec<String>,
1211}
1312
1413impl ApihostConfig {
15 pub fn is_platform_admin(&self, uid: &str) -> bool {
16 self.admin_uids.iter().any(|id| id == uid)
17 }
18
1914 pub fn enabled(&self) -> bool {
2015 self.enabled.unwrap_or(true)
2116 }
Modified lib/config/src/auth.rs lib/config/src/auth.rs +0 −13 View lib/config/src/auth.rs
@@ -44,7 +44,6 @@
4444 pub max_verify_attempts: u64,
4545 pub min_age: u8,
4646 pub totp_encryption_key: Option<String>,
47 pub chat_jwt_sec: Option<String>,
4847 pub agreements: Vec<AgreementDocConfig>,
4948}
5049
@@ -60,7 +59,6 @@
6059 max_verify_attempts: 5,
6160 min_age: 13,
6261 totp_encryption_key: None,
63 chat_jwt_sec: None,
6462 agreements: vec![
6563 AgreementDocConfig {
6664 key: "terms".to_string(),
@@ -126,11 +124,6 @@
126124
127125 pub fn with_totp_encryption_key(mut self, key: impl Into<String>) -> Self {
128126 self.totp_encryption_key = Some(key.into());
129 self
130 }
131
132 pub fn with_chat_jwt_sec(mut self, key: impl Into<String>) -> Self {
133 self.chat_jwt_sec = Some(key.into());
134127 self
135128 }
136129
@@ -168,12 +161,6 @@
168161 "auth.totp_encryption_key must be base64 of at least 32 bytes".to_string(),
169162 );
170163 }
171 }
172 if let Some(key) = &self.chat_jwt_sec
173 && !key.is_empty()
174 && key.len() < 32
175 {
176 return Err("auth.chat_jwt_sec must be at least 32 bytes when configured".to_string());
177164 }
178165 Ok(())
179166 }
Modified lib/config/src/lib.rs lib/config/src/lib.rs +8 −8 View lib/config/src/lib.rs
@@ -1,4 +1,3 @@
1pub mod admin;
21pub mod agents;
32pub mod ai;
43pub mod aihost;
@@ -14,9 +13,11 @@
1413pub mod health;
1514pub mod im;
1615pub mod import;
16pub mod internal;
1717pub mod issues;
1818pub mod livekit;
1919pub mod mcp;
20pub mod oauth;
2021pub mod pow;
2122pub mod push;
2223pub mod quota;
@@ -26,7 +27,6 @@
2627pub mod sandbox_rpc;
2728pub mod scoped;
2829pub mod search;
29pub mod session;
3030pub mod space;
3131pub mod user;
3232pub mod web;
@@ -37,7 +37,6 @@
3737
3838pub use error::ConfigError;
3939
40use crate::admin::AdminConfig;
4140use crate::agents::AgentsConfig;
4241use crate::ai::AiConfig;
4342use crate::aihost::AiHostServerConfig;
@@ -53,9 +52,11 @@
5352use crate::health::HealthConfig;
5453use crate::im::ImServerConfig;
5554use crate::import::ImportConfig;
55use crate::internal::InternalConfig;
5656use crate::issues::IssueConfig;
5757use crate::livekit::LiveKitConfig;
5858use crate::mcp::McpConfig;
59use crate::oauth::OAuthConfig;
5960use crate::pow::PowConfig;
6061use crate::push::PushConfig;
6162use crate::redis::RedisConfig;
@@ -63,7 +64,6 @@
6364use crate::s3::S3Config;
6465use crate::sandbox_rpc::SandboxRpcConfig;
6566use crate::search::SearchConfig;
66use crate::session::SessionConfig;
6767use crate::space::SpaceConfig;
6868use crate::user::UserConfig;
6969use crate::web::WebConfig;
@@ -77,7 +77,6 @@
7777 pub redis: RedisConfig,
7878 pub chat: ChatConfig,
7979 pub database: DatabaseConfig,
80 pub session: SessionConfig,
8180 pub search: SearchConfig,
8281 pub import: ImportConfig,
8382 pub pow: PowConfig,
@@ -91,6 +90,7 @@
9190 pub sandbox_rpc: SandboxRpcConfig,
9291 pub livekit: LiveKitConfig,
9392 pub mcp: McpConfig,
93 pub oauth: OAuthConfig,
9494 pub git: GitConfig,
9595 pub email: EmailConfig,
9696 pub email_queue: EmailQueueConfig,
@@ -98,7 +98,7 @@
9898 pub githost: GithostConfig,
9999 pub im: ImServerConfig,
100100 pub health: HealthConfig,
101 pub admin: AdminConfig,
101 pub internal: InternalConfig,
102102 pub ci: CiConfig,
103103 pub webhook: WebhookConfig,
104104 pub runner: RunnerConfig,
@@ -122,20 +122,20 @@
122122 self.email_queue
123123 .is_valid()
124124 .map_err(ConfigError::Validation)?;
125 self.session.is_valid().map_err(ConfigError::Validation)?;
126125 self.search.validate().map_err(ConfigError::Validation)?;
127126 self.import.validate().map_err(ConfigError::Validation)?;
128127 self.apihost.is_valid().map_err(ConfigError::Validation)?;
129128 self.githost.is_valid().map_err(ConfigError::Validation)?;
130129 self.im.is_valid().map_err(ConfigError::Validation)?;
131130 self.health.is_valid().map_err(ConfigError::Validation)?;
132 self.admin.is_valid().map_err(ConfigError::Validation)?;
131 self.internal.is_valid().map_err(ConfigError::Validation)?;
133132 self.ci.validate().map_err(ConfigError::Validation)?;
134133 self.webhook.is_valid().map_err(ConfigError::Validation)?;
135134 self.ai.validate().map_err(ConfigError::Validation)?;
136135 self.aihost.is_valid().map_err(ConfigError::Validation)?;
137136 self.livekit.is_valid().map_err(ConfigError::Validation)?;
138137 self.mcp.is_valid().map_err(ConfigError::Validation)?;
138 self.oauth.is_valid().map_err(ConfigError::Validation)?;
139139 self.agents.validate().map_err(ConfigError::Validation)?;
140140 self.sandbox_rpc
141141 .is_valid()
Modified lib/config/src/scoped.rs lib/config/src/scoped.rs +0 −57 View lib/config/src/scoped.rs
@@ -1,7 +1,6 @@
11use serde::Deserialize;
22use serde::de::DeserializeOwned;
33
4use crate::admin::AdminConfig as AdminSection;
54use crate::agents::AgentsConfig;
65use crate::ai::AiConfig;
76use crate::aihost::AiHostServerConfig;
@@ -28,7 +27,6 @@
2827use crate::s3::S3Config;
2928use crate::sandbox_rpc::SandboxRpcConfig;
3029use crate::search::SearchConfig;
31use crate::session::SessionConfig;
3230use crate::space::SpaceConfig;
3331use crate::user::UserConfig;
3432use crate::web::WebConfig;
@@ -194,7 +192,6 @@
194192 pub redis: RedisConfig,
195193 pub chat: ChatConfig,
196194 pub database: DatabaseConfig,
197 pub session: SessionConfig,
198195 pub pow: PowConfig,
199196 pub auth: AuthConfig,
200197 pub web: WebConfig,
@@ -225,7 +222,6 @@
225222 redis: self.redis,
226223 chat: self.chat,
227224 database: self.database,
228 session: self.session,
229225 pow: self.pow,
230226 auth: self.auth,
231227 web: self.web,
@@ -261,7 +257,6 @@
261257 self.mcp.is_valid()?;
262258 self.email.is_valid()?;
263259 self.email_queue.is_valid()?;
264 self.session.is_valid()?;
265260 self.search.validate()?;
266261 self.import.validate()?;
267262 self.apihost.is_valid()?;
@@ -313,58 +308,6 @@
313308 self.email.is_valid()?;
314309 self.email_queue.is_valid()?;
315310 self.im.is_valid()
316 }
317}
318
319#[derive(Clone, Debug, Default, Deserialize)]
320#[serde(default)]
321pub struct AdminhostConfig {
322 pub database: DatabaseConfig,
323 pub redis: RedisConfig,
324 pub s3: S3Config,
325 pub session: SessionConfig,
326 pub auth: AuthConfig,
327 pub email: EmailConfig,
328 pub email_queue: EmailQueueConfig,
329 pub search: SearchConfig,
330 pub import: ImportConfig,
331 pub git: GitConfig,
332 pub ai: AiConfig,
333 pub admin: AdminSection,
334}
335
336impl AdminhostConfig {
337 pub fn into_app_config(self) -> AppConfig {
338 AppConfig {
339 database: self.database,
340 redis: self.redis,
341 s3: self.s3,
342 session: self.session,
343 auth: self.auth,
344 email: self.email,
345 email_queue: self.email_queue,
346 search: self.search,
347 import: self.import,
348 git: self.git,
349 ai: self.ai,
350 admin: self.admin,
351 ..Default::default()
352 }
353 }
354}
355
356impl ScopedConfig for AdminhostConfig {
357 fn validate_scoped(&self) -> Result<(), String> {
358 self.redis.validate()?;
359 self.database.validate()?;
360 self.auth.validate()?;
361 self.email.is_valid()?;
362 self.email_queue.is_valid()?;
363 self.session.is_valid()?;
364 self.search.validate()?;
365 self.import.validate()?;
366 self.ai.validate()?;
367 self.admin.is_valid()
368311 }
369312}
370313
Modified lib/export/src/api_error.rs lib/export/src/api_error.rs +9 −8 View lib/export/src/api_error.rs
@@ -116,7 +116,7 @@
116116/// `IntoResponses` derive, envelope constructors, `IntoResponse`, and the
117117/// `From` impls shared by every crate (boxed self, `AppResponseData<()>`,
118118/// request rejections, `serde_json::Error`, `(StatusCode, &str)` and
119/// `session_layer::SessionError`).
119/// `jwt::SessionError`).
120120///
121121/// The enum must contain an `Internal` variant; `log` labels server-side
122122/// warnings as "`<log> handler failed`".
@@ -308,15 +308,16 @@
308308 }
309309 }
310310
311 impl From<::session_layer::SessionError> for $name {
312 fn from(error: ::session_layer::SessionError) -> Self {
311 impl From<::jwt::SessionError> for $name {
312 fn from(error: ::jwt::SessionError) -> Self {
313313 match error {
314 ::session_layer::SessionError::Unauthorized => {
315 Self::build(401, Self::payload(401, "unauthorized"))
314 ::jwt::SessionError::Unauthorized => {
315 Self::build(401, Self::payload(401, "unauthenticated"))
316316 }
317 ::session_layer::SessionError::Database(message) => {
318 ::tracing::warn!(error = %message, concat!($log, " handler failed"));
319 Self::build(500, Self::payload(500, "internal server error"))
317 ::jwt::SessionError::Expired => Self::build(401, Self::payload(401, "token_expired")),
318 ::jwt::SessionError::Verifier(message) => {
319 ::tracing::debug!(error = %message, "bearer token rejected");
320 Self::build(401, Self::payload(401, "invalid_token"))
320321 }
321322 }
322323 }
Modified lib/export/src/authz.rs lib/export/src/authz.rs +7 −7 View lib/export/src/authz.rs
@@ -1,16 +1,16 @@
11use std::collections::HashSet;
22use std::sync::{OnceLock, RwLock};
33
4use uuid::Uuid;
54
6static SYSTEM_UID: OnceLock<Uuid> = OnceLock::new();
7static SPACE_BOTS: OnceLock<RwLock<HashSet<Uuid>>> = OnceLock::new();
85
6static SYSTEM_UID: OnceLock<i64> = OnceLock::new();
7static SPACE_BOTS: OnceLock<RwLock<HashSet<i64>>> = OnceLock::new();
8
99/// Register the platform system actor (AI bot) that MCP internal keys act as.
1010/// Once set, every authorization check treats this uid as the owner of every
1111/// space and repository. The account is a bot with login prohibited, so it can
1212/// only ever be reached through the internal MCP key path.
13pub fn set_system_uid(uid: Uuid) {
13pub fn set_system_uid(uid: i64) {
1414 let _ = SYSTEM_UID.set(uid);
1515}
1616
@@ -20,18 +20,18 @@
2020/// tool dispatch and the Git HTTP auth entry points both enforce the bind).
2121/// Space bots have no password, no sessions and no access keys, so this uid
2222/// can only be reached with the token that registered it.
23pub fn register_space_bot(uid: Uuid) {
23pub fn register_space_bot(uid: i64) {
2424 let bots = SPACE_BOTS.get_or_init(|| RwLock::new(HashSet::new()));
2525 if let Ok(mut bots) = bots.write() {
2626 bots.insert(uid);
2727 }
2828}
2929
30pub fn system_uid() -> Option<Uuid> {
30pub fn system_uid() -> Option<i64> {
3131 SYSTEM_UID.get().copied()
3232}
3333
34pub fn is_system_uid(uid: Uuid) -> bool {
34pub fn is_system_uid(uid: i64) -> bool {
3535 if SYSTEM_UID.get().is_some_and(|value| *value == uid) {
3636 return true;
3737 }
Modified lib/export/src/health.rs lib/export/src/health.rs +21 −4 View lib/export/src/health.rs
@@ -12,6 +12,7 @@
1212struct HealthState {
1313 database: Option<Arc<AppDatabase>>,
1414 redis: AppRedis,
15 kenauth: Option<Arc<kenauth::KenAuthClient>>,
1516}
1617
1718async fn healthz() -> Response {
@@ -40,20 +41,36 @@
4041 redis::cmd("PING").query_async::<String>(c).await.is_ok()
4142 }
4243 };
43 if db_ok && redis_ok {
44 let kenauth_ok = match state.kenauth.as_ref() {
45 None => true,
46 Some(client) => client.check_health().await.is_ok(),
47 };
48 if db_ok && redis_ok && kenauth_ok {
4449 (StatusCode::OK, "ready").into_response()
4550 } else {
4651 (StatusCode::SERVICE_UNAVAILABLE, "not ready").into_response()
4752 }
4853}
4954
50pub fn router_with(database: Option<Arc<AppDatabase>>, redis: AppRedis) -> Router {
55pub fn router_with(
56 database: Option<Arc<AppDatabase>>,
57 redis: AppRedis,
58 kenauth: Option<Arc<kenauth::KenAuthClient>>,
59) -> Router {
5160 Router::new()
5261 .route("/healthz", axum::routing::get(healthz))
5362 .route("/readyz", axum::routing::get(readyz))
54 .with_state(HealthState { database, redis })
63 .with_state(HealthState {
64 database,
65 redis,
66 kenauth,
67 })
5568}
5669
5770pub fn router(state: AppState) -> Router {
58 router_with(Some(state.database.clone()), state.redis.clone())
71 router_with(
72 Some(state.database.clone()),
73 state.redis.clone(),
74 state.kenauth.clone(),
75 )
5976}
Modified lib/export/src/lib.rs lib/export/src/lib.rs +14 −1 View lib/export/src/lib.rs
@@ -4,10 +4,10 @@
44pub mod error;
55pub mod health;
66pub mod imaging;
7pub mod jwt;
87pub mod metrics;
98pub mod middleware;
109pub mod notify;
10pub mod oauth_client;
1111pub mod pagination;
1212pub mod push;
1313pub mod redis;
@@ -38,6 +38,7 @@
3838 pub storage: Arc<AppStorage>,
3939 pub email: EmailService,
4040 pub config: AppConfig,
41 pub kenauth: Option<Arc<kenauth::KenAuthClient>>,
4142}
4243
4344impl AppState {
@@ -61,12 +62,24 @@
6162 .await;
6263 let email = builder.build().await?;
6364
65 let kenauth = if config.oauth.enabled {
66 let client = Arc::new(
67 kenauth::KenAuthClient::connect(&config.oauth)
68 .map_err(|err| ExportError::InvalidConfig(err.to_string()))?,
69 );
70 oauth_client::install(client.clone());
71 Some(client)
72 } else {
73 None
74 };
75
6476 Ok(Self {
6577 database,
6678 redis,
6779 storage,
6880 email,
6981 config,
82 kenauth,
7083 })
7184 }
7285}
Modified lib/export/src/notify.rs lib/export/src/notify.rs +6 −6 View lib/export/src/notify.rs
@@ -7,7 +7,7 @@
77
88/// The chat WS per-user channel; every crate that writes a notification
99/// publishes to it so connected clients receive the row in real time.
10pub fn user_channel(uid: Uuid) -> String {
10pub fn user_channel(uid: i64) -> String {
1111 format!("chat:user:{uid}")
1212}
1313
@@ -18,7 +18,7 @@
1818 })
1919}
2020
21pub async fn publish_user_event(redis: &AppRedis, uid: Uuid, event: &Value) {
21pub async fn publish_user_event(redis: &AppRedis, uid: i64, event: &Value) {
2222 let payload = match serde_json::to_string(event) {
2323 Ok(payload) => payload,
2424 Err(error) => {
@@ -41,7 +41,7 @@
4141 }
4242}
4343
44pub async fn publish_notification(redis: &AppRedis, uid: Uuid, notification: Value) {
44pub async fn publish_notification(redis: &AppRedis, uid: i64, notification: Value) {
4545 let event = notification_event_json(notification);
4646 publish_user_event(redis, uid, &event).await;
4747}
@@ -49,11 +49,11 @@
4949/// Notifications created inside a DB transaction; flush after commit.
5050#[derive(Clone, Default)]
5151pub struct PendingNotifications {
52 inner: Arc<Mutex<Vec<(Uuid, Value)>>>,
52 inner: Arc<Mutex<Vec<(i64, Value)>>>,
5353}
5454
5555impl PendingNotifications {
56 pub fn push(&self, uid: Uuid, notification: Value) {
56 pub fn push(&self, uid: i64, notification: Value) {
5757 match self.inner.lock() {
5858 Ok(mut pending) => pending.push((uid, notification_event_json(notification))),
5959 Err(_) => {
@@ -63,7 +63,7 @@
6363 }
6464
6565 pub async fn flush(&self, redis: &AppRedis) {
66 let batch: Vec<(Uuid, Value)> = match self.inner.lock() {
66 let batch: Vec<(i64, Value)> = match self.inner.lock() {
6767 Ok(mut pending) => std::mem::take(&mut *pending),
6868 Err(_) => return,
6969 };
Modified lib/export/src/push.rs lib/export/src/push.rs +9 −8 View lib/export/src/push.rs
@@ -15,7 +15,7 @@
1515///
1616/// 存的是一个排序集合:每条连接一个成员(成员 id),分数是该连接占位的
1717/// 到期时刻——强杀进程留下的成员会随分数过期被惰性清理,不会污染在线判定。
18pub fn online_key(uid: Uuid) -> String {
18pub fn online_key(uid: i64) -> String {
1919 format!("chat:ws:{uid}")
2020}
2121
@@ -86,7 +86,7 @@
8686/// `data` 里的路由 slug,两者都不值得让每个写入方各做一遍。
8787#[derive(Debug, Clone, Serialize, Deserialize)]
8888pub struct PushJob {
89 pub user_id: Uuid,
89 pub user_id: i64,
9090 pub category: PushCategory,
9191 /// 站内通知的标题,worker 会在前面拼上作者显示名。
9292 pub title: String,
@@ -94,7 +94,7 @@
9494 #[serde(default)]
9595 pub data: Option<Value>,
9696 #[serde(default)]
97 pub actor_id: Option<Uuid>,
97 pub actor_id: Option<i64>,
9898 /// 站内通知主键;仅用于日志关联,不参与投递决策。
9999 #[serde(default)]
100100 pub notification_id: Option<Uuid>,
@@ -106,7 +106,7 @@
106106
107107impl PushJob {
108108 /// 把一个 `user_notifications` 事件载荷转成推送任务;形状不对时返回 `None`。
109 pub fn from_notification(user_id: Uuid, notification: &Value) -> Option<Self> {
109 pub fn from_notification(user_id: i64, notification: &Value) -> Option<Self> {
110110 let notification_type = notification.get("notification_type")?.as_str()?;
111111 let title = notification.get("title")?.as_str()?.to_string();
112112 let body = notification
@@ -114,10 +114,11 @@
114114 .and_then(Value::as_str)
115115 .map(str::to_string);
116116 let data = notification.get("data").cloned();
117 let actor_id = notification
118 .get("actor_id")
119 .and_then(Value::as_str)
120 .and_then(|raw| Uuid::parse_str(raw).ok());
117 let actor_id = notification.get("actor_id").and_then(|value| {
118 value
119 .as_i64()
120 .or_else(|| value.as_str().and_then(|raw| raw.parse::<i64>().ok()))
121 });
121122 let notification_id = notification
122123 .get("id")
123124 .and_then(Value::as_str)
Modified lib/export/src/space_token.rs lib/export/src/space_token.rs +1 −1 View lib/export/src/space_token.rs
@@ -18,7 +18,7 @@
1818 pub token_id: Uuid,
1919 pub space_id: Uuid,
2020 pub space_name: String,
21 pub bot_uid: Uuid,
21 pub bot_uid: i64,
2222 pub secret_hash: String,
2323 #[serde(default)]
2424 pub issued_by: Option<String>,
Modified lib/export/src/urls.rs lib/export/src/urls.rs +1 −1 View lib/export/src/urls.rs
@@ -29,7 +29,7 @@
2929/// non-`storage://` value — e.g. an avatar URL imported from another host —
3030/// is passed through untouched.
3131#[must_use]
32pub fn resolve_user_avatar_url(uid: Uuid, raw: &str) -> String {
32pub fn resolve_user_avatar_url(uid: i64, raw: &str) -> String {
3333 if raw.is_empty() {
3434 String::new()
3535 } else if raw.starts_with(STORAGE_SCHEME) {
Modified lib/export/Cargo.toml lib/export/Cargo.toml +1 −2 View lib/export/Cargo.toml
@@ -10,6 +10,7 @@
1010redis = { workspace = true }
1111tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] }
1212email = { workspace = true }
13kenauth = { workspace = true }
1314imaging = { workspace = true }
1415axum = { workspace = true }
1516serde = { workspace = true, features = ["derive"] }
@@ -24,13 +25,11 @@
2425reqwest = { workspace = true }
2526tracing-opentelemetry = { workspace = true }
2627utoipa = { workspace = true }
27rand.workspace = true
2828tower = { workspace = true }
2929tower-http = { workspace = true }
3030serde_json = { workspace = true }
3131uuid = { workspace = true }
3232bytes = { workspace = true }
3333http-body-util = { workspace = true }
34jsonwebtoken = { workspace = true }
3534thiserror = { workspace = true }
3635time = { workspace = true }
Modified lib/git/src/handler/blame.rs lib/git/src/handler/blame.rs +1 −1 View lib/git/src/handler/blame.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::{ApiError, GitApiError};
66use crate::handler::resolve_reader;
Modified lib/git/src/handler/graphql.rs lib/git/src/handler/graphql.rs +2 −2 View lib/git/src/handler/graphql.rs
@@ -1,7 +1,7 @@
11use axum::Json;
22use axum::extract::State;
33use axum::response::IntoResponse;
4use session_layer::{Session, SessionUser};
4use jwt::{Session, SessionUser};
55use uuid::Uuid;
66
77use crate::errors::ApiError;
@@ -24,7 +24,7 @@
2424 schema: axum::extract::Extension<GitSchema>,
2525 Json(mut request): Json<async_graphql::Request>,
2626) -> Result<axum::response::Response, ApiError> {
27 request = request.data::<Option<Uuid>>(session.uid());
27 request = request.data::<Option<i64>>(session.uid());
2828 let response = schema.execute(request).await;
2929 Ok(Json(response).into_response())
3030}
Modified lib/git/src/handler/path_log.rs lib/git/src/handler/path_log.rs +1 −1 View lib/git/src/handler/path_log.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::{ApiError, GitApiError};
66use crate::handler::resolve_reader;
Modified lib/git/src/handler/share.rs lib/git/src/handler/share.rs +6 −6 View lib/git/src/handler/share.rs
@@ -1,7 +1,7 @@
11use axum::extract::{FromRequestParts, Path};
22use axum::http::request::Parts;
33use serde::Deserialize;
4use session_layer::{Session, SessionUser};
4use jwt::{Session, SessionUser};
55use utoipa::IntoParams;
66use uuid::Uuid;
77
@@ -36,7 +36,7 @@
3636 }
3737}
3838
39pub fn ensure_caller(session: &Session) -> Result<Uuid, ApiError> {
39pub fn ensure_caller(session: &Session) -> Result<i64, ApiError> {
4040 session.uid().ok_or(ApiError::unauthorized())
4141}
4242
@@ -45,7 +45,7 @@
4545 session: &Session,
4646 space_name: String,
4747 repo_name: String,
48) -> Result<(crate::context::facade::GitContext, Option<Uuid>), ApiError> {
48) -> Result<(crate::context::facade::GitContext, Option<i64>), ApiError> {
4949 let ctx = service
5050 .context(space_name, repo_name)
5151 .await
@@ -65,7 +65,7 @@
6565 service: &GitService,
6666 session: &Session,
6767 ctx: &crate::context::facade::GitContext,
68) -> Result<Option<Uuid>, ApiError> {
68) -> Result<Option<i64>, ApiError> {
6969 if ctx.model.is_private {
7070 let uid = session.uid().ok_or(ApiError::unauthorized())?;
7171 authorize_repo_access(service, uid, ctx, false, false)
@@ -82,7 +82,7 @@
8282 session: &Session,
8383 space_name: String,
8484 repo_name: String,
85) -> Result<(crate::context::facade::GitContext, Uuid), ApiError> {
85) -> Result<(crate::context::facade::GitContext, i64), ApiError> {
8686 let ctx = service
8787 .context(space_name, repo_name)
8888 .await
@@ -99,7 +99,7 @@
9999 session: &Session,
100100 space_name: String,
101101 repo_name: String,
102) -> Result<(crate::context::facade::GitContext, Uuid), ApiError> {
102) -> Result<(crate::context::facade::GitContext, i64), ApiError> {
103103 let ctx = service
104104 .context(space_name, repo_name)
105105 .await
Modified lib/git/src/handler/stars.rs lib/git/src/handler/stars.rs +1 −1 View lib/git/src/handler/stars.rs
@@ -1,7 +1,7 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
33use serde::Deserialize;
4use session_layer::Session;
4use jwt::Session;
55use utoipa::IntoParams;
66
77use crate::errors::{ApiError, GitApiError};
Modified lib/git/src/handler/topics.rs lib/git/src/handler/topics.rs +1 −1 View lib/git/src/handler/topics.rs
@@ -2,7 +2,7 @@
22use axum::extract::{Path, Query, State};
33use export::rest::{AppResponseData, NoData};
44use serde::{Deserialize, Serialize};
5use session_layer::Session;
5use jwt::Session;
66use utoipa::IntoParams;
77
88use crate::errors::{ApiError, GitApiError};
Modified lib/git/src/handler/watches.rs lib/git/src/handler/watches.rs +1 −1 View lib/git/src/handler/watches.rs
@@ -3,7 +3,7 @@
33use export::rest::AppResponseData;
44use models::common::watch_mode::WatchMode;
55use serde::Deserialize;
6use session_layer::Session;
6use jwt::Session;
77use utoipa::IntoParams;
88
99use crate::errors::{ApiError, GitApiError};
Modified lib/git/src/handler/whitelists.rs lib/git/src/handler/whitelists.rs +2 −2 View lib/git/src/handler/whitelists.rs
@@ -5,7 +5,7 @@
55use utoipa::IntoParams;
66use uuid::Uuid;
77
8use session_layer::Session;
8use jwt::Session;
99
1010use crate::errors::{ApiError, GitApiError};
1111use crate::handler::require_repo_admin;
@@ -41,7 +41,7 @@
4141 pub space_name: String,
4242 pub repo_name: String,
4343 pub policy_id: Uuid,
44 pub user_id: Uuid,
44 pub user_id: i64,
4545}
4646
4747#[utoipa::path(
Modified lib/git/src/lfs/lock.rs lib/git/src/lfs/lock.rs +5 −5 View lib/git/src/lfs/lock.rs
@@ -18,7 +18,7 @@
1818 svc: &GitService,
1919 space_name: String,
2020 repo_name: String,
21 caller: Uuid,
21 caller: i64,
2222 path: String,
2323 oid: Option<String>,
2424) -> Result<LfsLock, GitError> {
@@ -55,7 +55,7 @@
5555 svc: &GitService,
5656 space_name: String,
5757 repo_name: String,
58 caller: Uuid,
58 caller: i64,
5959 path: String,
6060 force: bool,
6161) -> Result<(), GitError> {
@@ -81,7 +81,7 @@
8181 svc: &GitService,
8282 space_name: String,
8383 repo_name: String,
84 caller: Uuid,
84 caller: i64,
8585 lock_id: String,
8686 force: bool,
8787) -> Result<(), GitError> {
@@ -138,7 +138,7 @@
138138 svc: &GitService,
139139 space_name: String,
140140 repo_name: String,
141 caller: Uuid,
141 caller: i64,
142142 limit: u64,
143143) -> Result<LfsVerifyLocksResponse, GitError> {
144144 let ctx = svc.context(space_name, repo_name).await?;
@@ -187,7 +187,7 @@
187187 locks
188188}
189189
190async fn owner_name(svc: &GitService, uid: Uuid) -> String {
190async fn owner_name(svc: &GitService, uid: i64) -> String {
191191 match user::Entity::find_by_id(uid).one(svc.db_read()).await {
192192 Ok(Some(u)) if !u.display_name.trim().is_empty() => u.display_name,
193193 Ok(Some(u)) => u.username,
Modified lib/git/src/lfs/object.rs lib/git/src/lfs/object.rs +2 −2 View lib/git/src/lfs/object.rs
@@ -153,7 +153,7 @@
153153 svc: &GitService,
154154 space_name: String,
155155 repo_name: String,
156 caller: Uuid,
156 caller: i64,
157157 oid: String,
158158 size: i64,
159159 content_type: Option<String>,
@@ -259,7 +259,7 @@
259259 svc: &GitService,
260260 space_name: String,
261261 repo_name: String,
262 caller: Uuid,
262 caller: i64,
263263 oid: String,
264264 size: i64,
265265 content_type: Option<String>,
Modified lib/git/src/service/ai_review.rs lib/git/src/service/ai_review.rs +2 −2 View lib/git/src/service/ai_review.rs
@@ -85,7 +85,7 @@
8585 space_name: String,
8686 repo_name: String,
8787 revision: String,
88 creator: Uuid,
88 creator: i64,
8989 language: Option<String>,
9090 ) -> Result<AiReviewJobDto, GitError> {
9191 let cfg = &self.state.config.ai;
@@ -146,7 +146,7 @@
146146 &self,
147147 space_name: String,
148148 repo_name: String,
149 creator: Uuid,
149 creator: i64,
150150 status: AiReviewStatus,
151151 ) -> Result<(), GitError> {
152152 let AiReviewStatus {
Modified lib/git/src/service/ai_summary.rs lib/git/src/service/ai_summary.rs +1 −1 View lib/git/src/service/ai_summary.rs
@@ -122,7 +122,7 @@
122122 &self,
123123 space_name: String,
124124 repo_name: String,
125 actor: Option<Uuid>,
125 actor: Option<i64>,
126126 cmd: TriggerSummaryCmd,
127127 ) -> Result<Uuid, GitError> {
128128 let cfg = &self.state.config.ai;
Modified lib/git/src/service/code_comments.rs lib/git/src/service/code_comments.rs +6 −6 View lib/git/src/service/code_comments.rs
@@ -26,7 +26,7 @@
2626 pub parent_id: Option<Uuid>,
2727 pub body: String,
2828 pub resolved: bool,
29 pub resolved_by: Option<Uuid>,
29 pub resolved_by: Option<i64>,
3030 /// 该评论锚定的提交是否仍是当前分支提示(否则视为 outdated)。
3131 pub outdated: bool,
3232 pub created_at: OffsetDateTime,
@@ -129,7 +129,7 @@
129129 &self,
130130 space_name: String,
131131 repo_name: String,
132 author: Uuid,
132 author: i64,
133133 cmd: CreateCodeCommentCmd,
134134 ) -> Result<CodeCommentDto, GitError> {
135135 if cmd.path.trim().is_empty() {
@@ -196,7 +196,7 @@
196196 space_name: String,
197197 repo_name: String,
198198 comment_id: Uuid,
199 actor: Uuid,
199 actor: i64,
200200 admin: bool,
201201 cmd: UpdateCodeCommentCmd,
202202 ) -> Result<CodeCommentDto, GitError> {
@@ -221,7 +221,7 @@
221221 space_name: String,
222222 repo_name: String,
223223 comment_id: Uuid,
224 actor: Uuid,
224 actor: i64,
225225 admin: bool,
226226 ) -> Result<(), GitError> {
227227 let ctx = self.context(space_name, repo_name).await?;
@@ -247,7 +247,7 @@
247247 space_name: String,
248248 repo_name: String,
249249 comment_id: Uuid,
250 actor: Uuid,
250 actor: i64,
251251 resolved: bool,
252252 ) -> Result<CodeCommentDto, GitError> {
253253 let ctx = self.context(space_name, repo_name).await?;
@@ -284,7 +284,7 @@
284284 if rows.is_empty() {
285285 return Ok(Vec::new());
286286 }
287 let uids: Vec<Uuid> = rows.iter().map(|row| row.author_id).collect();
287 let uids: Vec<i64> = rows.iter().map(|row| row.author_id).collect();
288288 let users = self.user_dto_map(&uids).await?;
289289 // outdated 判定:锚定提交不是当前默认分支提示即视为过期。
290290 let head = self
Modified lib/git/src/service/commits.rs lib/git/src/service/commits.rs +6 −6 View lib/git/src/service/commits.rs
@@ -129,7 +129,7 @@
129129 pub context: String,
130130 pub description: Option<String>,
131131 pub target_url: Option<String>,
132 pub creator_id: Uuid,
132 pub creator_id: i64,
133133 pub creator: Option<space::services::spaces::UserDto>,
134134 pub created_at: OffsetDateTime,
135135 pub updated_at: Option<OffsetDateTime>,
@@ -150,7 +150,7 @@
150150 }
151151}
152152
153async fn uid_for_email(db: &DatabaseConnection, email: &str) -> Result<Option<Uuid>, GitError> {
153async fn uid_for_email(db: &DatabaseConnection, email: &str) -> Result<Option<i64>, GitError> {
154154 let row = user_emails::Entity::find()
155155 .filter(user_emails::Column::LowerEmail.eq(email.to_lowercase()))
156156 .one(db)
@@ -180,7 +180,7 @@
180180 space_name: String,
181181 repo_name: String,
182182 cmd: ListCommitsCmd,
183 viewer: Option<Uuid>,
183 viewer: Option<i64>,
184184 ) -> Result<CommitsPageDto, GitError> {
185185 let ctx = self.context(space_name, repo_name).await?;
186186 let baby = self.baby(&ctx);
@@ -247,7 +247,7 @@
247247 })
248248 }
249249
250 pub async fn user_diff_view_style(&self, viewer: Option<Uuid>) -> String {
250 pub async fn user_diff_view_style(&self, viewer: Option<i64>) -> String {
251251 if let Some(uid) = viewer
252252 && let Ok(Some(row)) = models::users::user_preferences::Entity::find_by_id(uid)
253253 .one(self.db_read())
@@ -384,7 +384,7 @@
384384 &self,
385385 space_name: String,
386386 repo_name: String,
387 creator: Uuid,
387 creator: i64,
388388 cmd: SetCommitStatusCmd,
389389 ) -> Result<CommitStatusDto, GitError> {
390390 if cmd.sha.trim().is_empty() {
@@ -456,7 +456,7 @@
456456 .all(self.db_read())
457457 .await?;
458458 let mut out: Vec<CommitStatusDto> = rows.into_iter().map(CommitStatusDto::from).collect();
459 let uids: Vec<Uuid> = out.iter().map(|c| c.creator_id).collect();
459 let uids: Vec<i64> = out.iter().map(|c| c.creator_id).collect();
460460 let map = self.user_dto_map(&uids).await?;
461461 for c in out.iter_mut() {
462462 c.creator = map.get(&c.creator_id).cloned();
Modified lib/git/src/service/content.rs lib/git/src/service/content.rs +1 −1 View lib/git/src/service/content.rs
@@ -55,7 +55,7 @@
5555impl GitService {
5656 pub async fn caller_signature(
5757 &self,
58 caller: uuid::Uuid,
58 caller: i64,
5959 when: OffsetDateTime,
6060 ) -> Result<SignatureDto, GitError> {
6161 let ident = load_identity(&self.state.database, caller).await?;
Modified lib/git/src/service/lfs.rs lib/git/src/service/lfs.rs +6 −6 View lib/git/src/service/lfs.rs
@@ -23,7 +23,7 @@
2323 &self,
2424 space_name: String,
2525 repo_name: String,
26 caller: Uuid,
26 caller: i64,
2727 oid: String,
2828 size: i64,
2929 content_type: Option<String>,
@@ -47,7 +47,7 @@
4747 &self,
4848 space_name: String,
4949 repo_name: String,
50 caller: Uuid,
50 caller: i64,
5151 oid: String,
5252 size: i64,
5353 content_type: Option<String>,
@@ -104,7 +104,7 @@
104104 &self,
105105 space_name: String,
106106 repo_name: String,
107 owner: Uuid,
107 owner: i64,
108108 path: String,
109109 oid: Option<String>,
110110 ) -> Result<LfsLock, GitError> {
@@ -115,7 +115,7 @@
115115 &self,
116116 space_name: String,
117117 repo_name: String,
118 owner: Uuid,
118 owner: i64,
119119 path: String,
120120 force: bool,
121121 ) -> Result<(), GitError> {
@@ -126,7 +126,7 @@
126126 &self,
127127 space_name: String,
128128 repo_name: String,
129 owner: Uuid,
129 owner: i64,
130130 lock_id: String,
131131 force: bool,
132132 ) -> Result<(), GitError> {
@@ -147,7 +147,7 @@
147147 &self,
148148 space_name: String,
149149 repo_name: String,
150 caller: Uuid,
150 caller: i64,
151151 limit: u64,
152152 ) -> Result<LfsVerifyLocksResponse, GitError> {
153153 lfs::verify_locks(self, space_name, repo_name, caller, limit).await
Modified lib/git/src/service/merge.rs lib/git/src/service/merge.rs +2 −2 View lib/git/src/service/merge.rs
@@ -52,7 +52,7 @@
5252 pub head_revision: String,
5353 pub method: MergeMethod,
5454 pub message: Option<String>,
55 pub caller: Uuid,
55 pub caller: i64,
5656 pub allow_ff_only: bool,
5757 pub summary: String,
5858}
@@ -386,7 +386,7 @@
386386 async fn enqueue_ref_sync(
387387 &self,
388388 ctx: &GitContext,
389 actor: Option<Uuid>,
389 actor: Option<i64>,
390390 ref_name: &str,
391391 old_sha: &str,
392392 new_sha: &str,
Modified lib/git/src/service/releases.rs lib/git/src/service/releases.rs +5 −5 View lib/git/src/service/releases.rs
@@ -120,7 +120,7 @@
120120 &self,
121121 space_name: String,
122122 repo_name: String,
123 publisher: Uuid,
123 publisher: i64,
124124 cmd: CreateReleaseCmd,
125125 ) -> Result<ReleaseDto, GitError> {
126126 let tag = cmd.tag_name.trim();
@@ -191,7 +191,7 @@
191191 space_name: String,
192192 repo_name: String,
193193 tag: String,
194 actor: Uuid,
194 actor: i64,
195195 admin: bool,
196196 cmd: UpdateReleaseCmd,
197197 ) -> Result<ReleaseDto, GitError> {
@@ -246,7 +246,7 @@
246246 space_name: String,
247247 repo_name: String,
248248 tag: String,
249 actor: Uuid,
249 actor: i64,
250250 admin: bool,
251251 ) -> Result<(), GitError> {
252252 let ctx = self.context(space_name, repo_name).await?;
@@ -265,7 +265,7 @@
265265 &self,
266266 _ctx: &GitContext,
267267 release: &repo_release::Model,
268 actor: Option<Uuid>,
268 actor: Option<i64>,
269269 ) -> Result<Uuid, GitError> {
270270 let cfg = &self.state.config.ai;
271271 if !cfg.release_notes_on() {
@@ -329,7 +329,7 @@
329329 &self,
330330 rows: &[repo_release::Model],
331331 ) -> Result<Vec<ReleaseDto>, GitError> {
332 let uids: Vec<Uuid> = rows.iter().map(|row| row.publisher_id).collect();
332 let uids: Vec<i64> = rows.iter().map(|row| row.publisher_id).collect();
333333 let users = self.user_dto_map(&uids).await?;
334334 Ok(rows
335335 .iter()
Modified lib/git/src/service/repos.rs lib/git/src/service/repos.rs +5 −5 View lib/git/src/service/repos.rs
@@ -357,7 +357,7 @@
357357 pub async fn create_repo(
358358 &self,
359359 space_name: String,
360 caller: Uuid,
360 caller: i64,
361361 cmd: CreateRepoCmd,
362362 ) -> Result<RepoDto, GitError> {
363363 let space = self.lookup_space(&space_name).await?;
@@ -560,7 +560,7 @@
560560 self.repo_dto(ctx.model).await
561561 }
562562
563 pub async fn repo_viewer_state(&self, repo_id: Uuid, uid: Uuid) -> Result<RepoMeDto, GitError> {
563 pub async fn repo_viewer_state(&self, repo_id: Uuid, uid: i64) -> Result<RepoMeDto, GitError> {
564564 use models::repo::repo_star;
565565 use models::repo::repo_watch;
566566 let starred = repo_star::Entity::find()
@@ -591,7 +591,7 @@
591591 pub async fn list_repos(
592592 &self,
593593 space_name: String,
594 caller: Option<Uuid>,
594 caller: Option<i64>,
595595 page: export::pagination::PageQuery,
596596 ) -> Result<export::pagination::Page<RepoSummaryDto>, GitError> {
597597 let space = self.lookup_space(&space_name).await?;
@@ -650,7 +650,7 @@
650650 &self,
651651 space_name: String,
652652 repo_name: String,
653 caller: Uuid,
653 caller: i64,
654654 cmd: UpdateRepoCmd,
655655 ) -> Result<RepoDto, GitError> {
656656 let ctx = self.context(space_name, repo_name).await?;
@@ -732,7 +732,7 @@
732732 &self,
733733 space_name: String,
734734 repo_name: String,
735 caller: Uuid,
735 caller: i64,
736736 archived: bool,
737737 ) -> Result<RepoDto, GitError> {
738738 let ctx = self.context(space_name, repo_name).await?;
Modified lib/git/src/service/stars.rs lib/git/src/service/stars.rs +7 −7 View lib/git/src/service/stars.rs
@@ -15,7 +15,7 @@
1515
1616#[derive(Debug, Clone, Serialize, ToSchema)]
1717pub struct RepoStarDto {
18 pub user_id: Uuid,
18 pub user_id: i64,
1919 pub repo_id: Uuid,
2020 pub created_at: OffsetDateTime,
2121}
@@ -27,7 +27,7 @@
2727
2828#[derive(Debug, Clone, Serialize, ToSchema)]
2929pub struct StargazerDto {
30 pub user_id: Uuid,
30 pub user_id: i64,
3131 pub username: String,
3232 pub display_name: String,
3333 pub avatar_url: String,
@@ -38,7 +38,7 @@
3838 pub async fn star_repo(
3939 &self,
4040 ctx: &GitContext,
41 user_id: Uuid,
41 user_id: i64,
4242 ) -> Result<RepoStarResp, GitError> {
4343 let db = self.db_write();
4444 let repo_id = ctx.model.id;
@@ -76,7 +76,7 @@
7676 pub async fn unstar_repo(
7777 &self,
7878 ctx: &GitContext,
79 user_id: Uuid,
79 user_id: i64,
8080 ) -> Result<RepoStarResp, GitError> {
8181 let db = self.db_write();
8282 let repo_id = ctx.model.id;
@@ -107,7 +107,7 @@
107107 Ok(RepoStarResp { stars: after })
108108 }
109109
110 pub async fn is_starred(&self, ctx: &GitContext, user_id: Uuid) -> Result<bool, GitError> {
110 pub async fn is_starred(&self, ctx: &GitContext, user_id: i64) -> Result<bool, GitError> {
111111 let row = repo_star::Entity::find()
112112 .filter(repo_star::Column::UserId.eq(user_id))
113113 .filter(repo_star::Column::RepoId.eq(ctx.model.id))
@@ -132,12 +132,12 @@
132132 if rows.is_empty() {
133133 return Ok(Vec::new());
134134 }
135 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
135 let user_ids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
136136 let users = models::users::user::Entity::find()
137137 .filter(models::users::user::Column::Uid.is_in(user_ids.iter().copied()))
138138 .all(self.db_read())
139139 .await?;
140 let by_id: std::collections::HashMap<Uuid, models::users::user::Model> =
140 let by_id: std::collections::HashMap<i64, models::users::user::Model> =
141141 users.into_iter().map(|u| (u.uid, u)).collect();
142142 Ok(rows
143143 .into_iter()
Modified lib/git/src/service/watches.rs lib/git/src/service/watches.rs +7 −7 View lib/git/src/service/watches.rs
@@ -16,7 +16,7 @@
1616
1717#[derive(Debug, Clone, Serialize, ToSchema)]
1818pub struct RepoWatchDto {
19 pub user_id: Uuid,
19 pub user_id: i64,
2020 pub repo_id: Uuid,
2121 pub mode: WatchMode,
2222 pub created_at: OffsetDateTime,
@@ -30,7 +30,7 @@
3030
3131#[derive(Debug, Clone, Serialize, ToSchema)]
3232pub struct WatcherDto {
33 pub user_id: Uuid,
33 pub user_id: i64,
3434 pub username: String,
3535 pub display_name: String,
3636 pub avatar_url: String,
@@ -42,7 +42,7 @@
4242 pub async fn upsert_watch(
4343 &self,
4444 ctx: &GitContext,
45 user_id: Uuid,
45 user_id: i64,
4646 cmd: UpsertWatchCmd,
4747 ) -> Result<RepoWatchDto, GitError> {
4848 let db = self.db_write();
@@ -96,7 +96,7 @@
9696 })
9797 }
9898
99 pub async fn unwatch(&self, ctx: &GitContext, user_id: Uuid) -> Result<(), GitError> {
99 pub async fn unwatch(&self, ctx: &GitContext, user_id: i64) -> Result<(), GitError> {
100100 let db = self.db_write();
101101 let repo_id = ctx.model.id;
102102 let now = OffsetDateTime::now_utc();
@@ -129,7 +129,7 @@
129129 pub async fn get_watch_mode(
130130 &self,
131131 ctx: &GitContext,
132 user_id: Uuid,
132 user_id: i64,
133133 ) -> Result<Option<WatchMode>, GitError> {
134134 let row = repo_watch::Entity::find()
135135 .filter(repo_watch::Column::UserId.eq(user_id))
@@ -158,12 +158,12 @@
158158 if rows.is_empty() {
159159 return Ok(Vec::new());
160160 }
161 let user_ids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
161 let user_ids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
162162 let users = models::users::user::Entity::find()
163163 .filter(models::users::user::Column::Uid.is_in(user_ids.iter().copied()))
164164 .all(self.db_read())
165165 .await?;
166 let by_id: std::collections::HashMap<Uuid, models::users::user::Model> =
166 let by_id: std::collections::HashMap<i64, models::users::user::Model> =
167167 users.into_iter().map(|u| (u.uid, u)).collect();
168168 Ok(rows
169169 .into_iter()
Modified lib/git/src/service/whitelists.rs lib/git/src/service/whitelists.rs +9 −9 View lib/git/src/service/whitelists.rs
@@ -16,7 +16,7 @@
1616 pub id: Uuid,
1717 pub policy_id: Uuid,
1818 pub kind: WhitelistKind,
19 pub user_id: Option<Uuid>,
19 pub user_id: Option<i64>,
2020 pub user: Option<space::services::spaces::UserDto>,
2121 pub group_id: Option<Uuid>,
2222 pub created_at: OffsetDateTime,
@@ -39,14 +39,14 @@
3939#[derive(Debug, Clone, Deserialize, ToSchema)]
4040pub struct AddBranchWhitelistCmd {
4141 pub kind: WhitelistKind,
42 pub user_id: Option<Uuid>,
42 pub user_id: Option<i64>,
4343 pub group_id: Option<Uuid>,
4444}
4545
4646#[derive(Debug, Clone, Serialize, ToSchema)]
4747pub struct TagWhitelistDto {
4848 pub policy_id: Uuid,
49 pub user_id: Uuid,
49 pub user_id: i64,
5050 pub user: Option<space::services::spaces::UserDto>,
5151}
5252
@@ -62,7 +62,7 @@
6262
6363#[derive(Debug, Clone, Deserialize, ToSchema)]
6464pub struct AddTagWhitelistCmd {
65 pub user_id: Uuid,
65 pub user_id: i64,
6666}
6767
6868impl GitService {
@@ -70,7 +70,7 @@
7070 &self,
7171 mut rows: Vec<BranchWhitelistDto>,
7272 ) -> Result<Vec<BranchWhitelistDto>, GitError> {
73 let uids: Vec<Uuid> = rows.iter().filter_map(|r| r.user_id).collect();
73 let uids: Vec<i64> = rows.iter().filter_map(|r| r.user_id).collect();
7474 if !uids.is_empty() {
7575 let map = self.user_dto_map(&uids).await?;
7676 for row in rows.iter_mut() {
@@ -82,8 +82,8 @@
8282
8383 pub(crate) async fn user_dto_map(
8484 &self,
85 uids: &[Uuid],
86 ) -> Result<std::collections::HashMap<Uuid, space::services::spaces::UserDto>, GitError> {
85 uids: &[i64],
86 ) -> Result<std::collections::HashMap<i64, space::services::spaces::UserDto>, GitError> {
8787 use models::users::user;
8888 let mut map = std::collections::HashMap::new();
8989 if uids.is_empty() {
@@ -242,7 +242,7 @@
242242 &self,
243243 repo_id: Uuid,
244244 policy_id: Uuid,
245 user_id: Uuid,
245 user_id: i64,
246246 ) -> Result<(), GitError> {
247247 let policy = self.ensure_tag_policy(policy_id).await?;
248248 if policy.repo_id != repo_id {
@@ -268,7 +268,7 @@
268268 .all(self.db_read())
269269 .await?;
270270 let mut out: Vec<TagWhitelistDto> = rows.into_iter().map(Into::into).collect();
271 let uids: Vec<Uuid> = out.iter().map(|t| t.user_id).collect();
271 let uids: Vec<i64> = out.iter().map(|t| t.user_id).collect();
272272 let map = self.user_dto_map(&uids).await?;
273273 for t in out.iter_mut() {
274274 t.user = map.get(&t.user_id).cloned();
Modified lib/git/src/share/access.rs lib/git/src/share/access.rs +29 −58 View lib/git/src/share/access.rs
@@ -2,8 +2,7 @@
22use axum::http::header;
33use base64::Engine as _;
44use models::repo::repo_collaborations::{self, CollaborationMode};
5use models::users::user;
6use models::users::user_access_keys::{self, AccessKeyScope};
5use models::users::access_scope::AccessKeyScope;
76use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
87use sha2::{Digest, Sha256};
98use uuid::Uuid;
@@ -46,57 +45,29 @@
4645pub async fn verify_access_key(
4746 svc: &GitService,
4847 token: &str,
49) -> Result<Option<(Uuid, AccessKeyScope)>, GitError> {
50 let Some((key_id, secret)) = token.split_once('.') else {
51 return Ok(None);
52 };
53 let Ok(key_id) = Uuid::parse_str(key_id) else {
54 return Ok(None);
55 };
56 let db = svc.state.database.clone();
57 let Some(row) = user_access_keys::Entity::find_by_id(key_id)
58 .one(db.read())
59 .await?
60 else {
61 return Ok(None);
62 };
63 let Some(caller) = user::Entity::find_by_id(row.user_id).one(db.read()).await? else {
64 return Ok(None);
48) -> Result<Option<(i64, AccessKeyScope)>, GitError> {
49 let Some(client) = svc.state.kenauth.as_ref() else {
50 return Err(GitError::Internal(
51 "kenoauth is not configured for access key verification".into(),
52 ));
6553 };
66 if !caller.is_active || caller.prohibit_login || caller.is_restricted {
67 return Ok(None);
68 }
69 let computed = sha256_hex(format!("{secret}{}", row.token_salt).as_bytes());
70 if !constant_time_eq(computed.as_bytes(), row.token_hash.as_bytes()) {
71 return Ok(None);
72 }
73 let now = time::OffsetDateTime::now_utc();
74 if let Some(expires_at) = row.expires_at
75 && expires_at <= now
76 {
77 return Ok(None);
78 }
79 let stale = row
80 .last_used_at
81 .map(|t| (now - t).whole_minutes() >= 1)
82 .unwrap_or(true);
83 if stale {
84 let update = user_access_keys::Entity::update_many()
85 .col_expr(
86 user_access_keys::Column::LastUsedAt,
87 sea_orm::sea_query::Expr::value(now),
88 )
89 .filter(user_access_keys::Column::Id.eq(row.id))
90 .filter(
91 sea_orm::Condition::any()
92 .add(user_access_keys::Column::LastUsedAt.is_null())
93 .add(user_access_keys::Column::LastUsedAt.lt(now - time::Duration::minutes(1))),
94 )
95 .exec(db.write())
96 .await;
97 let _ = update;
54 match client.verify_access_key(token, Vec::new()).await {
55 Ok(Some(principal)) => {
56 let write = principal.scopes.iter().any(|scope| *scope == 12);
57 Ok(Some((
58 principal.user_id,
59 if write {
60 AccessKeyScope::Write
61 } else {
62 AccessKeyScope::Read
63 },
64 )))
65 }
66 Ok(None) => Ok(None),
67 Err(error) => Err(GitError::Internal(format!(
68 "access key verification failed: {error}"
69 ))),
9870 }
99 Ok(Some((row.user_id, row.scope)))
10071}
10172
10273/// HTTP write-path auth: token must be valid AND the resolved `uid` must
@@ -106,7 +77,7 @@
10677 svc: &GitService,
10778 auth: &BasicAuth,
10879 ctx: &GitContext,
109) -> Result<Uuid, GitError> {
80) -> Result<i64, GitError> {
11081 if let Some(token) = auth
11182 .token
11283 .as_deref()
@@ -136,7 +107,7 @@
136107 svc: &GitService,
137108 token: &str,
138109 ctx: &GitContext,
139) -> Result<Option<Uuid>, GitError> {
110) -> Result<Option<i64>, GitError> {
140111 if !export::space_token::looks_like(token) {
141112 return Ok(None);
142113 }
@@ -161,7 +132,7 @@
161132 token: &str,
162133 ctx: &GitContext,
163134 write: bool,
164) -> Result<Uuid, GitError> {
135) -> Result<i64, GitError> {
165136 let (id, secret) = token
166137 .strip_prefix("ci_")
167138 .and_then(|value| value.split_once('.'))
@@ -200,7 +171,7 @@
200171 svc: &GitService,
201172 auth: &BasicAuth,
202173 ctx: &GitContext,
203) -> Result<Option<Uuid>, GitError> {
174) -> Result<Option<i64>, GitError> {
204175 if let Some(token) = auth
205176 .token
206177 .as_deref()
@@ -235,7 +206,7 @@
235206 svc: &GitService,
236207 auth: &BasicAuth,
237208 ctx: &GitContext,
238) -> Result<Uuid, GitError> {
209) -> Result<i64, GitError> {
239210 if let Some(token) = auth
240211 .token
241212 .as_deref()
@@ -257,7 +228,7 @@
257228 Ok(uid)
258229}
259230
260async fn resolve_caller_for_write(svc: &GitService, auth: &BasicAuth) -> Result<Uuid, GitError> {
231async fn resolve_caller_for_write(svc: &GitService, auth: &BasicAuth) -> Result<i64, GitError> {
261232 let Some(token) = &auth.token else {
262233 return Err(GitError::Unauthorized);
263234 };
@@ -283,7 +254,7 @@
283254/// - admin: requires `repo_collaborations.mode == Admin`
284255pub async fn authorize_repo_access(
285256 svc: &GitService,
286 uid: Uuid,
257 uid: i64,
287258 ctx: &GitContext,
288259 want_write: bool,
289260 require_admin: bool,
Modified lib/git/src/share/identity.rs lib/git/src/share/identity.rs +1 −1 View lib/git/src/share/identity.rs
@@ -11,7 +11,7 @@
1111 pub email: String,
1212}
1313
14pub async fn load_identity(db: &export::AppDatabase, uid: Uuid) -> Result<GitIdentity, GitError> {
14pub async fn load_identity(db: &export::AppDatabase, uid: i64) -> Result<GitIdentity, GitError> {
1515 let u = user::Entity::find_by_id(uid)
1616 .one(db.read())
1717 .await?
Modified lib/git/src/share/policy.rs lib/git/src/share/policy.rs +5 −5 View lib/git/src/share/policy.rs
@@ -58,16 +58,16 @@
5858
5959#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6060pub enum WhitelistSubject {
61 User(Uuid),
61 User(i64),
6262 #[allow(dead_code)]
6363 Group(Uuid),
6464}
6565
6666#[derive(Debug, Default)]
6767pub struct WhitelistSet {
68 pub push_allowed: Vec<Uuid>,
69 pub merge_allowed: Vec<Uuid>,
70 pub approval_allowed: Vec<Uuid>,
68 pub push_allowed: Vec<i64>,
69 pub merge_allowed: Vec<i64>,
70 pub approval_allowed: Vec<i64>,
7171}
7272
7373impl WhitelistSet {
@@ -78,7 +78,7 @@
7878 }
7979 }
8080
81 pub fn tag_modifier_allowed(&self, uid: Uuid) -> bool {
81 pub fn tag_modifier_allowed(&self, uid: i64) -> bool {
8282 self.merge_allowed.contains(&uid) || self.approval_allowed.contains(&uid)
8383 }
8484}
Modified lib/git/src/smart/dispatch.rs lib/git/src/smart/dispatch.rs +2 −2 View lib/git/src/smart/dispatch.rs
@@ -167,7 +167,7 @@
167167 ctx: &crate::context::facade::GitContext,
168168 auth: &BasicAuth,
169169 want_write: bool,
170) -> SmartResult<Option<Uuid>> {
170) -> SmartResult<Option<i64>> {
171171 let svc = &service.git;
172172 if want_write {
173173 let uid = require_writer(svc, auth, ctx).await?;
@@ -188,7 +188,7 @@
188188 pub ci_topic: Option<String>,
189189 pub space_id: Uuid,
190190 pub repo_id: Uuid,
191 pub actor_id: Option<Uuid>,
191 pub actor_id: Option<i64>,
192192 pub bare: std::path::PathBuf,
193193 pub ai_ctx: Option<crate::sync::post_receive::AiPushCtx>,
194194}
Modified lib/git/src/smart/pre_receive.rs lib/git/src/smart/pre_receive.rs +2 −2 View lib/git/src/smart/pre_receive.rs
@@ -39,7 +39,7 @@
3939/// Who is pushing and how they are privileged on this repository.
4040#[derive(Debug, Clone, Copy)]
4141struct PushActor {
42 user_id: Uuid,
42 user_id: i64,
4343 is_admin: bool,
4444}
4545
@@ -60,7 +60,7 @@
6060 db: &sea_orm::DatabaseConnection,
6161 baby: &gitbaby::GitBaby,
6262 repo_id: Uuid,
63 actor_user_id: Uuid,
63 actor_user_id: i64,
6464 refs: &[RefUpdate],
6565) -> SmartResult<PreReceiveVerdict> {
6666 tracing::debug!(
Modified lib/git/src/sync/ai_language.rs lib/git/src/sync/ai_language.rs +1 −1 View lib/git/src/sync/ai_language.rs
@@ -5,7 +5,7 @@
55///
66/// 生产侧在入队时把语言写进任务,消费侧便无需再查用户表;查不到时返回 `None`,
77/// 由 aihost 回退到 `[ai].output_language`。
8pub async fn actor_language(db: &DatabaseConnection, actor_id: Option<Uuid>) -> Option<String> {
8pub async fn actor_language(db: &DatabaseConnection, actor_id: Option<i64>) -> Option<String> {
99 use models::users::user;
1010 let uid = actor_id?;
1111 let row = user::Entity::find_by_id(uid).one(db).await.ok()??;
Modified lib/git/src/sync/ai_task.rs lib/git/src/sync/ai_task.rs +4 −4 View lib/git/src/sync/ai_task.rs
@@ -103,7 +103,7 @@
103103 redis: &AppRedis,
104104 cfg: &AiConfig,
105105 repo_id: Uuid,
106 actor_id: Option<Uuid>,
106 actor_id: Option<i64>,
107107 refs: &[(String, String, String)],
108108) {
109109 let conn = db.write();
@@ -194,7 +194,7 @@
194194 redis: &AppRedis,
195195 cfg: &AiConfig,
196196 repo_id: Uuid,
197 actor_id: Option<Uuid>,
197 actor_id: Option<i64>,
198198 language: Option<String>,
199199 tag_name: &str,
200200 target_sha: &str,
@@ -379,7 +379,7 @@
379379}
380380
381381/// 草稿发布者的兜底:无推送者(如系统同步)时取空间所有者。
382async fn repo_publisher_fallback(db: &DatabaseConnection, repo_id: Uuid) -> SyncResult<Uuid> {
382async fn repo_publisher_fallback(db: &DatabaseConnection, repo_id: Uuid) -> SyncResult<i64> {
383383 let repo = repos::Entity::find_by_id(repo_id)
384384 .one(db)
385385 .await?
@@ -392,7 +392,7 @@
392392}
393393
394394/// 入队时的语言解析:调用方已解析过就直接用,避免重复查用户表。
395async fn resolve_language_for(db: &DatabaseConnection, actor_id: Option<Uuid>) -> Option<String> {
395async fn resolve_language_for(db: &DatabaseConnection, actor_id: Option<i64>) -> Option<String> {
396396 crate::sync::ai_language::actor_language(db, actor_id).await
397397}
398398
Modified lib/git/src/sync/commits.rs lib/git/src/sync/commits.rs +2 −2 View lib/git/src/sync/commits.rs
@@ -156,7 +156,7 @@
156156async fn resolve_user_ids(
157157 txn: &DatabaseTransaction,
158158 commits: &[CommitData],
159) -> SyncResult<HashMap<String, Uuid>> {
159) -> SyncResult<HashMap<String, i64>> {
160160 let mut lower_emails: HashSet<String> = HashSet::new();
161161 for c in commits {
162162 lower_emails.insert(c.author_email.to_lowercase());
@@ -174,7 +174,7 @@
174174 .await
175175 .map_err(SyncError::Db)?;
176176
177 let mut map: HashMap<String, Uuid> = rows
177 let mut map: HashMap<String, i64> = rows
178178 .into_iter()
179179 .map(|r| (r.lower_email, r.user_id))
180180 .collect();
Modified lib/git/src/sync/event.rs lib/git/src/sync/event.rs +2 −2 View lib/git/src/sync/event.rs
@@ -45,7 +45,7 @@
4545 pub id: Uuid,
4646 pub repo_id: Uuid,
4747 pub channel_id: Uuid,
48 pub actor_id: Option<Uuid>,
48 pub actor_id: Option<i64>,
4949 pub refs: Vec<PostReceiveRefUpdate>,
5050 #[serde(default)]
5151 pub trace: Option<String>,
@@ -58,7 +58,7 @@
5858 pub fn new(
5959 repo_id: Uuid,
6060 channel_id: Uuid,
61 actor_id: Option<Uuid>,
61 actor_id: Option<i64>,
6262 refs: Vec<PostReceiveRefUpdate>,
6363 ) -> Self {
6464 Self {
Modified lib/git/src/sync/post_receive.rs lib/git/src/sync/post_receive.rs +1 −1 View lib/git/src/sync/post_receive.rs
@@ -24,7 +24,7 @@
2424pub struct PushTarget<'a> {
2525 pub space_id: Uuid,
2626 pub repo_id: Uuid,
27 pub actor_id: Option<Uuid>,
27 pub actor_id: Option<i64>,
2828 pub bare: &'a Path,
2929}
3030
Modified lib/git/src/sync/pulls.rs lib/git/src/sync/pulls.rs +2 −2 View lib/git/src/sync/pulls.rs
@@ -51,7 +51,7 @@
5151 branch: &str,
5252 new_oid: &str,
5353 deleted: bool,
54 actor: Option<uuid::Uuid>,
54 actor: Option<i64>,
5555) -> SyncResult<()> {
5656 let open = pull_requests::Entity::find()
5757 .filter(pull_requests::Column::RepoId.eq(repo.id))
@@ -87,7 +87,7 @@
8787async fn record_event(
8888 db: &AppDatabase,
8989 pull: &pull_requests::Model,
90 actor: Option<uuid::Uuid>,
90 actor: Option<i64>,
9191 event_type: &str,
9292 commit_sha: Option<&str>,
9393) -> SyncResult<()> {
Modified lib/git/Cargo.toml lib/git/Cargo.toml +1 −2 View lib/git/Cargo.toml
@@ -31,11 +31,10 @@
3131redis = { workspace = true }
3232tracing = { workspace = true }
3333russh = { workspace = true }
34ssh-key = { workspace = true }
3534flate2 = { workspace = true }
3635rand = { workspace = true }
3736bytes = { workspace = true }
38session-layer = { workspace = true }
37jwt = { workspace = true }
3938search = { workspace = true }
4039utoipa = { workspace = true }
4140maud = { workspace = true }
Modified lib/i18n/src/locale.rs lib/i18n/src/locale.rs +3 −4 View lib/i18n/src/locale.rs
@@ -1,7 +1,6 @@
11use std::str::FromStr;
22
33use sea_orm::{ConnectionTrait, EntityTrait};
4use uuid::Uuid;
54
65/// A locale with a shipped server-side catalogue.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -118,7 +117,7 @@
118117/// pass the result directly to a template.
119118pub async fn resolve_user_locale<C: ConnectionTrait>(
120119 db: &C,
121 uid: Uuid,
120 uid: i64,
122121 fallback: UiLocale,
123122) -> UiLocale {
124123 use models::users::user;
@@ -140,9 +139,9 @@
140139/// must not issue one query per recipient.
141140pub async fn resolve_user_locales<C: ConnectionTrait>(
142141 db: &C,
143 uids: &[Uuid],
142 uids: &[i64],
144143 fallback: UiLocale,
145) -> std::collections::HashMap<Uuid, UiLocale> {
144) -> std::collections::HashMap<i64, UiLocale> {
146145 use models::users::user;
147146 use sea_orm::{ColumnTrait, QueryFilter};
148147 let mut out = std::collections::HashMap::new();
Modified lib/import/src/handlers/cancel_import.rs lib/import/src/handlers/cancel_import.rs +1 −1 View lib/import/src/handlers/cancel_import.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::ApiError;
66use crate::handlers::caller;
Modified lib/import/src/handlers/create_import.rs lib/import/src/handlers/create_import.rs +1 −1 View lib/import/src/handlers/create_import.rs
@@ -1,7 +1,7 @@
11use axum::Json;
22use axum::extract::{Path, State};
33use export::rest::AppResponseData;
4use session_layer::Session;
4use jwt::Session;
55
66use crate::errors::ApiError;
77use crate::handlers::caller;
Modified lib/import/src/handlers/get_import.rs lib/import/src/handlers/get_import.rs +1 −1 View lib/import/src/handlers/get_import.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::ApiError;
66use crate::handlers::caller;
Modified lib/import/src/handlers/list_imports.rs lib/import/src/handlers/list_imports.rs +1 −1 View lib/import/src/handlers/list_imports.rs
@@ -1,6 +1,6 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
3use session_layer::Session;
3use jwt::Session;
44
55use crate::errors::ApiError;
66use crate::handlers::caller;
Modified lib/import/src/handlers/mod.rs lib/import/src/handlers/mod.rs +2 −2 View lib/import/src/handlers/mod.rs
@@ -6,11 +6,11 @@
66pub mod requeue_imports;
77pub mod retry_import;
88
9use session_layer::{Session, SessionUser};
9use jwt::{Session, SessionUser};
1010use uuid::Uuid;
1111
1212use crate::errors::ApiError;
1313
14pub fn caller(session: &Session) -> Result<Uuid, ApiError> {
14pub fn caller(session: &Session) -> Result<i64, ApiError> {
1515 session.uid().ok_or_else(ApiError::unauthorized)
1616}
Modified lib/import/src/handlers/probe_import.rs lib/import/src/handlers/probe_import.rs +1 −1 View lib/import/src/handlers/probe_import.rs
@@ -1,7 +1,7 @@
11use axum::Json;
22use axum::extract::{Path, State};
33use export::rest::AppResponseData;
4use session_layer::Session;
4use jwt::Session;
55
66use crate::errors::ApiError;
77use crate::handlers::caller;
Modified lib/import/src/handlers/retry_import.rs lib/import/src/handlers/retry_import.rs +1 −1 View lib/import/src/handlers/retry_import.rs
@@ -1,7 +1,7 @@
11use axum::Json;
22use axum::extract::{Path, State};
33use export::rest::AppResponseData;
4use session_layer::Session;
4use jwt::Session;
55
66use crate::errors::ApiError;
77use crate::handlers::caller;
Modified lib/import/src/share/guard.rs lib/import/src/share/guard.rs +2 −2 View lib/import/src/share/guard.rs
@@ -1,8 +1,8 @@
11use axum::extract::{Request, State};
22use axum::middleware::Next;
33use axum::response::Response;
4use session_layer::Session;
5use session_layer::share::guard::session_guard;
4use jwt::Session;
5use jwt::share::guard::session_guard;
66
77use crate::service::ImportService;
88
Modified lib/import/src/identity.rs lib/import/src/identity.rs +4 −4 View lib/import/src/identity.rs
@@ -87,7 +87,7 @@
8787 });
8888 }
8989
90 let mut resolved: HashMap<String, Uuid> = HashMap::new();
90 let mut resolved: HashMap<String, i64> = HashMap::new();
9191 for candidate in candidates {
9292 if let Some((email, uid)) = upsert(db, provider, &candidate).await? {
9393 resolved.insert(email, uid);
@@ -101,7 +101,7 @@
101101 db: &DatabaseConnection,
102102 candidate: &Candidate,
103103 lower_login: &str,
104) -> Result<Option<Uuid>, ImportError> {
104) -> Result<Option<i64>, ImportError> {
105105 if let Some(email) = candidate
106106 .email
107107 .as_deref()
@@ -127,7 +127,7 @@
127127 db: &DatabaseConnection,
128128 provider: ImportProvider,
129129 candidate: &Candidate,
130) -> Result<Option<(String, Uuid)>, ImportError> {
130) -> Result<Option<(String, i64)>, ImportError> {
131131 let login = candidate.login.trim();
132132 if login.is_empty() {
133133 return Ok(None);
@@ -202,7 +202,7 @@
202202async fn backfill_commit_authors(
203203 db: &DatabaseConnection,
204204 repo_id: Uuid,
205 resolved: &HashMap<String, Uuid>,
205 resolved: &HashMap<String, i64>,
206206) -> Result<(), ImportError> {
207207 for (email, uid) in resolved {
208208 git_commits::Entity::update_many()
Modified lib/import/src/service.rs lib/import/src/service.rs +13 −27 View lib/import/src/service.rs
@@ -156,7 +156,7 @@
156156 pub attempts: i32,
157157 pub last_error: Option<String>,
158158 pub stats: ImportStatsDto,
159 pub created_by: Uuid,
159 pub created_by: i64,
160160 pub created_at: OffsetDateTime,
161161 pub updated_at: OffsetDateTime,
162162 pub started_at: Option<OffsetDateTime>,
@@ -254,10 +254,6 @@
254254 self.state.database.write()
255255 }
256256
257 fn is_platform_admin(&self, caller: Uuid) -> bool {
258 self.config().apihost.is_platform_admin(&caller.to_string())
259 }
260
261257 pub fn dto(&self, row: &job::Model, space_name: &str) -> ImportJobDto {
262258 ImportJobDto {
263259 id: row.id,
@@ -295,8 +291,8 @@
295291 .ok_or(ImportError::NotFound)
296292 }
297293
298 async fn require_member(&self, space: &spaces::Model, caller: Uuid) -> Result<(), ImportError> {
299 if space.owner_id == caller || self.is_platform_admin(caller) {
294 async fn require_member(&self, space: &spaces::Model, caller: i64) -> Result<(), ImportError> {
295 if space.owner_id == caller {
300296 return Ok(());
301297 }
302298 let member = space_members::Entity::find_by_id((space.id, caller))
@@ -337,7 +333,7 @@
337333
338334 pub async fn probe(
339335 &self,
340 caller: Uuid,
336 caller: i64,
341337 space_name: &str,
342338 cmd: ProbeImportCmd,
343339 ) -> Result<ImportProbeDto, ImportError> {
@@ -357,7 +353,7 @@
357353
358354 pub async fn create(
359355 &self,
360 caller: Uuid,
356 caller: i64,
361357 space_name: &str,
362358 cmd: CreateImportCmd,
363359 ) -> Result<ImportJobDto, ImportError> {
@@ -366,7 +362,7 @@
366362 return Err(ImportError::Disabled);
367363 }
368364 let space = self.lookup_space(space_name).await?;
369 if space.owner_id != caller && !self.is_platform_admin(caller) {
365 if space.owner_id != caller {
370366 return Err(ImportError::Forbidden);
371367 }
372368 let info = self
@@ -472,7 +468,7 @@
472468
473469 pub async fn list(
474470 &self,
475 caller: Uuid,
471 caller: i64,
476472 space_name: &str,
477473 query: ListImportQuery,
478474 ) -> Result<ImportJobPage, ImportError> {
@@ -481,7 +477,7 @@
481477 let limit = query.limit.unwrap_or(20).clamp(1, 100);
482478 let offset = query.offset.unwrap_or(0);
483479 let mut select = job::Entity::find().filter(job::Column::SpaceId.eq(space.id));
484 if space.owner_id != caller && !self.is_platform_admin(caller) {
480 if space.owner_id != caller {
485481 select = select.filter(job::Column::CreatedBy.eq(caller));
486482 }
487483 if let Some(state) = query
@@ -518,7 +514,7 @@
518514
519515 pub async fn load_job(
520516 &self,
521 caller: Uuid,
517 caller: i64,
522518 space_name: &str,
523519 job_id: Uuid,
524520 ) -> Result<(spaces::Model, job::Model), ImportError> {
@@ -529,7 +525,7 @@
529525 .one(self.db_read())
530526 .await?
531527 .ok_or(ImportError::NotFound)?;
532 if row.created_by != caller && space.owner_id != caller && !self.is_platform_admin(caller) {
528 if row.created_by != caller && space.owner_id != caller {
533529 return Err(ImportError::Forbidden);
534530 }
535531 Ok((space, row))
@@ -537,7 +533,7 @@
537533
538534 pub async fn get(
539535 &self,
540 caller: Uuid,
536 caller: i64,
541537 space_name: &str,
542538 job_id: Uuid,
543539 ) -> Result<ImportJobDto, ImportError> {
@@ -597,7 +593,7 @@
597593
598594 pub async fn cancel(
599595 &self,
600 caller: Uuid,
596 caller: i64,
601597 space_name: &str,
602598 job_id: Uuid,
603599 ) -> Result<ImportJobDto, ImportError> {
@@ -626,7 +622,7 @@
626622
627623 pub async fn retry(
628624 &self,
629 caller: Uuid,
625 caller: i64,
630626 space_name: &str,
631627 job_id: Uuid,
632628 cmd: RetryImportCmd,
@@ -677,14 +673,4 @@
677673pub fn decrypt_token(secret: &str, stored: &str) -> Result<String, ImportError> {
678674 auth::share::crypto::aes_decrypt(secret, stored)
679675 .map_err(|e| ImportError::Internal(format!("token decryption failed: {e}")))
680}
681
682#[async_trait::async_trait]
683impl session_layer::SessionGuard for ImportService {
684 async fn lookup_session_with_user(
685 &self,
686 token_hash: &str,
687 ) -> Result<Option<session_layer::SessionWithUser>, session_layer::SessionError> {
688 session_layer::lookup_session_with_user(self.db_read(), token_hash).await
689 }
690676}
Modified lib/import/Cargo.toml lib/import/Cargo.toml +1 −1 View lib/import/Cargo.toml
@@ -16,7 +16,7 @@
1616sea-orm = { workspace = true }
1717serde = { workspace = true, features = ["derive"] }
1818serde_json = { workspace = true }
19session-layer = { workspace = true }
19jwt = { workspace = true }
2020thiserror = { workspace = true }
2121time = { workspace = true }
2222tokio = { workspace = true, features = ["fs", "process", "time", "sync", "rt", "macros"] }
Modified lib/issues/src/services/attachments.rs lib/issues/src/services/attachments.rs +7 −7 View lib/issues/src/services/attachments.rs
@@ -78,7 +78,7 @@
7878 pub id: Uuid,
7979 /// `None` until the draft is submitted with this attachment attached.
8080 pub issue_id: Option<Uuid>,
81 pub uploader_id: Uuid,
81 pub uploader_id: i64,
8282 pub filename: String,
8383 pub content_type: String,
8484 pub size: i64,
@@ -121,7 +121,7 @@
121121 #[tracing::instrument(name = "issues.create_issue_attachment", skip_all, fields(caller = %caller))]
122122 pub async fn create_issue_attachment(
123123 &self,
124 caller: Uuid,
124 caller: i64,
125125 cmd: CreateIssueAttachmentCmd,
126126 ) -> Result<IssueUploadIntentDto, IssueError> {
127127 let max_size = self.config().issues.attachment.max_size;
@@ -204,7 +204,7 @@
204204 #[tracing::instrument(name = "issues.finalize_issue_attachment", skip_all, fields(caller = %caller, attachment_id = %attachment_id))]
205205 pub async fn finalize_issue_attachment(
206206 &self,
207 caller: Uuid,
207 caller: i64,
208208 attachment_id: Uuid,
209209 ) -> Result<IssueAttachmentDto, IssueError> {
210210 let row = issue_attachment::Entity::find_by_id(attachment_id)
@@ -258,7 +258,7 @@
258258 #[tracing::instrument(name = "issues.delete_issue_attachment", skip_all, fields(caller = %caller, attachment_id = %attachment_id))]
259259 pub async fn delete_issue_attachment(
260260 &self,
261 caller: Uuid,
261 caller: i64,
262262 attachment_id: Uuid,
263263 ) -> Result<(), IssueError> {
264264 let row = issue_attachment::Entity::find_by_id(attachment_id)
@@ -282,7 +282,7 @@
282282
283283 pub async fn list_issue_attachments(
284284 &self,
285 caller: Uuid,
285 caller: i64,
286286 space_name: &str,
287287 board_id: Uuid,
288288 issue_ref: &str,
@@ -303,7 +303,7 @@
303303 /// who can read the issue the file is attached to.
304304 pub async fn download_issue_attachment(
305305 &self,
306 caller: Uuid,
306 caller: i64,
307307 attachment_id: Uuid,
308308 ) -> Result<(issue_attachment::Model, Body, String), IssueError> {
309309 let row = issue_attachment::Entity::find_by_id(attachment_id)
@@ -362,7 +362,7 @@
362362 /// the create transaction so a rejected attachment rolls the issue back.
363363 pub(crate) async fn link_issue_attachments(
364364 txn: &impl sea_orm::ConnectionTrait,
365 caller: Uuid,
365 caller: i64,
366366 issue_id: Uuid,
367367 attachment_ids: &[Uuid],
368368 now: OffsetDateTime,
Modified lib/issues/src/services/boards.rs lib/issues/src/services/boards.rs +5 −5 View lib/issues/src/services/boards.rs
@@ -195,7 +195,7 @@
195195 #[tracing::instrument(name = "issues.create_board", skip_all, fields(caller = %caller, space = %space_name))]
196196 pub async fn create_board(
197197 &self,
198 caller: Uuid,
198 caller: i64,
199199 space_name: &str,
200200 cmd: CreateBoardCmd,
201201 ) -> Result<BoardDto, IssueError> {
@@ -299,7 +299,7 @@
299299
300300 pub async fn list_boards(
301301 &self,
302 caller: Uuid,
302 caller: i64,
303303 space_name: &str,
304304 q: ListBoardQuery,
305305 ) -> Result<BoardPage, IssueError> {
@@ -381,7 +381,7 @@
381381
382382 pub async fn get_board(
383383 &self,
384 caller: Uuid,
384 caller: i64,
385385 space_name: &str,
386386 board_id: Uuid,
387387 ) -> Result<BoardDto, IssueError> {
@@ -392,7 +392,7 @@
392392 #[tracing::instrument(name = "issues.update_board", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
393393 pub async fn update_board(
394394 &self,
395 caller: Uuid,
395 caller: i64,
396396 space_name: &str,
397397 board_id: Uuid,
398398 cmd: UpdateBoardCmd,
@@ -466,7 +466,7 @@
466466 #[tracing::instrument(name = "issues.delete_board", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
467467 pub async fn delete_board(
468468 &self,
469 caller: Uuid,
469 caller: i64,
470470 space_name: &str,
471471 board_id: Uuid,
472472 ) -> Result<(), IssueError> {
Modified lib/issues/src/services/comments.rs lib/issues/src/services/comments.rs +9 −9 View lib/issues/src/services/comments.rs
@@ -72,7 +72,7 @@
7272 #[tracing::instrument(name = "issues.create_comment", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
7373 pub async fn create_comment(
7474 &self,
75 caller: Uuid,
75 caller: i64,
7676 space_name: &str,
7777 board_id: Uuid,
7878 issue_id: Uuid,
@@ -102,7 +102,7 @@
102102 ));
103103 }
104104
105 let mut parent_poster_id: Option<Uuid> = None;
105 let mut parent_poster_id: Option<i64> = None;
106106 if let Some(pid) = cmd.parent_id {
107107 let parent_comment = comment::Entity::find_by_id(pid)
108108 .one(self.db().read())
@@ -296,7 +296,7 @@
296296
297297 pub async fn list_comments(
298298 &self,
299 caller: Uuid,
299 caller: i64,
300300 space_name: &str,
301301 board_id: Uuid,
302302 issue_id: Uuid,
@@ -326,7 +326,7 @@
326326 .all(self.db().read())
327327 .await?;
328328
329 let poster_ids: Vec<Uuid> = rows.iter().map(|c| c.poster_id).collect();
329 let poster_ids: Vec<i64> = rows.iter().map(|c| c.poster_id).collect();
330330 let user_map = self.load_user_briefs(&poster_ids).await?;
331331
332332 let items = rows
@@ -355,7 +355,7 @@
355355
356356 pub async fn list_comments_tree(
357357 &self,
358 caller: Uuid,
358 caller: i64,
359359 space_name: &str,
360360 board_id: Uuid,
361361 issue_id: Uuid,
@@ -368,7 +368,7 @@
368368 .all(self.db().read())
369369 .await?;
370370
371 let poster_ids: Vec<Uuid> = rows.iter().map(|c| c.poster_id).collect();
371 let poster_ids: Vec<i64> = rows.iter().map(|c| c.poster_id).collect();
372372 let user_map = self.load_user_briefs(&poster_ids).await?;
373373
374374 let dtos: Vec<CommentDto> = rows
@@ -412,7 +412,7 @@
412412
413413 pub async fn get_comment(
414414 &self,
415 caller: Uuid,
415 caller: i64,
416416 space_name: &str,
417417 board_id: Uuid,
418418 issue_id: Uuid,
@@ -443,7 +443,7 @@
443443 #[tracing::instrument(name = "issues.update_comment", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, comment_id = %comment_id))]
444444 pub async fn update_comment(
445445 &self,
446 caller: Uuid,
446 caller: i64,
447447 space_name: &str,
448448 board_id: Uuid,
449449 issue_id: Uuid,
@@ -535,7 +535,7 @@
535535 #[tracing::instrument(name = "issues.delete_comment", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, comment_id = %comment_id))]
536536 pub async fn delete_comment(
537537 &self,
538 caller: Uuid,
538 caller: i64,
539539 space_name: &str,
540540 board_id: Uuid,
541541 issue_id: Uuid,
Modified lib/issues/src/services/content_history.rs lib/issues/src/services/content_history.rs +2 −2 View lib/issues/src/services/content_history.rs
@@ -41,7 +41,7 @@
4141impl IssueService {
4242 pub async fn list_content_history(
4343 &self,
44 caller: Uuid,
44 caller: i64,
4545 space_name: &str,
4646 board_id: Uuid,
4747 issue_id: Uuid,
@@ -67,7 +67,7 @@
6767 .all(self.db().read())
6868 .await?;
6969
70 let poster_ids: Vec<Uuid> = rows.iter().map(|h| h.poster_id).collect();
70 let poster_ids: Vec<i64> = rows.iter().map(|h| h.poster_id).collect();
7171 let user_map = self.load_user_briefs(&poster_ids).await?;
7272
7373 let items = rows
Modified lib/issues/src/services/dependencies.rs lib/issues/src/services/dependencies.rs +4 −4 View lib/issues/src/services/dependencies.rs
@@ -50,7 +50,7 @@
5050 #[tracing::instrument(name = "issues.create_dependency", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
5151 pub async fn create_dependency(
5252 &self,
53 caller: Uuid,
53 caller: i64,
5454 space_name: &str,
5555 board_id: Uuid,
5656 issue_id: Uuid,
@@ -123,7 +123,7 @@
123123
124124 pub async fn list_dependents(
125125 &self,
126 caller: Uuid,
126 caller: i64,
127127 space_name: &str,
128128 board_id: Uuid,
129129 issue_id: Uuid,
@@ -141,7 +141,7 @@
141141
142142 pub async fn list_dependencies(
143143 &self,
144 caller: Uuid,
144 caller: i64,
145145 space_name: &str,
146146 board_id: Uuid,
147147 issue_id: Uuid,
@@ -160,7 +160,7 @@
160160 #[tracing::instrument(name = "issues.delete_dependency", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, dep_id = %dep_id))]
161161 pub async fn delete_dependency(
162162 &self,
163 caller: Uuid,
163 caller: i64,
164164 space_name: &str,
165165 board_id: Uuid,
166166 issue_id: Uuid,
Modified lib/issues/src/services/events.rs lib/issues/src/services/events.rs +2 −2 View lib/issues/src/services/events.rs
@@ -44,7 +44,7 @@
4444impl IssueService {
4545 pub async fn list_events(
4646 &self,
47 caller: Uuid,
47 caller: i64,
4848 space_name: &str,
4949 board_id: Uuid,
5050 issue_id: Uuid,
@@ -65,7 +65,7 @@
6565 .all(self.db().read())
6666 .await?;
6767
68 let user_ids: Vec<Uuid> = rows.iter().map(|e| e.user_id).collect();
68 let user_ids: Vec<i64> = rows.iter().map(|e| e.user_id).collect();
6969 let user_map = self.load_user_briefs(&user_ids).await?;
7070
7171 let items = rows
Modified lib/issues/src/services/issue_users.rs lib/issues/src/services/issue_users.rs +6 −6 View lib/issues/src/services/issue_users.rs
@@ -16,7 +16,7 @@
1616#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1717pub struct IssueUserDto {
1818 pub id: Uuid,
19 pub user_id: Uuid,
19 pub user_id: i64,
2020 pub user: Option<UserDto>,
2121 pub issue_id: Uuid,
2222 pub is_read: bool,
@@ -42,13 +42,13 @@
4242 pub space_name: String,
4343 pub board_id: Uuid,
4444 pub issue_id: String,
45 pub user_id: Uuid,
45 pub user_id: i64,
4646}
4747
4848impl IssueService {
4949 pub async fn list_issue_users(
5050 &self,
51 caller: Uuid,
51 caller: i64,
5252 space_name: &str,
5353 board_id: Uuid,
5454 issue_id: Uuid,
@@ -69,7 +69,7 @@
6969 .all(self.db().read())
7070 .await?;
7171
72 let uids: Vec<Uuid> = rows.iter().map(|u| u.uid).collect();
72 let uids: Vec<i64> = rows.iter().map(|u| u.uid).collect();
7373 let user_map = self.load_user_briefs(&uids).await?;
7474
7575 let items = rows
@@ -98,11 +98,11 @@
9898 #[tracing::instrument(name = "issues.update_issue_user", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, uid = %uid))]
9999 pub async fn update_issue_user(
100100 &self,
101 caller: Uuid,
101 caller: i64,
102102 space_name: &str,
103103 board_id: Uuid,
104104 issue_id: Uuid,
105 uid: Uuid,
105 uid: i64,
106106 cmd: UpdateIssueUserCmd,
107107 ) -> Result<IssueUserDto, IssueError> {
108108 require_issue_access(self, caller, space_name, board_id, issue_id).await?;
Modified lib/issues/src/services/issues.rs lib/issues/src/services/issues.rs +13 −13 View lib/issues/src/services/issues.rs
@@ -103,7 +103,7 @@
103103 )]
104104 pub due_at: Option<OffsetDateTime>,
105105 #[serde(default)]
106 pub assignee_ids: Vec<Uuid>,
106 pub assignee_ids: Vec<i64>,
107107 #[serde(default)]
108108 pub label_ids: Vec<Uuid>,
109109 /// Draft uploads (`POST /issues/attachments`) to link to the new issue.
@@ -129,7 +129,7 @@
129129 )]
130130 pub due_at: Option<Option<OffsetDateTime>>,
131131 #[serde(default)]
132 pub assignee_ids: Option<Vec<Uuid>>,
132 pub assignee_ids: Option<Vec<i64>>,
133133 #[serde(default)]
134134 pub label_ids: Option<Vec<Uuid>>,
135135}
@@ -137,7 +137,7 @@
137137#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
138138pub struct ListIssueQuery {
139139 pub state: Option<IssueState>,
140 pub author_id: Option<Uuid>,
140 pub author_id: Option<i64>,
141141 /// One id or a comma-separated id list (`a,b`); `none` matches issues
142142 /// without a milestone. Any of the listed values matches.
143143 pub milestone_id: Option<String>,
@@ -352,7 +352,7 @@
352352 .filter(issue_member::Column::Kind.eq(issue_member::IssueMemberKind::Assignee))
353353 .all(self.db().read())
354354 .await?;
355 let uids: Vec<Uuid> = rows.iter().map(|r| r.user_id).collect();
355 let uids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
356356 let user_map = self.load_user_briefs(&uids).await?;
357357 for r in rows {
358358 if let Some(brief) = user_map.get(&r.user_id) {
@@ -403,7 +403,7 @@
403403
404404 pub(crate) async fn build_issue_dto(
405405 &self,
406 caller: Uuid,
406 caller: i64,
407407 item: issue_item::Model,
408408 ) -> Result<IssueDto, IssueError> {
409409 let user_map = self.load_user_briefs(&[item.author_id]).await?;
@@ -447,7 +447,7 @@
447447 #[tracing::instrument(name = "issues.create_issue", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
448448 pub async fn create_issue(
449449 &self,
450 caller: Uuid,
450 caller: i64,
451451 space_name: &str,
452452 board_id: Uuid,
453453 cmd: CreateIssueCmd,
@@ -699,7 +699,7 @@
699699
700700 pub async fn list_issues(
701701 &self,
702 caller: Uuid,
702 caller: i64,
703703 space_name: &str,
704704 board_id: Uuid,
705705 q: ListIssueQuery,
@@ -792,7 +792,7 @@
792792 .all(self.db().read())
793793 .await?;
794794
795 let author_ids: Vec<Uuid> = rows.iter().map(|i| i.author_id).collect();
795 let author_ids: Vec<i64> = rows.iter().map(|i| i.author_id).collect();
796796 let user_map = self.load_user_briefs(&author_ids).await?;
797797 let issue_ids: Vec<Uuid> = rows.iter().map(|i| i.id).collect();
798798 let assignee_map = self.load_assignee_map(&issue_ids).await?;
@@ -838,7 +838,7 @@
838838
839839 pub async fn get_issue(
840840 &self,
841 caller: Uuid,
841 caller: i64,
842842 space_name: &str,
843843 board_id: Uuid,
844844 issue_id: Uuid,
@@ -851,7 +851,7 @@
851851 #[tracing::instrument(name = "issues.update_issue", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
852852 pub async fn update_issue(
853853 &self,
854 caller: Uuid,
854 caller: i64,
855855 space_name: &str,
856856 board_id: Uuid,
857857 issue_id: Uuid,
@@ -1157,7 +1157,7 @@
11571157 #[tracing::instrument(name = "issues.bulk_update_state", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
11581158 pub async fn bulk_update_state(
11591159 &self,
1160 caller: Uuid,
1160 caller: i64,
11611161 space_name: &str,
11621162 board_id: Uuid,
11631163 cmd: BulkIssuesCmd,
@@ -1218,7 +1218,7 @@
12181218 #[tracing::instrument(name = "issues.delete_issue", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
12191219 pub async fn delete_issue(
12201220 &self,
1221 caller: Uuid,
1221 caller: i64,
12221222 space_name: &str,
12231223 board_id: Uuid,
12241224 issue_id: Uuid,
@@ -1256,7 +1256,7 @@
12561256 #[tracing::instrument(name = "issues.close_issue", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
12571257 pub async fn close_issue(
12581258 &self,
1259 caller: Uuid,
1259 caller: i64,
12601260 space_name: &str,
12611261 board_id: Uuid,
12621262 issue_id: Uuid,
Modified lib/issues/src/services/labels.rs lib/issues/src/services/labels.rs +9 −9 View lib/issues/src/services/labels.rs
@@ -113,7 +113,7 @@
113113impl IssueService {
114114 async fn lookup_managed_label(
115115 &self,
116 caller: Uuid,
116 caller: i64,
117117 space_name: &str,
118118 board_id: Uuid,
119119 issue_id: Uuid,
@@ -134,7 +134,7 @@
134134 #[tracing::instrument(name = "issues.create_label", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
135135 pub async fn create_label(
136136 &self,
137 caller: Uuid,
137 caller: i64,
138138 space_name: &str,
139139 board_id: Uuid,
140140 cmd: CreateLabelCmd,
@@ -185,7 +185,7 @@
185185
186186 pub async fn list_labels(
187187 &self,
188 caller: Uuid,
188 caller: i64,
189189 space_name: &str,
190190 board_id: Uuid,
191191 q: ListLabelQuery,
@@ -234,7 +234,7 @@
234234
235235 pub async fn get_label(
236236 &self,
237 caller: Uuid,
237 caller: i64,
238238 space_name: &str,
239239 board_id: Uuid,
240240 label_id: Uuid,
@@ -253,7 +253,7 @@
253253 #[tracing::instrument(name = "issues.update_label", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, label_id = %label_id))]
254254 pub async fn update_label(
255255 &self,
256 caller: Uuid,
256 caller: i64,
257257 space_name: &str,
258258 board_id: Uuid,
259259 label_id: Uuid,
@@ -327,7 +327,7 @@
327327 #[tracing::instrument(name = "issues.delete_label", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, label_id = %label_id))]
328328 pub async fn delete_label(
329329 &self,
330 caller: Uuid,
330 caller: i64,
331331 space_name: &str,
332332 board_id: Uuid,
333333 label_id: Uuid,
@@ -371,7 +371,7 @@
371371
372372 pub async fn list_labels_for_issue(
373373 &self,
374 caller: Uuid,
374 caller: i64,
375375 space_name: &str,
376376 board_id: Uuid,
377377 issue_id: Uuid,
@@ -399,7 +399,7 @@
399399 #[tracing::instrument(name = "issues.bind_label", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, label_id = %label_id))]
400400 pub async fn bind_label(
401401 &self,
402 caller: Uuid,
402 caller: i64,
403403 space_name: &str,
404404 board_id: Uuid,
405405 issue_id: Uuid,
@@ -464,7 +464,7 @@
464464 #[tracing::instrument(name = "issues.unbind_label", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, label_id = %label_id))]
465465 pub async fn unbind_label(
466466 &self,
467 caller: Uuid,
467 caller: i64,
468468 space_name: &str,
469469 board_id: Uuid,
470470 issue_id: Uuid,
Modified lib/issues/src/services/members.rs lib/issues/src/services/members.rs +5 −5 View lib/issues/src/services/members.rs
@@ -32,7 +32,7 @@
3232
3333#[derive(Debug, Clone, Deserialize, ToSchema)]
3434pub struct CreateIssueMemberCmd {
35 pub user_id: Uuid,
35 pub user_id: i64,
3636 pub kind: IssueMemberKind,
3737}
3838
@@ -55,7 +55,7 @@
5555 #[tracing::instrument(name = "issues.create_member", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
5656 pub async fn create_member(
5757 &self,
58 caller: Uuid,
58 caller: i64,
5959 space_name: &str,
6060 board_id: Uuid,
6161 issue_id: Uuid,
@@ -201,7 +201,7 @@
201201
202202 pub async fn list_members(
203203 &self,
204 caller: Uuid,
204 caller: i64,
205205 space_name: &str,
206206 board_id: Uuid,
207207 issue_id: Uuid,
@@ -227,7 +227,7 @@
227227 .all(self.db().read())
228228 .await?;
229229
230 let user_ids: Vec<Uuid> = rows.iter().map(|m| m.user_id).collect();
230 let user_ids: Vec<i64> = rows.iter().map(|m| m.user_id).collect();
231231 let user_map = self.load_user_briefs(&user_ids).await?;
232232
233233 let items = rows
@@ -256,7 +256,7 @@
256256 #[tracing::instrument(name = "issues.delete_member", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, member_id = %member_id))]
257257 pub async fn delete_member(
258258 &self,
259 caller: Uuid,
259 caller: i64,
260260 space_name: &str,
261261 board_id: Uuid,
262262 issue_id: Uuid,
Modified lib/issues/src/services/milestones.rs lib/issues/src/services/milestones.rs +5 −5 View lib/issues/src/services/milestones.rs
@@ -150,7 +150,7 @@
150150 #[tracing::instrument(name = "issues.create_milestone", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
151151 pub async fn create_milestone(
152152 &self,
153 caller: Uuid,
153 caller: i64,
154154 space_name: &str,
155155 board_id: Uuid,
156156 cmd: CreateMilestoneCmd,
@@ -207,7 +207,7 @@
207207
208208 pub async fn list_milestones(
209209 &self,
210 caller: Uuid,
210 caller: i64,
211211 space_name: &str,
212212 board_id: Uuid,
213213 q: ListMilestoneQuery,
@@ -261,7 +261,7 @@
261261
262262 pub async fn get_milestone(
263263 &self,
264 caller: Uuid,
264 caller: i64,
265265 space_name: &str,
266266 board_id: Uuid,
267267 milestone_id: Uuid,
@@ -280,7 +280,7 @@
280280 #[tracing::instrument(name = "issues.update_milestone", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, milestone_id = %milestone_id))]
281281 pub async fn update_milestone(
282282 &self,
283 caller: Uuid,
283 caller: i64,
284284 space_name: &str,
285285 board_id: Uuid,
286286 milestone_id: Uuid,
@@ -338,7 +338,7 @@
338338 #[tracing::instrument(name = "issues.delete_milestone", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, milestone_id = %milestone_id))]
339339 pub async fn delete_milestone(
340340 &self,
341 caller: Uuid,
341 caller: i64,
342342 space_name: &str,
343343 board_id: Uuid,
344344 milestone_id: Uuid,
Modified lib/issues/src/services/pins.rs lib/issues/src/services/pins.rs +4 −4 View lib/issues/src/services/pins.rs
@@ -53,7 +53,7 @@
5353 #[tracing::instrument(name = "issues.update_pin", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, pin_id = %pin_id))]
5454 pub async fn update_pin(
5555 &self,
56 caller: Uuid,
56 caller: i64,
5757 space_name: &str,
5858 board_id: Uuid,
5959 pin_id: Uuid,
@@ -80,7 +80,7 @@
8080 #[tracing::instrument(name = "issues.create_pin", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id))]
8181 pub async fn create_pin(
8282 &self,
83 caller: Uuid,
83 caller: i64,
8484 space_name: &str,
8585 board_id: Uuid,
8686 cmd: CreatePinCmd,
@@ -122,7 +122,7 @@
122122
123123 pub async fn list_pins(
124124 &self,
125 caller: Uuid,
125 caller: i64,
126126 space_name: &str,
127127 board_id: Uuid,
128128 ) -> Result<Vec<PinDto>, IssueError> {
@@ -141,7 +141,7 @@
141141 #[tracing::instrument(name = "issues.delete_pin", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, pin_id = %pin_id))]
142142 pub async fn delete_pin(
143143 &self,
144 caller: Uuid,
144 caller: i64,
145145 space_name: &str,
146146 board_id: Uuid,
147147 pin_id: Uuid,
Modified lib/issues/src/services/reactions.rs lib/issues/src/services/reactions.rs +9 −9 View lib/issues/src/services/reactions.rs
@@ -36,7 +36,7 @@
3636pub struct ReactionAggDto {
3737 pub emoji: String,
3838 pub count: i64,
39 pub user_ids: Vec<Uuid>,
39 pub user_ids: Vec<i64>,
4040}
4141
4242#[derive(Debug, Clone, Deserialize, ToSchema)]
@@ -66,7 +66,7 @@
6666}
6767
6868fn aggregate_reactions(rows: Vec<issue_reactions::Model>) -> Vec<ReactionAggDto> {
69 let mut groups: HashMap<String, Vec<Uuid>> = HashMap::new();
69 let mut groups: HashMap<String, Vec<i64>> = HashMap::new();
7070 for r in rows {
7171 groups.entry(r.emoji).or_default().push(r.user_id);
7272 }
@@ -98,7 +98,7 @@
9898 #[tracing::instrument(name = "issues.create_reaction", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
9999 pub async fn create_reaction(
100100 &self,
101 caller: Uuid,
101 caller: i64,
102102 space_name: &str,
103103 board_id: Uuid,
104104 issue_id: Uuid,
@@ -158,7 +158,7 @@
158158 #[tracing::instrument(name = "issues.create_comment_reaction", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, comment_id = %comment_id))]
159159 pub async fn create_comment_reaction(
160160 &self,
161 caller: Uuid,
161 caller: i64,
162162 space_name: &str,
163163 board_id: Uuid,
164164 issue_id: Uuid,
@@ -224,7 +224,7 @@
224224
225225 pub async fn list_reactions(
226226 &self,
227 caller: Uuid,
227 caller: i64,
228228 space_name: &str,
229229 board_id: Uuid,
230230 issue_id: Uuid,
@@ -241,7 +241,7 @@
241241
242242 pub async fn list_comment_reactions(
243243 &self,
244 caller: Uuid,
244 caller: i64,
245245 space_name: &str,
246246 board_id: Uuid,
247247 issue_id: Uuid,
@@ -266,7 +266,7 @@
266266 #[tracing::instrument(name = "issues.remove_reaction_by_emoji", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
267267 pub async fn remove_reaction_by_emoji(
268268 &self,
269 caller: Uuid,
269 caller: i64,
270270 space_name: &str,
271271 board_id: Uuid,
272272 issue_id: Uuid,
@@ -292,7 +292,7 @@
292292 #[tracing::instrument(name = "issues.remove_comment_reaction_by_emoji", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, comment_id = %comment_id))]
293293 pub async fn remove_comment_reaction_by_emoji(
294294 &self,
295 caller: Uuid,
295 caller: i64,
296296 space_name: &str,
297297 board_id: Uuid,
298298 issue_id: Uuid,
@@ -323,7 +323,7 @@
323323 #[tracing::instrument(name = "issues.delete_reaction", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, reaction_id = %reaction_id))]
324324 pub async fn delete_reaction(
325325 &self,
326 caller: Uuid,
326 caller: i64,
327327 space_name: &str,
328328 board_id: Uuid,
329329 issue_id: Uuid,
Modified lib/issues/src/services/repo_relations.rs lib/issues/src/services/repo_relations.rs +3 −3 View lib/issues/src/services/repo_relations.rs
@@ -47,7 +47,7 @@
4747 #[tracing::instrument(name = "issues.create_repo_relation", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
4848 pub async fn create_repo_relation(
4949 &self,
50 caller: Uuid,
50 caller: i64,
5151 space_name: &str,
5252 board_id: Uuid,
5353 issue_id: Uuid,
@@ -89,7 +89,7 @@
8989
9090 pub async fn list_repo_relations(
9191 &self,
92 caller: Uuid,
92 caller: i64,
9393 space_name: &str,
9494 board_id: Uuid,
9595 issue_id: Uuid,
@@ -108,7 +108,7 @@
108108 #[tracing::instrument(name = "issues.delete_repo_relation", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, rel_id = %rel_id))]
109109 pub async fn delete_repo_relation(
110110 &self,
111 caller: Uuid,
111 caller: i64,
112112 space_name: &str,
113113 board_id: Uuid,
114114 issue_id: Uuid,
Modified lib/issues/src/services/service.rs lib/issues/src/services/service.rs +16 −28 View lib/issues/src/services/service.rs
@@ -1,7 +1,6 @@
11use std::collections::{HashMap, HashSet};
22use std::sync::Arc;
33
4use async_trait::async_trait;
54use config::AppConfig;
65use email::EmailService;
76use export::AppState;
@@ -13,8 +12,6 @@
1312use models::users::user;
1413use models::users::user_notifications::{self, NotificationType};
1514use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter, Set};
16use session_layer::SessionError;
17use session_layer::share::guard::{SessionGuard, SessionWithUser};
1815use space::services::spaces::UserDto;
1916use time::OffsetDateTime;
2017use uuid::Uuid;
@@ -70,12 +67,12 @@
7067
7168 pub async fn load_user_briefs(
7269 &self,
73 uids: &[Uuid],
74 ) -> Result<HashMap<Uuid, UserDto>, IssueError> {
70 uids: &[i64],
71 ) -> Result<HashMap<i64, UserDto>, IssueError> {
7572 if uids.is_empty() {
7673 return Ok(HashMap::new());
7774 }
78 let mut set: Vec<Uuid> = uids.to_vec();
75 let mut set: Vec<i64> = uids.to_vec();
7976 set.sort();
8077 set.dedup();
8178 let users = user::Entity::find()
@@ -92,7 +89,7 @@
9289 #[allow(clippy::too_many_arguments)]
9390 pub async fn resolve_issue_ref(
9491 &self,
95 caller: Uuid,
92 caller: i64,
9693 space_name: &str,
9794 board_id: Uuid,
9895 issue_ref: &str,
@@ -113,7 +110,7 @@
113110 &self,
114111 txn: &impl ConnectionTrait,
115112 issue_id: Uuid,
116 user_id: Uuid,
113 user_id: i64,
117114 event_type: IssueEventType,
118115 detail: IssueEventDetail,
119116 ) -> Result<(), IssueError> {
@@ -148,12 +145,12 @@
148145 &self,
149146 txn: &impl ConnectionTrait,
150147 pending: &export::notify::PendingNotifications,
151 recipient: Uuid,
148 recipient: i64,
152149 ntype: NotificationType,
153150 title: String,
154151 body: Option<String>,
155152 data: Option<serde_json::Value>,
156 actor_uid: Option<Uuid>,
153 actor_uid: Option<i64>,
157154 ) -> Result<(), IssueError> {
158155 let row = user_notifications::ActiveModel {
159156 id: Set(Uuid::now_v7()),
@@ -176,15 +173,15 @@
176173 &self,
177174 space: &spaces::Model,
178175 text: &str,
179 exclude: Uuid,
180 ) -> Result<Vec<Uuid>, IssueError> {
176 exclude: i64,
177 ) -> Result<Vec<i64>, IssueError> {
181178 let members = space_members::Entity::find()
182179 .filter(space_members::Column::SpaceId.eq(space.id))
183180 .filter(space_members::Column::Status.eq(MemberStatus::Active))
184181 .all(self.db().read())
185182 .await?;
186183
187 let mut member_uids: Vec<Uuid> = members.into_iter().map(|m| m.user_id).collect();
184 let mut member_uids: Vec<i64> = members.into_iter().map(|m| m.user_id).collect();
188185 if !member_uids.contains(&space.owner_id) {
189186 member_uids.push(space.owner_id);
190187 }
@@ -198,14 +195,14 @@
198195 .all(self.db().read())
199196 .await?;
200197
201 let mut name_to_uid: HashMap<String, Uuid> = HashMap::new();
198 let mut name_to_uid: HashMap<String, i64> = HashMap::new();
202199 for u in users {
203200 name_to_uid.insert(u.username.to_lowercase(), u.uid);
204201 name_to_uid.insert(u.lower_name.to_lowercase(), u.uid);
205202 name_to_uid.insert(u.uid.to_string(), u.uid);
206203 }
207204
208 let mut result_set: HashSet<Uuid> = HashSet::new();
205 let mut result_set: HashSet<i64> = HashSet::new();
209206 let punctuation = [
210207 ',', '.', ';', ':', '!', '?', '\'', '"', '“', '”', '`', ')', ']', '}', '>',
211208 ];
@@ -225,7 +222,7 @@
225222 }
226223 }
227224
228 let mut uids: Vec<Uuid> = result_set.into_iter().collect();
225 let mut uids: Vec<i64> = result_set.into_iter().collect();
229226 uids.sort();
230227 let max_mentions = self.config().issues.max_mentions_per_message;
231228 if uids.len() > max_mentions {
@@ -239,15 +236,15 @@
239236 ///
240237 /// Account preference only: an issue event fans out to watchers, the
241238 /// assignee and mentioned users, each of whom may read a different language.
242 pub(crate) async fn recipient_locale(&self, uid: Uuid) -> i18n::UiLocale {
239 pub(crate) async fn recipient_locale(&self, uid: i64) -> i18n::UiLocale {
243240 let fallback = i18n::config_fallback(self.config().email.default_locale());
244241 i18n::resolve_user_locale(self.db().read(), uid, fallback).await
245242 }
246243
247244 pub(crate) async fn primary_emails(
248245 &self,
249 uids: &[Uuid],
250 ) -> Result<HashMap<Uuid, String>, IssueError> {
246 uids: &[i64],
247 ) -> Result<HashMap<i64, String>, IssueError> {
251248 if uids.is_empty() {
252249 return Ok(HashMap::new());
253250 }
@@ -271,12 +268,3 @@
271268 }
272269}
273270
274#[async_trait]
275impl SessionGuard for IssueService {
276 async fn lookup_session_with_user(
277 &self,
278 token_hash: &str,
279 ) -> Result<Option<SessionWithUser>, SessionError> {
280 session_layer::lookup_session_with_user(self.db().read(), token_hash).await
281 }
282}
Modified lib/issues/src/services/stopwatch.rs lib/issues/src/services/stopwatch.rs +3 −3 View lib/issues/src/services/stopwatch.rs
@@ -35,7 +35,7 @@
3535impl IssueService {
3636 pub async fn get_stopwatch(
3737 &self,
38 caller: Uuid,
38 caller: i64,
3939 space_name: &str,
4040 board_id: Uuid,
4141 issue_id: Uuid,
@@ -75,7 +75,7 @@
7575 #[tracing::instrument(name = "issues.start_stopwatch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
7676 pub async fn start_stopwatch(
7777 &self,
78 caller: Uuid,
78 caller: i64,
7979 space_name: &str,
8080 board_id: Uuid,
8181 issue_id: Uuid,
@@ -120,7 +120,7 @@
120120 #[tracing::instrument(name = "issues.stop_stopwatch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
121121 pub async fn stop_stopwatch(
122122 &self,
123 caller: Uuid,
123 caller: i64,
124124 space_name: &str,
125125 board_id: Uuid,
126126 issue_id: Uuid,
Modified lib/issues/src/services/tracked_time.rs lib/issues/src/services/tracked_time.rs +6 −6 View lib/issues/src/services/tracked_time.rs
@@ -26,7 +26,7 @@
2626
2727#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
2828pub struct ListTrackedTimeQuery {
29 pub user_id: Option<Uuid>,
29 pub user_id: Option<i64>,
3030 pub limit: Option<u64>,
3131 pub offset: Option<u64>,
3232}
@@ -60,7 +60,7 @@
6060 #[tracing::instrument(name = "issues.create_tracked_time", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
6161 pub async fn create_tracked_time(
6262 &self,
63 caller: Uuid,
63 caller: i64,
6464 space_name: &str,
6565 board_id: Uuid,
6666 issue_id: Uuid,
@@ -94,7 +94,7 @@
9494 #[tracing::instrument(name = "issues.update_tracked_time", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, time_id = %time_id))]
9595 pub async fn update_tracked_time(
9696 &self,
97 caller: Uuid,
97 caller: i64,
9898 space_name: &str,
9999 board_id: Uuid,
100100 issue_id: Uuid,
@@ -131,7 +131,7 @@
131131 #[tracing::instrument(name = "issues.delete_tracked_time", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id, time_id = %time_id))]
132132 pub async fn delete_tracked_time(
133133 &self,
134 caller: Uuid,
134 caller: i64,
135135 space_name: &str,
136136 board_id: Uuid,
137137 issue_id: Uuid,
@@ -154,7 +154,7 @@
154154
155155 pub async fn list_tracked_time(
156156 &self,
157 caller: Uuid,
157 caller: i64,
158158 space_name: &str,
159159 board_id: Uuid,
160160 issue_id: Uuid,
@@ -180,7 +180,7 @@
180180 .all(self.db().read())
181181 .await?;
182182
183 let user_ids: Vec<Uuid> = rows.iter().map(|t| t.user_id).collect();
183 let user_ids: Vec<i64> = rows.iter().map(|t| t.user_id).collect();
184184 let user_map = self.load_user_briefs(&user_ids).await?;
185185
186186 let items = rows
Modified lib/issues/src/services/watches.rs lib/issues/src/services/watches.rs +3 −3 View lib/issues/src/services/watches.rs
@@ -36,7 +36,7 @@
3636impl IssueService {
3737 pub async fn get_watch(
3838 &self,
39 caller: Uuid,
39 caller: i64,
4040 space_name: &str,
4141 board_id: Uuid,
4242 issue_id: Uuid,
@@ -66,7 +66,7 @@
6666 #[tracing::instrument(name = "issues.upsert_watch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
6767 pub async fn upsert_watch(
6868 &self,
69 caller: Uuid,
69 caller: i64,
7070 space_name: &str,
7171 board_id: Uuid,
7272 issue_id: Uuid,
@@ -117,7 +117,7 @@
117117 #[tracing::instrument(name = "issues.delete_watch", skip_all, fields(caller = %caller, space = %space_name, board_id = %board_id, issue_id = %issue_id))]
118118 pub async fn delete_watch(
119119 &self,
120 caller: Uuid,
120 caller: i64,
121121 space_name: &str,
122122 board_id: Uuid,
123123 issue_id: Uuid,
Modified lib/issues/src/share/authz.rs lib/issues/src/share/authz.rs +3 −3 View lib/issues/src/share/authz.rs
@@ -8,7 +8,7 @@
88pub async fn require_board_manage(
99 service: &IssueService,
1010 space: &spaces::Model,
11 caller: Uuid,
11 caller: i64,
1212) -> Result<(), IssueError> {
1313 if is_board_manager(service, space, caller).await? {
1414 Ok(())
@@ -20,12 +20,12 @@
2020pub async fn is_board_manager(
2121 service: &IssueService,
2222 space: &spaces::Model,
23 caller: Uuid,
23 caller: i64,
2424) -> Result<bool, IssueError> {
2525 if space.owner_id == caller {
2626 return Ok(true);
2727 }
28 let perms = effective_permissions(service.db().read(), space, caller)
28 let perms = effective_permissions(&service.state, space, caller)
2929 .await
3030 .map_err(IssueError::from)?;
3131 Ok(perms.has(MANAGE_CHANNELS))
Modified lib/issues/src/share/guard.rs lib/issues/src/share/guard.rs +2 −2 View lib/issues/src/share/guard.rs
@@ -1,8 +1,8 @@
11use axum::extract::{Request, State};
22use axum::middleware::Next;
33use axum::response::Response;
4use session_layer::Session;
5use session_layer::share::guard::session_guard;
4use jwt::Session;
5use jwt::share::guard::session_guard;
66
77use crate::services::service::IssueService;
88
Modified lib/issues/src/share/lookup.rs lib/issues/src/share/lookup.rs +8 −8 View lib/issues/src/share/lookup.rs
@@ -29,7 +29,7 @@
2929 service: &IssueService,
3030 space: &spaces::Model,
3131 board_id: Uuid,
32 caller: Uuid,
32 caller: i64,
3333) -> Result<issues::Model, IssueError> {
3434 let board = issues::Entity::find_by_id(board_id)
3535 .one(service.db().read())
@@ -112,9 +112,9 @@
112112pub async fn require_member(
113113 service: &IssueService,
114114 space: &spaces::Model,
115 caller: Uuid,
115 caller: i64,
116116) -> Result<(), IssueError> {
117 let perms = effective_permissions(service.db().read(), space, caller)
117 let perms = effective_permissions(&service.state, space, caller)
118118 .await
119119 .map_err(IssueError::from)?;
120120 if perms.has(VIEW_SPACE) || perms.has(READ) {
@@ -126,7 +126,7 @@
126126
127127pub async fn space_context(
128128 service: &IssueService,
129 caller: Uuid,
129 caller: i64,
130130 space_name: &str,
131131) -> Result<spaces::Model, IssueError> {
132132 let space = lookup_space_by_name(service, space_name).await?;
@@ -136,7 +136,7 @@
136136
137137pub async fn board_context(
138138 service: &IssueService,
139 caller: Uuid,
139 caller: i64,
140140 space_name: &str,
141141 board_id: Uuid,
142142) -> Result<(spaces::Model, issues::Model), IssueError> {
@@ -147,7 +147,7 @@
147147
148148pub async fn issue_context(
149149 service: &IssueService,
150 caller: Uuid,
150 caller: i64,
151151 space_name: &str,
152152 board_id: Uuid,
153153 issue_id: Uuid,
@@ -159,7 +159,7 @@
159159
160160pub async fn require_issue_access(
161161 service: &IssueService,
162 caller: Uuid,
162 caller: i64,
163163 space_name: &str,
164164 board_id: Uuid,
165165 issue_id: Uuid,
@@ -172,7 +172,7 @@
172172/// used by routes that carry no space/board in their path (attachment reads).
173173pub async fn issue_context_by_id(
174174 service: &IssueService,
175 caller: Uuid,
175 caller: i64,
176176 issue_id: Uuid,
177177) -> Result<(spaces::Model, issues::Model, issue_item::Model), IssueError> {
178178 let item = issue_item::Entity::find_by_id(issue_id)
Modified lib/issues/Cargo.toml lib/issues/Cargo.toml +1 −1 View lib/issues/Cargo.toml
@@ -10,7 +10,7 @@
1010i18n = { workspace = true }
1111email = { workspace = true }
1212models = { workspace = true }
13session-layer = { workspace = true }
13jwt = { workspace = true }
1414space = { workspace = true }
1515axum = { workspace = true }
1616http = { workspace = true }
Modified lib/mcp-api/src/tools/args.rs lib/mcp-api/src/tools/args.rs +12 −12 View lib/mcp-api/src/tools/args.rs
@@ -15,7 +15,7 @@
1515#[derive(Debug, Deserialize, schemars::JsonSchema)]
1616pub struct UserRefArgs {
1717 #[serde(default)]
18 pub user_id: Option<Uuid>,
18 pub user_id: Option<i64>,
1919 #[serde(default)]
2020 pub username: Option<String>,
2121}
@@ -97,7 +97,7 @@
9797#[derive(Debug, Deserialize, schemars::JsonSchema)]
9898pub struct SpaceMemberAddArgs {
9999 pub space: String,
100 pub user_id: Uuid,
100 pub user_id: i64,
101101 #[serde(default)]
102102 pub alias_name: Option<String>,
103103}
@@ -105,7 +105,7 @@
105105#[derive(Debug, Deserialize, schemars::JsonSchema)]
106106pub struct SpaceMemberUpdateArgs {
107107 pub space: String,
108 pub user_id: Uuid,
108 pub user_id: i64,
109109 #[serde(default)]
110110 pub alias_name: Option<String>,
111111 #[serde(default)]
@@ -115,7 +115,7 @@
115115#[derive(Debug, Deserialize, schemars::JsonSchema)]
116116pub struct SpaceMemberRefArgs {
117117 pub space: String,
118 pub user_id: Uuid,
118 pub user_id: i64,
119119}
120120
121121#[derive(Debug, Deserialize, schemars::JsonSchema)]
@@ -162,7 +162,7 @@
162162#[derive(Debug, Deserialize, schemars::JsonSchema)]
163163pub struct SpaceGroupAssignArgs {
164164 pub space: String,
165 pub user_id: Uuid,
165 pub user_id: i64,
166166 pub group_id: Uuid,
167167}
168168
@@ -211,7 +211,7 @@
211211pub struct SpaceInviteCreateArgs {
212212 pub space: String,
213213 #[serde(default)]
214 pub user_id: Option<Uuid>,
214 pub user_id: Option<i64>,
215215 #[serde(default)]
216216 pub email: Option<String>,
217217 #[serde(default)]
@@ -576,13 +576,13 @@
576576 #[serde(default)]
577577 pub state: Option<String>,
578578 #[serde(default)]
579 pub author_id: Option<Uuid>,
579 pub author_id: Option<i64>,
580580 #[serde(default)]
581581 pub milestone_id: Option<Uuid>,
582582 #[serde(default)]
583583 pub label_id: Option<Uuid>,
584584 #[serde(default)]
585 pub assignee_id: Option<Uuid>,
585 pub assignee_id: Option<i64>,
586586 #[serde(default)]
587587 pub priority: Option<String>,
588588 #[serde(default)]
@@ -618,7 +618,7 @@
618618 #[serde(default)]
619619 pub due_at: Option<String>,
620620 #[serde(default)]
621 pub assignee_ids: Vec<Uuid>,
621 pub assignee_ids: Vec<i64>,
622622 #[serde(default)]
623623 pub label_ids: Vec<Uuid>,
624624}
@@ -641,7 +641,7 @@
641641 #[serde(default)]
642642 pub due_at: Option<String>,
643643 #[serde(default)]
644 pub assignee_ids: Option<Vec<Uuid>>,
644 pub assignee_ids: Option<Vec<i64>>,
645645 #[serde(default)]
646646 pub label_ids: Option<Vec<Uuid>>,
647647}
@@ -807,7 +807,7 @@
807807 pub space: String,
808808 pub board_id: Uuid,
809809 pub issue_id: String,
810 pub user_id: Uuid,
810 pub user_id: i64,
811811 pub kind: String,
812812}
813813
@@ -1052,7 +1052,7 @@
10521052 pub space: String,
10531053 pub repo: String,
10541054 pub pull: String,
1055 pub reviewer_id: Uuid,
1055 pub reviewer_id: i64,
10561056}
10571057
10581058#[derive(Debug, Deserialize, schemars::JsonSchema)]
Modified lib/mcp-api/src/tools/helpers.rs lib/mcp-api/src/tools/helpers.rs +2 −2 View lib/mcp-api/src/tools/helpers.rs
@@ -34,7 +34,7 @@
3434
3535pub(crate) async fn repo_read(
3636 services: &McpServices,
37 uid: Uuid,
37 uid: i64,
3838 space: &str,
3939 repo: &str,
4040) -> Result<GitContext, CallToolResult> {
@@ -51,7 +51,7 @@
5151
5252pub(crate) async fn repo_write(
5353 services: &McpServices,
54 uid: Uuid,
54 uid: i64,
5555 space: &str,
5656 repo: &str,
5757) -> Result<GitContext, CallToolResult> {
Modified lib/mcp-api/src/tools/me.rs lib/mcp-api/src/tools/me.rs +1 −1 View lib/mcp-api/src/tools/me.rs
@@ -48,7 +48,7 @@
4848 Err(error) => return tool_error(error),
4949 };
5050 match space::share::authz::effective_permissions(
51 services.state.database.read(),
51 &services.state,
5252 &space,
5353 principal.uid,
5454 )
Modified lib/mcp-api/src/auth.rs lib/mcp-api/src/auth.rs +2 −2 View lib/mcp-api/src/auth.rs
@@ -7,7 +7,7 @@
77use axum::middleware::Next;
88use axum::response::{IntoResponse, Response};
99use base64::Engine as _;
10use models::users::user_access_keys::AccessKeyScope;
10use models::users::access_scope::AccessKeyScope;
1111use rmcp::ErrorData;
1212use rmcp::RoleServer;
1313use rmcp::service::RequestContext;
@@ -44,7 +44,7 @@
4444
4545#[derive(Debug, Clone)]
4646pub struct Principal {
47 pub uid: Uuid,
47 pub uid: i64,
4848 pub scope: AccessKeyScope,
4949 pub full_access: bool,
5050 pub source: PrincipalSource,
Modified lib/mcp-api/src/lib.rs lib/mcp-api/src/lib.rs +3 −3 View lib/mcp-api/src/lib.rs
@@ -35,7 +35,7 @@
3535 internal_keys: Vec<InternalKey>,
3636 internal_cidrs: Vec<IpNet>,
3737 trusted_proxies: Vec<IpNet>,
38 system_uid: Option<Uuid>,
38 system_uid: Option<i64>,
3939}
4040
4141#[derive(Clone)]
@@ -46,7 +46,7 @@
4646 &self.0.services
4747 }
4848
49 pub(crate) fn system_uid(&self) -> Option<Uuid> {
49 pub(crate) fn system_uid(&self) -> Option<i64> {
5050 self.0.system_uid
5151 }
5252
@@ -74,7 +74,7 @@
7474 return Router::new();
7575 }
7676
77 let system_uid = match agent::bot::resolve_bot_user(state.database.write(), &state.config.ai)
77 let system_uid = match agent::bot::resolve_bot_user(&state, &state.config.ai)
7878 .await
7979 {
8080 Ok(uid) => {
Modified lib/mcp-api/src/space_token.rs lib/mcp-api/src/space_token.rs +17 −55 View lib/mcp-api/src/space_token.rs
@@ -65,7 +65,7 @@
6565 pub expires_at: OffsetDateTime,
6666 pub space_id: Uuid,
6767 pub space_name: String,
68 pub bot_uid: Uuid,
68 pub bot_uid: i64,
6969 pub bot_username: String,
7070}
7171
@@ -161,60 +161,16 @@
161161 state: &AppState,
162162 space: &spaces::Model,
163163 username: &str,
164) -> Result<Uuid, SpaceTokenError> {
165 let lower = username.to_lowercase();
166 let db = state.database.write();
167 if let Some(row) = user::Entity::find()
168 .filter(user::Column::LowerName.eq(&lower))
169 .one(db)
170 .await?
171 {
172 if !row.bot {
173 return Err(SpaceTokenError::BotNameConflict(username.to_string()));
164) -> Result<i64, SpaceTokenError> {
165 let display = format!("{} Bot", space.name);
166 match agent::bot::ensure_bot(state, username, &display, true).await {
167 Ok(uid) => Ok(uid),
168 Err(sea_orm::DbErr::Custom(message))
169 if message.to_lowercase().contains("already taken") =>
170 {
171 Err(SpaceTokenError::BotNameConflict(username.to_string()))
174172 }
175 return Ok(row.uid);
176 }
177
178 let uid = Uuid::now_v7();
179 let now = OffsetDateTime::now_utc();
180 let created = user::ActiveModel {
181 uid: Set(uid),
182 username: Set(username.to_string()),
183 lower_name: Set(lower.clone()),
184 display_name: Set(format!("{} Bot", space.name)),
185 avatar_url: Set(String::new()),
186 is_active: Set(true),
187 is_restricted: Set(false),
188 prohibit_login: Set(true),
189 visibility: Set(ProfileVisibility::Public),
190 bot: Set(true),
191 system: Set(true),
192 last_login_at: Set(None),
193 last_login_ip_hash: Set(None),
194 version: Set(1),
195 passwd_hash: Set(None),
196 password_last_changed_at: Set(None),
197 passwd_version: Set(None),
198 totp_encrypted_secret: Set(None),
199 totp_enabled: Set(false),
200 totp_last_used_at: Set(None),
201 totp_last_counter: Set(None),
202 timezone: Set(None),
203 locale: Set(None),
204 language: Set(None),
205 created_at: Set(now),
206 updated_at: Set(now),
207 }
208 .insert(db)
209 .await;
210 match created {
211 Ok(row) => Ok(row.uid),
212 Err(_) => user::Entity::find()
213 .filter(user::Column::LowerName.eq(&lower))
214 .one(db)
215 .await?
216 .map(|row| row.uid)
217 .ok_or_else(|| sea_orm::DbErr::RecordNotFound("space bot user".to_string()).into()),
173 Err(error) => Err(SpaceTokenError::Database(error)),
218174 }
219175}
220176
@@ -224,8 +180,14 @@
224180async fn ensure_space_membership(
225181 state: &AppState,
226182 space_id: Uuid,
227 bot_uid: Uuid,
183 bot_uid: i64,
228184) -> Result<(), SpaceTokenError> {
185 if let Some(client) = state.kenauth.as_ref() {
186 client
187 .ensure_bot_member(&space_id.to_string(), bot_uid, Vec::new(), Vec::new())
188 .await
189 .map_err(|error| SpaceTokenError::Export(export::ExportError::Connection(error.to_string())))?;
190 }
229191 let now = OffsetDateTime::now_utc();
230192 state
231193 .database
Modified lib/models/src/agent/agent_item.rs lib/models/src/agent/agent_item.rs +1 −1 View lib/models/src/agent/agent_item.rs
@@ -12,7 +12,7 @@
1212 pub collection_id: Uuid,
1313 #[sea_orm(unique_key = "collection_number")]
1414 pub autoincrement_id: i32,
15 pub author_id: Uuid,
15 pub author_id: i64,
1616 pub name: String,
1717 pub description: Option<String>,
1818 pub instructions: Option<String>,
Modified lib/models/src/ai/code_comment.rs lib/models/src/ai/code_comment.rs +2 −2 View lib/models/src/ai/code_comment.rs
@@ -15,12 +15,12 @@
1515 pub line: i32,
1616 #[sea_orm(indexed)]
1717 pub commit_sha: String,
18 pub author_id: Uuid,
18 pub author_id: i64,
1919 #[sea_orm(indexed)]
2020 pub parent_id: Option<Uuid>,
2121 pub body: String,
2222 pub resolved_at: Option<OffsetDateTime>,
23 pub resolved_by: Option<Uuid>,
23 pub resolved_by: Option<i64>,
2424 pub created_at: OffsetDateTime,
2525 pub updated_at: Option<OffsetDateTime>,
2626 #[sea_orm(belongs_to, from = "repo_id", to = "id")]
Modified lib/models/src/chat/attachments.rs lib/models/src/chat/attachments.rs +1 −1 View lib/models/src/chat/attachments.rs
@@ -33,7 +33,7 @@
3333 #[sea_orm(primary_key, auto_increment = false)]
3434 pub id: Uuid,
3535 #[sea_orm(indexed)]
36 pub user_id: Uuid,
36 pub user_id: i64,
3737 #[sea_orm(indexed)]
3838 pub room_id: Option<Uuid>,
3939 #[sea_orm(indexed)]
Modified lib/models/src/chat/emojis.rs lib/models/src/chat/emojis.rs +1 −1 View lib/models/src/chat/emojis.rs
@@ -13,7 +13,7 @@
1313 pub name: String,
1414 #[sea_orm(unique_key = "space_emoji_name")]
1515 pub lower_name: String,
16 pub user_id: Uuid,
16 pub user_id: i64,
1717 pub bucket: String,
1818 pub s3_key: String,
1919 #[sea_orm(default_value = "false")]
Modified lib/models/src/chat/message_edits.rs lib/models/src/chat/message_edits.rs +1 −1 View lib/models/src/chat/message_edits.rs
@@ -11,7 +11,7 @@
1111 pub id: Uuid,
1212 #[sea_orm(indexed)]
1313 pub message_id: Uuid,
14 pub editor_id: Uuid,
14 pub editor_id: i64,
1515 pub content: String,
1616 #[sea_orm(column_type = "JsonBinary", default_value = "[]")]
1717 pub attachments: JsonValue,
Modified lib/models/src/chat/message_reminders.rs lib/models/src/chat/message_reminders.rs +1 −1 View lib/models/src/chat/message_reminders.rs
@@ -36,7 +36,7 @@
3636 #[sea_orm(primary_key, auto_increment = false)]
3737 pub id: Uuid,
3838 #[sea_orm(indexed)]
39 pub user_id: Uuid,
39 pub user_id: i64,
4040 pub room_id: Uuid,
4141 pub message_id: Uuid,
4242 pub remind_at: OffsetDateTime,
Modified lib/models/src/chat/messages.rs lib/models/src/chat/messages.rs +2 −2 View lib/models/src/chat/messages.rs
@@ -78,7 +78,7 @@
7878 #[sea_orm(indexed, unique_key = "chat_message_room_seq")]
7979 pub room_id: Uuid,
8080 #[sea_orm(indexed)]
81 pub author_id: Uuid,
81 pub author_id: i64,
8282 #[sea_orm(default_value = 0)]
8383 pub author_type: AuthorType,
8484 pub webhook_id: Option<Uuid>,
@@ -106,7 +106,7 @@
106106 pub nonce: Option<String>,
107107 #[sea_orm(default_value = "false")]
108108 pub pinned: bool,
109 pub pinned_by: Option<Uuid>,
109 pub pinned_by: Option<i64>,
110110 pub pinned_at: Option<OffsetDateTime>,
111111 pub edited_at: Option<OffsetDateTime>,
112112 pub deleted_at: Option<OffsetDateTime>,
Modified lib/models/src/chat/outbound_webhooks.rs lib/models/src/chat/outbound_webhooks.rs +1 −1 View lib/models/src/chat/outbound_webhooks.rs
@@ -10,7 +10,7 @@
1010 pub id: Uuid,
1111 #[sea_orm(indexed)]
1212 pub room_id: Uuid,
13 pub user_id: Uuid,
13 pub user_id: i64,
1414 pub name: String,
1515 pub url: String,
1616 pub secret: String,
Modified lib/models/src/chat/presence_states.rs lib/models/src/chat/presence_states.rs +1 −1 View lib/models/src/chat/presence_states.rs
@@ -35,7 +35,7 @@
3535#[sea_orm(table_name = "chat_presence")]
3636pub struct Model {
3737 #[sea_orm(primary_key, auto_increment = false)]
38 pub user_id: Uuid,
38 pub user_id: i64,
3939 #[sea_orm(default_value = 3)]
4040 pub status: PresenceStatus,
4141 #[sea_orm(column_type = "JsonBinary", default_value = "[]")]
Modified lib/models/src/chat/reactions.rs lib/models/src/chat/reactions.rs +1 −1 View lib/models/src/chat/reactions.rs
@@ -11,7 +11,7 @@
1111 #[sea_orm(primary_key, auto_increment = false)]
1212 pub emoji: String,
1313 #[sea_orm(primary_key, auto_increment = false)]
14 pub user_id: Uuid,
14 pub user_id: i64,
1515 pub created_at: OffsetDateTime,
1616 #[sea_orm(belongs_to, from = "message_id", to = "id")]
1717 pub message: BelongsTo<super::messages::Entity>,
Modified lib/models/src/chat/read_states.rs lib/models/src/chat/read_states.rs +1 −1 View lib/models/src/chat/read_states.rs
@@ -7,7 +7,7 @@
77#[sea_orm(table_name = "chat_read_state")]
88pub struct Model {
99 #[sea_orm(primary_key, auto_increment = false)]
10 pub user_id: Uuid,
10 pub user_id: i64,
1111 #[sea_orm(primary_key, auto_increment = false)]
1212 pub room_id: Uuid,
1313 pub last_read_message_id: Option<Uuid>,
Modified lib/models/src/chat/room_bookmarks.rs lib/models/src/chat/room_bookmarks.rs +1 −1 View lib/models/src/chat/room_bookmarks.rs
@@ -10,7 +10,7 @@
1010 pub id: Uuid,
1111 #[sea_orm(indexed)]
1212 pub room_id: Uuid,
13 pub user_id: Uuid,
13 pub user_id: i64,
1414 pub title: String,
1515 pub url: Option<String>,
1616 pub emoji: Option<String>,
Modified lib/models/src/chat/room_user_settings.rs lib/models/src/chat/room_user_settings.rs +1 −1 View lib/models/src/chat/room_user_settings.rs
@@ -34,7 +34,7 @@
3434#[sea_orm(table_name = "chat_room_user_setting")]
3535pub struct Model {
3636 #[sea_orm(primary_key, auto_increment = false)]
37 pub user_id: Uuid,
37 pub user_id: i64,
3838 #[sea_orm(primary_key, auto_increment = false)]
3939 pub room_id: Uuid,
4040 #[sea_orm(default_value = 0)]
Modified lib/models/src/chat/room_webhooks.rs lib/models/src/chat/room_webhooks.rs +1 −1 View lib/models/src/chat/room_webhooks.rs
@@ -10,7 +10,7 @@
1010 pub id: Uuid,
1111 #[sea_orm(indexed)]
1212 pub room_id: Uuid,
13 pub user_id: Uuid,
13 pub user_id: i64,
1414 pub name: String,
1515 pub token_hash: String,
1616 #[sea_orm(default_value = "false")]
Modified lib/models/src/chat/thread_members.rs lib/models/src/chat/thread_members.rs +1 −1 View lib/models/src/chat/thread_members.rs
@@ -32,7 +32,7 @@
3232 #[sea_orm(primary_key, auto_increment = false)]
3333 pub thread_id: Uuid,
3434 #[sea_orm(primary_key, auto_increment = false)]
35 pub user_id: Uuid,
35 pub user_id: i64,
3636 #[sea_orm(default_value = 1)]
3737 pub notify_level: ThreadNotifyLevel,
3838 pub last_read_message_id: Option<Uuid>,
Modified lib/models/src/chat/threads.rs lib/models/src/chat/threads.rs +1 −1 View lib/models/src/chat/threads.rs
@@ -12,7 +12,7 @@
1212 pub room_id: Uuid,
1313 #[sea_orm(unique_key = "chat_thread_root_message")]
1414 pub root_message_id: Uuid,
15 pub owner_id: Uuid,
15 pub owner_id: i64,
1616 pub name: Option<String>,
1717 #[sea_orm(default_value = 0)]
1818 pub reply_count: i64,
Modified lib/models/src/chat/user_drafts.rs lib/models/src/chat/user_drafts.rs +1 −1 View lib/models/src/chat/user_drafts.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 #[sea_orm(indexed)]
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 #[sea_orm(indexed)]
1414 pub room_id: Uuid,
1515 pub thread_id: Option<Uuid>,
Modified lib/models/src/ci/ci_deployment.rs lib/models/src/ci/ci_deployment.rs +1 −1 View lib/models/src/ci/ci_deployment.rs
@@ -46,7 +46,7 @@
4646 pub commit_sha: String,
4747 pub git_ref: String,
4848 pub state: DeploymentState,
49 pub actor_id: Option<Uuid>,
49 pub actor_id: Option<i64>,
5050 pub created_at: OffsetDateTime,
5151 pub updated_at: OffsetDateTime,
5252 #[sea_orm(belongs_to, from = "environment_id", to = "id")]
Modified lib/models/src/ci/ci_secret.rs lib/models/src/ci/ci_secret.rs +1 −1 View lib/models/src/ci/ci_secret.rs
@@ -40,7 +40,7 @@
4040 pub environment_id: Option<Uuid>,
4141 pub name: String,
4242 pub value_ciphertext: String,
43 pub created_by: Option<Uuid>,
43 pub created_by: Option<i64>,
4444 pub created_at: OffsetDateTime,
4545 pub updated_at: OffsetDateTime,
4646 #[sea_orm(belongs_to, from = "repo_id", to = "id")]
Modified lib/models/src/ci/environment_review.rs lib/models/src/ci/environment_review.rs +1 −1 View lib/models/src/ci/environment_review.rs
@@ -13,7 +13,7 @@
1313 pub repo_id: Uuid,
1414 pub environment_id: Uuid,
1515 pub state: String,
16 pub reviewer_id: Option<Uuid>,
16 pub reviewer_id: Option<i64>,
1717 pub comment: Option<String>,
1818 pub requested_at: OffsetDateTime,
1919 pub eligible_at: OffsetDateTime,
Modified lib/models/src/ci/job_token.rs lib/models/src/ci/job_token.rs +1 −1 View lib/models/src/ci/job_token.rs
@@ -10,7 +10,7 @@
1010 pub id: Uuid,
1111 pub job_id: Uuid,
1212 pub repo_id: Uuid,
13 pub actor_id: Uuid,
13 pub actor_id: i64,
1414 pub token_hash: String,
1515 pub lease_token: String,
1616 pub contents_read: bool,
Modified lib/models/src/ci/workflow_run.rs lib/models/src/ci/workflow_run.rs +1 −1 View lib/models/src/ci/workflow_run.rs
@@ -88,7 +88,7 @@
8888 pub id: Uuid,
8989 #[sea_orm(indexed)]
9090 pub repo_id: Uuid,
91 pub trigger_user_id: Option<Uuid>,
91 pub trigger_user_id: Option<i64>,
9292 pub workflow_file: String,
9393 pub workflow_name: String,
9494 pub event: WorkflowEventKind,
Modified lib/models/src/dir/dir_overwrite_users.rs lib/models/src/dir/dir_overwrite_users.rs +1 −1 View lib/models/src/dir/dir_overwrite_users.rs
@@ -8,7 +8,7 @@
88 #[sea_orm(primary_key, auto_increment = false)]
99 pub dir_id: Uuid,
1010 #[sea_orm(primary_key, auto_increment = false)]
11 pub user_id: Uuid,
11 pub user_id: i64,
1212 #[sea_orm(default_value = 0)]
1313 pub allow_mask: i64,
1414 #[sea_orm(default_value = 0)]
Modified lib/models/src/dir/dirs.rs lib/models/src/dir/dirs.rs +2 −2 View lib/models/src/dir/dirs.rs
@@ -46,12 +46,12 @@
4646 #[sea_orm(default_value = "false")]
4747 pub is_archived: bool,
4848 pub archived_at: Option<OffsetDateTime>,
49 pub archived_by: Option<Uuid>,
49 pub archived_by: Option<i64>,
5050 #[sea_orm(indexed)]
5151 pub created_at: OffsetDateTime,
5252 pub updated_at: OffsetDateTime,
5353 #[sea_orm(indexed)]
54 pub created_by_uid: Uuid,
54 pub created_by_uid: i64,
5555 #[sea_orm(belongs_to, from = "space_id", to = "id")]
5656 pub space: BelongsTo<Option<super::super::space::spaces::Entity>>,
5757 #[sea_orm(
Modified lib/models/src/git/git_commit_status.rs lib/models/src/git/git_commit_status.rs +1 −1 View lib/models/src/git/git_commit_status.rs
@@ -44,7 +44,7 @@
4444 pub context: String,
4545 pub description: Option<String>,
4646 pub target_url: Option<String>,
47 pub creator_id: Uuid,
47 pub creator_id: i64,
4848 pub created_at: OffsetDateTime,
4949 pub updated_at: Option<OffsetDateTime>,
5050 #[sea_orm(belongs_to, from = "(repo_id, sha)", to = "(repo_id, sha)")]
Modified lib/models/src/git/git_commits.rs lib/models/src/git/git_commits.rs +1 −1 View lib/models/src/git/git_commits.rs
@@ -11,7 +11,7 @@
1111 #[sea_orm(primary_key, auto_increment = false)]
1212 pub sha: String,
1313 pub message: String,
14 pub author_id: Option<Uuid>,
14 pub author_id: Option<i64>,
1515 pub author_name: String,
1616 pub author_email: String,
1717 pub authored_at: OffsetDateTime,
Modified lib/models/src/git/git_lfs_lock.rs lib/models/src/git/git_lfs_lock.rs +1 −1 View lib/models/src/git/git_lfs_lock.rs
@@ -12,7 +12,7 @@
1212 pub repo_id: Uuid,
1313 #[sea_orm(unique_key = "repo_path")]
1414 pub path: String,
15 pub owner_id: Uuid,
15 pub owner_id: i64,
1616 pub oid: Option<String>,
1717 pub created_at: OffsetDateTime,
1818 #[sea_orm(belongs_to, from = "repo_id", to = "id")]
Modified lib/models/src/import/job.rs lib/models/src/import/job.rs +1 −1 View lib/models/src/import/job.rs
@@ -20,7 +20,7 @@
2020 pub repo_id: Option<Uuid>,
2121 pub repo_name: String,
2222 #[sea_orm(indexed)]
23 pub created_by: Uuid,
23 pub created_by: i64,
2424 #[sea_orm(indexed)]
2525 pub state: String,
2626 pub stage: String,
Modified lib/models/src/issue/comment.rs lib/models/src/issue/comment.rs +1 −1 View lib/models/src/issue/comment.rs
@@ -12,7 +12,7 @@
1212 // project 模块暂时移除后保留列,恢复时加回 belongs_to(同 issue_event_line/issue_pin/issue_reaction/issue_watch)
1313 pub project_id: Option<Uuid>,
1414 pub parent_id: Option<Uuid>,
15 pub poster_id: Uuid,
15 pub poster_id: i64,
1616 pub content: String,
1717 pub created_at: OffsetDateTime,
1818 pub updated_at: Option<OffsetDateTime>,
Modified lib/models/src/issue/issue_attachment.rs lib/models/src/issue/issue_attachment.rs +1 −1 View lib/models/src/issue/issue_attachment.rs
@@ -38,7 +38,7 @@
3838 #[sea_orm(indexed)]
3939 pub issue_id: Option<Uuid>,
4040 #[sea_orm(indexed)]
41 pub uploader_id: Uuid,
41 pub uploader_id: i64,
4242 pub filename: String,
4343 pub content_type: String,
4444 pub size: i64,
Modified lib/models/src/issue/issue_content_history.rs lib/models/src/issue/issue_content_history.rs +1 −1 View lib/models/src/issue/issue_content_history.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub issue_id: Uuid,
12 pub poster_id: Uuid,
12 pub poster_id: i64,
1313 pub comment_id: Option<Uuid>,
1414 pub content_before: Option<String>,
1515 pub content_after: Option<String>,
Modified lib/models/src/issue/issue_event_line.rs lib/models/src/issue/issue_event_line.rs +1 −1 View lib/models/src/issue/issue_event_line.rs
@@ -77,7 +77,7 @@
7777 pub issue_id: Option<Uuid>,
7878 // project 模块暂时移除后保留列,恢复时加回 belongs_to(同 comment/issue_pin/issue_reaction/issue_watch)
7979 pub project_id: Option<Uuid>,
80 pub user_id: Uuid,
80 pub user_id: i64,
8181 pub event_type: IssueEventType,
8282 pub comment_id: Option<Uuid>,
8383 pub commit_sha: Option<String>,
Modified lib/models/src/issue/issue_item.rs lib/models/src/issue/issue_item.rs +1 −1 View lib/models/src/issue/issue_item.rs
@@ -42,7 +42,7 @@
4242 pub collection_id: Uuid,
4343 #[sea_orm(unique_key = "collection_number")]
4444 pub autoincrement_id: i32,
45 pub author_id: Uuid,
45 pub author_id: i64,
4646 pub title: String,
4747 pub description: Option<String>,
4848 pub body: String,
Modified lib/models/src/issue/issue_member.rs lib/models/src/issue/issue_member.rs +1 −1 View lib/models/src/issue/issue_member.rs
@@ -37,7 +37,7 @@
3737 #[sea_orm(unique_key = "issue_user_kind")]
3838 pub issue_id: Uuid,
3939 #[sea_orm(unique_key = "issue_user_kind")]
40 pub user_id: Uuid,
40 pub user_id: i64,
4141 #[sea_orm(unique_key = "issue_user_kind")]
4242 pub kind: IssueMemberKind,
4343 pub created_at: OffsetDateTime,
Modified lib/models/src/issue/issue_reactions.rs lib/models/src/issue/issue_reactions.rs +1 −1 View lib/models/src/issue/issue_reactions.rs
@@ -16,7 +16,7 @@
1616 pub project_id: Option<Uuid>,
1717 #[sea_orm(unique_key = "issue_user_emoji")]
1818 #[sea_orm(unique_key = "comment_user_emoji")]
19 pub user_id: Uuid,
19 pub user_id: i64,
2020 #[sea_orm(unique_key = "issue_user_emoji")]
2121 #[sea_orm(unique_key = "comment_user_emoji")]
2222 pub emoji: String,
Modified lib/models/src/issue/issue_user.rs lib/models/src/issue/issue_user.rs +1 −1 View lib/models/src/issue/issue_user.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 #[sea_orm(unique_key = "uid_issue")]
12 pub uid: Uuid,
12 pub uid: i64,
1313 #[sea_orm(unique_key = "uid_issue")]
1414 pub issue_id: Uuid,
1515 #[sea_orm(default_value = "false")]
Modified lib/models/src/issue/issue_watch.rs lib/models/src/issue/issue_watch.rs +1 −1 View lib/models/src/issue/issue_watch.rs
@@ -16,7 +16,7 @@
1616 pub project_id: Option<Uuid>,
1717 #[sea_orm(unique_key = "issue_user")]
1818 #[sea_orm(unique_key = "project_user")]
19 pub user_id: Uuid,
19 pub user_id: i64,
2020 pub mode: WatchMode,
2121 pub created_at: OffsetDateTime,
2222 pub updated_at: Option<OffsetDateTime>,
Modified lib/models/src/issue/stopwatch.rs lib/models/src/issue/stopwatch.rs +1 −1 View lib/models/src/issue/stopwatch.rs
@@ -11,7 +11,7 @@
1111 #[sea_orm(unique_key = "user_issue")]
1212 pub issue_id: Uuid,
1313 #[sea_orm(unique_key = "user_issue")]
14 pub user_id: Uuid,
14 pub user_id: i64,
1515 pub started_at: OffsetDateTime,
1616 pub created_at: OffsetDateTime,
1717 #[sea_orm(belongs_to, from = "issue_id", to = "id")]
Modified lib/models/src/issue/tracked_time.rs lib/models/src/issue/tracked_time.rs +1 −1 View lib/models/src/issue/tracked_time.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub issue_id: Uuid,
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 pub seconds: i64,
1414 pub created_at: OffsetDateTime,
1515 #[sea_orm(belongs_to, from = "issue_id", to = "id")]
Modified lib/models/src/products/product_installs.rs lib/models/src/products/product_installs.rs +1 −1 View lib/models/src/products/product_installs.rs
@@ -27,7 +27,7 @@
2727 pub space_id: Uuid,
2828 #[sea_orm(unique_key = "space_product")]
2929 pub product_id: Uuid,
30 pub installed_by: Uuid,
30 pub installed_by: i64,
3131 pub status: InstallStatus,
3232 pub installed_at: OffsetDateTime,
3333 pub uninstalled_at: Option<OffsetDateTime>,
Modified lib/models/src/products/product_reviews.rs lib/models/src/products/product_reviews.rs +1 −1 View lib/models/src/products/product_reviews.rs
@@ -11,7 +11,7 @@
1111 #[sea_orm(unique_key = "product_user")]
1212 pub product_id: Uuid,
1313 #[sea_orm(unique_key = "product_user")]
14 pub user_id: Uuid,
14 pub user_id: i64,
1515 pub rating: i16,
1616 pub comment: Option<String>,
1717 pub created_at: OffsetDateTime,
Modified lib/models/src/products/products.rs lib/models/src/products/products.rs +1 −1 View lib/models/src/products/products.rs
@@ -24,7 +24,7 @@
2424 #[sea_orm(primary_key, auto_increment = false)]
2525 pub id: Uuid,
2626 pub space_id: Option<Uuid>,
27 pub owner_id: Uuid,
27 pub owner_id: i64,
2828 pub name: String,
2929 #[sea_orm(unique)]
3030 pub slug: String,
Modified lib/models/src/pull_request/pull_request_assignee.rs lib/models/src/pull_request/pull_request_assignee.rs +1 −1 View lib/models/src/pull_request/pull_request_assignee.rs
@@ -11,7 +11,7 @@
1111 #[sea_orm(unique_key = "pull_assignee")]
1212 pub pull_request_id: Uuid,
1313 #[sea_orm(unique_key = "pull_assignee")]
14 pub user_id: Uuid,
14 pub user_id: i64,
1515 pub created_at: OffsetDateTime,
1616 #[sea_orm(belongs_to, from = "pull_request_id", to = "id")]
1717 pub pull_request: BelongsTo<super::pull_requests::Entity>,
Modified lib/models/src/pull_request/pull_request_comment.rs lib/models/src/pull_request/pull_request_comment.rs +2 −2 View lib/models/src/pull_request/pull_request_comment.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub pull_request_id: Uuid,
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 pub review_id: Option<Uuid>,
1414 pub parent_comment_id: Option<Uuid>,
1515 pub commit_sha: Option<String>,
@@ -20,7 +20,7 @@
2020 pub original_line: Option<i32>,
2121 pub body: String,
2222 pub resolved_at: Option<OffsetDateTime>,
23 pub resolved_by: Option<Uuid>,
23 pub resolved_by: Option<i64>,
2424 pub created_at: OffsetDateTime,
2525 pub updated_at: Option<OffsetDateTime>,
2626 #[sea_orm(belongs_to, from = "pull_request_id", to = "id")]
Modified lib/models/src/pull_request/pull_request_event_line.rs lib/models/src/pull_request/pull_request_event_line.rs +1 −1 View lib/models/src/pull_request/pull_request_event_line.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub pull_request_id: Uuid,
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 pub event_type: String,
1414 pub commit_sha: Option<String>,
1515 pub content: Option<String>,
Modified lib/models/src/pull_request/pull_request_review.rs lib/models/src/pull_request/pull_request_review.rs +1 −1 View lib/models/src/pull_request/pull_request_review.rs
@@ -39,7 +39,7 @@
3939 #[sea_orm(primary_key, auto_increment = false)]
4040 pub id: Uuid,
4141 pub pull_request_id: Uuid,
42 pub user_id: Uuid,
42 pub user_id: i64,
4343 pub commit_sha: String,
4444 pub official: bool,
4545 pub content: Option<String>,
Modified lib/models/src/pull_request/pull_request_reviewer.rs lib/models/src/pull_request/pull_request_reviewer.rs +2 −2 View lib/models/src/pull_request/pull_request_reviewer.rs
@@ -41,13 +41,13 @@
4141 #[sea_orm(unique_key = "pull_reviewer")]
4242 pub pull_request_id: Uuid,
4343 #[sea_orm(unique_key = "pull_reviewer")]
44 pub user_id: Uuid,
44 pub user_id: i64,
4545 #[sea_orm(default_value = "0")]
4646 pub state: ReviewerState,
4747 #[sea_orm(default_value = "true")]
4848 pub required: bool,
4949 pub review_id: Option<Uuid>,
50 pub requested_by_id: Uuid,
50 pub requested_by_id: i64,
5151 pub created_at: OffsetDateTime,
5252 pub updated_at: Option<OffsetDateTime>,
5353 #[sea_orm(belongs_to, from = "pull_request_id", to = "id")]
Modified lib/models/src/pull_request/pull_requests.rs lib/models/src/pull_request/pull_requests.rs +2 −2 View lib/models/src/pull_request/pull_requests.rs
@@ -39,7 +39,7 @@
3939 pub issue_id: Option<Uuid>,
4040 #[sea_orm(unique_key = "repo_number")]
4141 pub autoincrement_id: i32,
42 pub author_id: Uuid,
42 pub author_id: i64,
4343 pub title: String,
4444 pub body: String,
4545 pub head_branch: String,
@@ -60,7 +60,7 @@
6060 pub additions: i32,
6161 pub deletions: i32,
6262 pub files_changed: i32,
63 pub merger_id: Option<Uuid>,
63 pub merger_id: Option<i64>,
6464 pub merged_at: Option<OffsetDateTime>,
6565 pub is_locked: bool,
6666 pub created_at: OffsetDateTime,
Modified lib/models/src/repo/repo_branch_protection_whitelists.rs lib/models/src/repo/repo_branch_protection_whitelists.rs +1 −1 View lib/models/src/repo/repo_branch_protection_whitelists.rs
@@ -37,7 +37,7 @@
3737 #[sea_orm(unique_key = "policy_principal")]
3838 pub kind: WhitelistKind,
3939 #[sea_orm(unique_key = "policy_principal")]
40 pub user_id: Option<Uuid>,
40 pub user_id: Option<i64>,
4141 #[sea_orm(unique_key = "policy_principal")]
4242 pub group_id: Option<Uuid>,
4343 #[sea_orm(belongs_to, from = "policy_id", to = "id")]
Modified lib/models/src/repo/repo_collaborations.rs lib/models/src/repo/repo_collaborations.rs +1 −1 View lib/models/src/repo/repo_collaborations.rs
@@ -26,7 +26,7 @@
2626 #[sea_orm(unique_key = "repo_user")]
2727 pub repo_id: Uuid,
2828 #[sea_orm(unique_key = "repo_user")]
29 pub user_id: Uuid,
29 pub user_id: i64,
3030 pub mode: CollaborationMode,
3131 pub created_at: OffsetDateTime,
3232 pub updated_at: OffsetDateTime,
Modified lib/models/src/repo/repo_release.rs lib/models/src/repo/repo_release.rs +1 −1 View lib/models/src/repo/repo_release.rs
@@ -10,7 +10,7 @@
1010 pub id: Uuid,
1111 #[sea_orm(unique_key = "repo_tag")]
1212 pub repo_id: Uuid,
13 pub publisher_id: Uuid,
13 pub publisher_id: i64,
1414 pub publisher_name: String,
1515 pub tag_name: String,
1616 #[sea_orm(unique_key = "repo_tag")]
Modified lib/models/src/repo/repo_star.rs lib/models/src/repo/repo_star.rs +1 −1 View lib/models/src/repo/repo_star.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 #[sea_orm(unique_key = "user_repo")]
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 #[sea_orm(unique_key = "user_repo")]
1414 pub repo_id: Uuid,
1515 pub created_at: OffsetDateTime,
Modified lib/models/src/repo/repo_tag_protection_whitelists.rs lib/models/src/repo/repo_tag_protection_whitelists.rs +1 −1 View lib/models/src/repo/repo_tag_protection_whitelists.rs
@@ -8,7 +8,7 @@
88 #[sea_orm(primary_key, auto_increment = false)]
99 pub policy_id: Uuid,
1010 #[sea_orm(primary_key, auto_increment = false)]
11 pub user_id: Uuid,
11 pub user_id: i64,
1212 #[sea_orm(belongs_to, from = "policy_id", to = "id")]
1313 pub policy: BelongsTo<super::repo_tag_protection_policy::Entity>,
1414 #[sea_orm(belongs_to, from = "user_id", to = "uid")]
Modified lib/models/src/repo/repo_watch.rs lib/models/src/repo/repo_watch.rs +1 −1 View lib/models/src/repo/repo_watch.rs
@@ -10,7 +10,7 @@
1010 #[sea_orm(primary_key, auto_increment = false)]
1111 pub id: Uuid,
1212 #[sea_orm(unique_key = "user_repo")]
13 pub user_id: Uuid,
13 pub user_id: i64,
1414 #[sea_orm(unique_key = "user_repo")]
1515 pub repo_id: Uuid,
1616 pub mode: WatchMode,
Modified lib/models/src/room/room_bans.rs lib/models/src/room/room_bans.rs +2 −2 View lib/models/src/room/room_bans.rs
@@ -9,9 +9,9 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub room_id: Uuid,
1111 #[sea_orm(primary_key, auto_increment = false)]
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 pub reason: Option<String>,
14 pub banned_by: Uuid,
14 pub banned_by: i64,
1515 pub expires_at: Option<OffsetDateTime>,
1616 pub created_at: OffsetDateTime,
1717 pub updated_at: Option<OffsetDateTime>,
Modified lib/models/src/room/room_invites.rs lib/models/src/room/room_invites.rs +2 −2 View lib/models/src/room/room_invites.rs
@@ -9,8 +9,8 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub id: Uuid,
1111 pub room_id: Uuid,
12 pub inviter_id: Uuid,
13 pub invitee_user_id: Option<Uuid>,
12 pub inviter_id: i64,
13 pub invitee_user_id: Option<i64>,
1414 pub invitee_email: Option<String>,
1515 pub accept_code: String,
1616 pub created_at: OffsetDateTime,
Modified lib/models/src/room/room_members.rs lib/models/src/room/room_members.rs +1 −1 View lib/models/src/room/room_members.rs
@@ -39,7 +39,7 @@
3939 #[sea_orm(primary_key, auto_increment = false)]
4040 pub room_id: Uuid,
4141 #[sea_orm(primary_key, auto_increment = false)]
42 pub user_id: Uuid,
42 pub user_id: i64,
4343 #[sea_orm(default_value = 0)]
4444 pub role: RoomMemberRole,
4545 pub nick: Option<String>,
Modified lib/models/src/room/rooms.rs lib/models/src/room/rooms.rs +1 −1 View lib/models/src/room/rooms.rs
@@ -59,7 +59,7 @@
5959 pub position: i64,
6060 #[sea_orm(indexed)]
6161 pub parent_id: Option<Uuid>,
62 pub owner_id: Uuid,
62 pub owner_id: i64,
6363 #[sea_orm(default_value = "false")]
6464 pub is_private: bool,
6565 #[sea_orm(default_value = "false")]
Modified lib/models/src/space/space_bans.rs lib/models/src/space/space_bans.rs +2 −2 View lib/models/src/space/space_bans.rs
@@ -9,9 +9,9 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub space_id: Uuid,
1111 #[sea_orm(primary_key, auto_increment = false)]
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 pub reason: Option<String>,
14 pub banned_by: Uuid,
14 pub banned_by: i64,
1515 pub created_at: OffsetDateTime,
1616 pub updated_at: OffsetDateTime,
1717 pub expires_at: Option<OffsetDateTime>,
Modified lib/models/src/space/space_groups.rs lib/models/src/space/space_groups.rs +1 −1 View lib/models/src/space/space_groups.rs
@@ -19,7 +19,7 @@
1919 pub version: i32,
2020 pub created_at: OffsetDateTime,
2121 pub updated_at: Option<OffsetDateTime>,
22 pub created_by: Uuid,
22 pub created_by: i64,
2323 #[sea_orm(belongs_to, from = "space_id", to = "id")]
2424 pub space: BelongsTo<super::spaces::Entity>,
2525 #[sea_orm(belongs_to, from = "created_by", to = "uid")]
Modified lib/models/src/space/space_invite.rs lib/models/src/space/space_invite.rs +1 −1 View lib/models/src/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: Option<Uuid>,
12 pub user_id: Option<i64>,
1313 pub email: String,
1414 // 授予的角色组见 space_invite_groups 关联表
1515 pub accept_code: String,
Modified lib/models/src/space/space_join_request.rs lib/models/src/space/space_join_request.rs +2 −2 View lib/models/src/space/space_join_request.rs
@@ -37,10 +37,10 @@
3737 #[sea_orm(primary_key, auto_increment = false)]
3838 pub id: Uuid,
3939 pub space_id: Uuid,
40 pub user_id: Uuid,
40 pub user_id: i64,
4141 pub status: SpaceJoinRequestStatus,
4242 pub note: Option<String>,
43 pub decided_by: Option<Uuid>,
43 pub decided_by: Option<i64>,
4444 pub decided_at: Option<OffsetDateTime>,
4545 pub created_at: OffsetDateTime,
4646 pub updated_at: OffsetDateTime,
Modified lib/models/src/space/space_member_groups.rs lib/models/src/space/space_member_groups.rs +1 −1 View lib/models/src/space/space_member_groups.rs
@@ -9,7 +9,7 @@
99 #[sea_orm(primary_key, auto_increment = false)]
1010 pub space_id: Uuid,
1111 #[sea_orm(primary_key, auto_increment = false)]
12 pub user_id: Uuid,
12 pub user_id: i64,
1313 #[sea_orm(primary_key, auto_increment = false)]
1414 pub group_id: Uuid,
1515 pub created_at: OffsetDateTime,
Modified lib/pulls/Cargo.toml lib/pulls/Cargo.toml +1 −1 View lib/pulls/Cargo.toml
@@ -10,7 +10,7 @@
1010git = { workspace = true }
1111issues = { workspace = true }
1212models = { workspace = true }
13session-layer = { workspace = true }
13jwt = { workspace = true }
1414space = { workspace = true }
1515axum = { workspace = true }
1616sea-orm = { workspace = true }
Modified lib/push/src/worker.rs lib/push/src/worker.rs +2 −2 View lib/push/src/worker.rs
@@ -216,7 +216,7 @@
216216
217217 /// "用户在线"= 任意标签页持有聊天 WS 连接(集合里还有未过期的连接成员);
218218 /// 此时站内 toast 已经送达,再弹系统通知就是重复提醒。
219 async fn skip_online(&self, uid: Uuid) -> bool {
219 async fn skip_online(&self, uid: i64) -> bool {
220220 export::redis::zset_live_count(&self.state.redis, &online_key(uid))
221221 .await
222222 .unwrap_or(0)
@@ -260,7 +260,7 @@
260260
261261 /// 头像在库里存的是 `storage://` locator,预签名后才是浏览器可取到的
262262 /// 地址;外链头像(导入数据)原样透传,空值表示前端渲染首字母。
263 async fn avatar_url(&self, uid: Uuid, stored: &str) -> Option<String> {
263 async fn avatar_url(&self, uid: i64, stored: &str) -> Option<String> {
264264 if stored.is_empty() {
265265 return None;
266266 }
Modified lib/search/src/service.rs lib/search/src/service.rs +12 −12 View lib/search/src/service.rs
@@ -637,7 +637,7 @@
637637
638638 pub async fn search(
639639 &self,
640 caller: Option<Uuid>,
640 caller: Option<i64>,
641641 query: SearchQuery,
642642 ) -> Result<SearchPage, SearchError> {
643643 self.search_scoped(caller, query, true).await
@@ -647,7 +647,7 @@
647647 // 不加这层过滤时引用数量会把定义和代码命中完全淹没。
648648 async fn search_scoped(
649649 &self,
650 caller: Option<Uuid>,
650 caller: Option<i64>,
651651 query: SearchQuery,
652652 definitions_only: bool,
653653 ) -> Result<SearchPage, SearchError> {
@@ -718,7 +718,7 @@
718718
719719 pub async fn symbols(
720720 &self,
721 caller: Option<Uuid>,
721 caller: Option<i64>,
722722 space_name: String,
723723 repo_name: String,
724724 q: String,
@@ -745,7 +745,7 @@
745745
746746 pub async fn references(
747747 &self,
748 caller: Option<Uuid>,
748 caller: Option<i64>,
749749 space_name: String,
750750 repo_name: String,
751751 symbol_name: String,
@@ -1225,7 +1225,7 @@
12251225 break;
12261226 }
12271227 before_seq = rows.last().map(|message| message.seq);
1228 let author_ids: Vec<Uuid> = rows.iter().map(|message| message.author_id).collect();
1228 let author_ids: Vec<i64> = rows.iter().map(|message| message.author_id).collect();
12291229 let authors = self.author_titles(&author_ids).await?;
12301230 for message in rows {
12311231 if documents.len() >= MAX_ROOM_MESSAGE_DOCUMENTS {
@@ -1282,12 +1282,12 @@
12821282 /// 消息作者在检索结果里的标题(显示名)。
12831283 async fn author_titles(
12841284 &self,
1285 author_ids: &[Uuid],
1286 ) -> Result<HashMap<Uuid, String>, SearchError> {
1285 author_ids: &[i64],
1286 ) -> Result<HashMap<i64, String>, SearchError> {
12871287 if author_ids.is_empty() {
12881288 return Ok(HashMap::new());
12891289 }
1290 let mut ids: Vec<Uuid> = author_ids.to_vec();
1290 let mut ids: Vec<i64> = author_ids.to_vec();
12911291 ids.sort();
12921292 ids.dedup();
12931293 Ok(user::Entity::find()
@@ -1362,7 +1362,7 @@
13621362 .await?)
13631363 }
13641364
1365 async fn visible_repos(&self, caller: Option<Uuid>) -> Result<Vec<RepoContext>, SearchError> {
1365 async fn visible_repos(&self, caller: Option<i64>) -> Result<Vec<RepoContext>, SearchError> {
13661366 let repos = repos::Entity::find()
13671367 .filter(repos::Column::IsArchived.eq(false))
13681368 .all(self.db.read())
@@ -1406,7 +1406,7 @@
14061406 .collect())
14071407 }
14081408
1409 async fn visible_space_ids(&self, caller: Option<Uuid>) -> Result<Vec<String>, SearchError> {
1409 async fn visible_space_ids(&self, caller: Option<i64>) -> Result<Vec<String>, SearchError> {
14101410 let Some(caller) = caller else {
14111411 return Ok(Vec::new());
14121412 };
@@ -1431,7 +1431,7 @@
14311431
14321432 /// 聊天消息的可见房间:来访者所在空间里的公共频道,加上他本人所在的
14331433 /// 私密频道与 DM/群聊;空间封禁与房间封禁都会把房间摘掉。
1434 async fn visible_room_ids(&self, caller: Option<Uuid>) -> Result<Vec<String>, SearchError> {
1434 async fn visible_room_ids(&self, caller: Option<i64>) -> Result<Vec<String>, SearchError> {
14351435 let Some(caller) = caller else {
14361436 return Ok(Vec::new());
14371437 };
@@ -1504,7 +1504,7 @@
15041504 message: &messages::Model,
15051505 room: &rooms::Model,
15061506 space: Option<&spaces::Model>,
1507 authors: &HashMap<Uuid, String>,
1507 authors: &HashMap<i64, String>,
15081508) -> Option<SearchDocument> {
15091509 if message.deleted_at.is_some() {
15101510 return None;
Modified lib/space/src/errors.rs lib/space/src/errors.rs +24 −0 View lib/space/src/errors.rs
@@ -35,6 +35,30 @@
3535
3636impl std::error::Error for SpaceError {}
3737
38impl From<kenauth::KenAuthError> for SpaceError {
39 fn from(error: kenauth::KenAuthError) -> Self {
40 match error {
41 kenauth::KenAuthError::NotFound => Self::NotFound,
42 kenauth::KenAuthError::Unauthenticated => Self::Unauthorized,
43 kenauth::KenAuthError::Unavailable(message) => {
44 Self::Internal(format!("kenoauth unavailable: {message}"))
45 }
46 kenauth::KenAuthError::Rejected(message) => {
47 let lower = message.to_lowercase();
48 if lower.contains("already") || lower.contains("taken") {
49 Self::Conflict(message)
50 } else if lower.contains("forbidden") || lower.contains("permission") {
51 Self::Forbidden
52 } else {
53 Self::BadRequest(message)
54 }
55 }
56 kenauth::KenAuthError::Config(message)
57 | kenauth::KenAuthError::Internal(message) => Self::Internal(message),
58 }
59 }
60}
61
3862export::db_error_conversions!(SpaceError);
3963export::storage_error_conversion!(SpaceError);
4064export::export_error_conversion!(SpaceError);
Modified lib/space/src/lib.rs lib/space/src/lib.rs +1 −0 View lib/space/src/lib.rs
@@ -4,3 +4,4 @@
44pub mod routes;
55pub mod services;
66pub mod share;
7pub mod sync;
Modified lib/space/Cargo.toml lib/space/Cargo.toml +3 −1 View lib/space/Cargo.toml
@@ -13,7 +13,9 @@
1313imaging = { workspace = true }
1414models = { workspace = true }
1515search = { workspace = true }
16session-layer = { workspace = true }
16jwt = { workspace = true }
17kenauth = { workspace = true }
18prost-types = { workspace = true }
1719storage = { workspace = true }
1820axum = { workspace = true }
1921sea-orm = { workspace = true }
Modified lib/user/src/openapi.rs lib/user/src/openapi.rs +1 −22 View lib/user/src/openapi.rs
@@ -2,13 +2,11 @@
22use models::users::user::ProfileVisibility;
33use models::users::user_push_subscriptions::PushSubscriptionPlatform;
44use models::users::user_reports::ReportStatus;
5use models::users::user_sessions::SessionKind;
65use models::users::user_suspensions::SuspensionType;
76use utoipa::OpenApi;
87
98use crate::handlers;
109use crate::services::avatar::{AvatarDto, AvatarFormat};
11use crate::services::emails::{AddEmailCmd, AddEmailResp, EmailDto, VerifyAddEmailCmd};
1210use crate::services::notifications::{
1311 RecentMentionDto, SaveMessageCmd, SavedMessageDto, UserReportDto,
1412};
@@ -16,7 +14,6 @@
1614 ListLimitQuery, MemeDto, MySpaceDto, ProfileDetailDto, SpaceDto, UpsertMemeCmd,
1715};
1816use crate::services::push_subscriptions::{PushSubscriptionDto, UpsertPushCmd};
19use crate::services::sessions::{CountResp, ListSessionsQuery, SessionDto};
2017use crate::services::suspensions::{IncludeRevokedQuery, SuspensionDto};
2118
2219#[derive(OpenApi)]
@@ -33,14 +30,6 @@
3330 handlers::update_meme::update_meme,
3431 handlers::delete_meme::delete_meme,
3532 handlers::get_profile_detail::get_profile_detail,
36 handlers::list_emails::list_emails,
37 handlers::add_email::add_email,
38 handlers::verify_add_email::verify_add_email,
39 handlers::set_primary_email::set_primary_email,
40 handlers::delete_email::delete_email,
41 handlers::list_sessions::list_sessions,
42 handlers::revoke_session::revoke_session,
43 handlers::revoke_all_sessions::revoke_all_sessions,
4433 handlers::list_push_subscriptions::list_push_subscriptions,
4534 handlers::upsert_push_subscription::upsert_push_subscription,
4635 handlers::delete_push_subscription::delete_push_subscription,
@@ -55,8 +44,6 @@
5544 components(schemas(
5645 export::pagination::PageQuery,
5746 imaging::layout::ImageVariant,
58 export::pagination::Page<crate::services::sessions::SessionDto>,
59
6047 NoData,
6148 AvatarFormat,
6249 AvatarDto,
@@ -66,13 +53,6 @@
6653 UpsertMemeCmd,
6754 ListLimitQuery,
6855 ProfileDetailDto,
69 EmailDto,
70 AddEmailCmd,
71 AddEmailResp,
72 VerifyAddEmailCmd,
73 SessionDto,
74 ListSessionsQuery,
75 CountResp,
7656 PushSubscriptionDto,
7757 UpsertPushCmd,
7858 RecentMentionDto,
@@ -83,10 +63,9 @@
8363 IncludeRevokedQuery,
8464 crate::handlers::push_config::PushConfigDto,
8565 ProfileVisibility,
86 SessionKind,
8766 PushSubscriptionPlatform,
8867 ReportStatus,
8968 SuspensionType,
9069 ))
9170)]
92pub struct UserApiDoc;
71pub struct UserApiDoc;
No newline at end of file
Modified lib/user/src/routes.rs lib/user/src/routes.rs +1 −22 View lib/user/src/routes.rs
@@ -1,33 +1,24 @@
11use axum::Router;
22use axum::routing::{delete, get, patch, post, put};
33
4use crate::handlers::add_email::add_email;
54use crate::handlers::create_meme::create_meme;
65use crate::handlers::delete_avatar::delete_avatar;
7use crate::handlers::delete_email::delete_email;
86use crate::handlers::delete_meme::delete_meme;
97use crate::handlers::delete_push_subscription::delete_push_subscription;
108use crate::handlers::get_avatar::get_avatar;
119use crate::handlers::get_profile_detail::get_profile_detail;
12use crate::handlers::get_session::get_session;
1310use crate::handlers::get_user_avatar::get_user_avatar;
14use crate::handlers::list_emails::list_emails;
1511use crate::handlers::list_memes::list_memes;
1612use crate::handlers::list_push_subscriptions::list_push_subscriptions;
1713use crate::handlers::list_saved_messages::list_saved_messages;
18use crate::handlers::list_sessions::list_sessions;
1914use crate::handlers::my_reports::my_reports;
2015use crate::handlers::my_spaces::my_spaces;
2116use crate::handlers::my_suspensions::my_suspensions;
2217use crate::handlers::recent_mentions::recent_mentions;
23use crate::handlers::revoke_all_sessions::revoke_all_sessions;
24use crate::handlers::revoke_session::revoke_session;
2518use crate::handlers::save_message::save_message;
26use crate::handlers::set_primary_email::set_primary_email;
2719use crate::handlers::unsave_message::unsave_message;
2820use crate::handlers::upload_avatar::upload_avatar;
2921use crate::handlers::upsert_push_subscription::upsert_push_subscription;
30use crate::handlers::verify_add_email::verify_add_email;
3122use crate::services::service::UserService;
3223use crate::share::guard::user_guard;
3324
@@ -55,19 +46,7 @@
5546 put(crate::handlers::update_meme::update_meme),
5647 )
5748 .route("/me/memes/{meme_id}", delete(delete_meme))
58 .route("/me/emails", get(list_emails).post(add_email))
59 .route("/me/emails/verify", post(verify_add_email))
60 .route("/me/emails/{email_id}/primary", patch(set_primary_email))
61 .route("/me/emails/{email_id}", delete(delete_email))
62 .route(
63 "/me/sessions",
64 get(list_sessions).delete(revoke_all_sessions),
65 )
6649 .route(
67 "/me/sessions/{session_id}",
68 get(get_session).delete(revoke_session),
69 )
70 .route(
7150 "/me/push-subscriptions",
7251 get(list_push_subscriptions).post(upsert_push_subscription),
7352 )
@@ -89,4 +68,4 @@
8968 ));
9069
9170 public.merge(protected)
92}
71}
No newline at end of file
Modified lib/user/Cargo.toml lib/user/Cargo.toml +1 −1 View lib/user/Cargo.toml
@@ -11,7 +11,7 @@
1111i18n = { workspace = true }
1212imaging = { workspace = true }
1313email = { workspace = true }
14session-layer = { workspace = true }
14jwt = { workspace = true }
1515storage = { workspace = true }
1616axum = { workspace = true }
1717sea-orm = { workspace = true }
Modified lib/webapi/src/cleanup.rs lib/webapi/src/cleanup.rs +3 −3 View lib/webapi/src/cleanup.rs
@@ -23,8 +23,8 @@
2323 TransactionTrait,
2424};
2525use serde::{Deserialize, Serialize};
26use session_layer::Session;
27use session_layer::share::session::require_uid;
26use jwt::Session;
27use jwt::share::session::require_uid;
2828use utoipa::{OpenApi, ToSchema};
2929use uuid::Uuid;
3030
@@ -171,7 +171,7 @@
171171async fn require_owner(
172172 state: &CleanupState,
173173 space_name: &str,
174 uid: Uuid,
174 uid: i64,
175175) -> Result<models::space::spaces::Model, ApiError> {
176176 let space = lookup_space_by_name(&state.space, space_name)
177177 .await
Modified lib/webapi/src/lib.rs lib/webapi/src/lib.rs +62 −7 View lib/webapi/src/lib.rs
@@ -4,14 +4,45 @@
44pub mod spa;
55
66use axum::Router;
7use axum::extract::{Request, State};
8use axum::http::StatusCode;
9use axum::middleware::Next;
10use axum::response::{IntoResponse, Response};
711use export::AppState;
812use export::middleware::AppTraceLayer;
9use session_layer::{SessionLayer, store};
1013use tower_http::limit::RequestBodyLimitLayer;
1114use tower_http::trace::TraceLayer;
1215
1316const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
17pub const SERVICE_KEY_HEADER: &str = "x-gitdata-service-key";
18
19fn constant_time_eq(left: &[u8], right: &[u8]) -> bool {
20 if left.len() != right.len() {
21 return false;
22 }
23 let mut diff = 0u8;
24 for (a, b) in left.iter().zip(right.iter()) {
25 diff |= a ^ b;
26 }
27 diff == 0
28}
1429
30async fn require_service_key(
31 State(expected): State<String>,
32 request: Request,
33 next: Next,
34) -> Response {
35 let provided = request
36 .headers()
37 .get(SERVICE_KEY_HEADER)
38 .and_then(|value| value.to_str().ok())
39 .unwrap_or_default();
40 if expected.is_empty() || !constant_time_eq(provided.as_bytes(), expected.as_bytes()) {
41 return (StatusCode::UNAUTHORIZED, "invalid service key").into_response();
42 }
43 next.run(request).await
44}
45
1546fn build_cors_layer(web: &config::web::WebConfig) -> tower_http::cors::CorsLayer {
1647 use axum::http::{HeaderName, header};
1748 use tower_http::cors::{AllowOrigin, CorsLayer};
@@ -42,9 +73,7 @@
4273 ])
4374}
4475
45pub async fn router(state: AppState) -> Result<Router, session::SessionError> {
46 let store = store::connect(state.redis.clone(), &state.config.session).await?;
47
76pub async fn router(state: AppState) -> Result<Router, jwt::SessionError> {
4877 let cors = build_cors_layer(&state.config.web);
4978
5079 let auth = auth::services::AuthService::new(state.clone());
@@ -60,6 +89,19 @@
6089 ::search::SearchService::from_parts(state.database.clone(), state.config.search.clone())
6190 .expect("validated search client configuration");
6291 let import = ::import::ImportService::new(state.clone());
92 let internal_service_key = state.config.internal.service_key.clone();
93 let jwt_verifier = if state.config.oauth.enabled {
94 match jwt::JwtVerifier::new(&state.config.oauth) {
95 Ok(verifier) => Some(verifier),
96 Err(error) => {
97 tracing::warn!(%error, "oauth is enabled but the JWT verifier could not be built");
98 None
99 }
100 }
101 } else {
102 None
103 };
104 let revalidate_state = state.clone();
63105 let health_state = state;
64106
65107 // 清理接口需要跨模块调用(space/issues/agents/git),这里的服务都还没被
@@ -85,8 +127,8 @@
85127 .merge(crate::search::internal_router().with_state(search))
86128 .merge(::import::routes::internal_router().with_state(import))
87129 .route_layer(axum::middleware::from_fn_with_state(
88 auth.clone(),
89 auth::share::guard::admin_guard,
130 internal_service_key,
131 require_service_key,
90132 ));
91133
92134 let router: Router = Router::new()
@@ -96,7 +138,6 @@
96138 .merge(export::health::router(health_state))
97139 .fallback(spa::fallback)
98140 .layer(cors)
99 .layer(SessionLayer::new(store))
100141 .layer(RequestBodyLimitLayer::new(MAX_REQUEST_BODY_BYTES))
101142 .layer(
102143 TraceLayer::new_for_http()
@@ -106,5 +147,19 @@
106147 .layer(export::telemetry::metrics_layer())
107148 .layer(AppTraceLayer::new());
108149
150 let router = match jwt_verifier {
151 Some(verifier) => {
152 let warm = verifier.clone();
153 tokio::spawn(async move { warm.warm_up().await });
154 router
155 .layer(axum::Extension(verifier))
156 .layer(axum::middleware::from_fn_with_state(
157 revalidate_state,
158 auth::oauth::revalidate::revalidate,
159 ))
160 }
161 None => router,
162 };
163
109164 Ok(router.merge(ci::actions_runtime::router().with_state(ci)))
110165}
Modified lib/webapi/src/openapi.rs lib/webapi/src/openapi.rs +12 −11 View lib/webapi/src/openapi.rs
@@ -23,17 +23,19 @@
2323- `POST /auth/logout` is idempotent: repeating it always succeeds with `200`.
2424
2525### Authentication
26- `session_cookie`: primary web session auth (cookie `gitdata_session`).
27- `basic_access_key`: HTTP Basic auth with an access key created via
28 `POST /auth/access-keys` (id as user, secret as password). **Scope: git
29 repository endpoints only** (`/repos/*`, `/graphql`); it is NOT accepted by
30 chat/issues/space/user endpoints.
26- `session_cookie`: KenOAuth access token carried in the `gitdata_session`
27 cookie. `GET /auth/login` starts the OIDC authorization-code + PKCE flow;
28 `POST /auth/refresh` rotates the access/refresh pair; `POST /auth/logout`
29 revokes the KenOAuth session and clears both cookies.
30- `basic_access_key`: HTTP Basic auth with a KenOAuth-issued access key.
31 **Scope: git repository endpoints only** (`/repos/*`, `/graphql`); it is NOT
32 accepted by chat/issues/space/user endpoints.
3133
3234### URL & parameter style
3335- Paths are `kebab-case`, plural nouns, nesting expresses ownership.
3436- Query/path/JSON field names are `snake_case`; enum values are `snake_case`.
35- All endpoints are versioned under `/api/v1`. `/api/v1/internal/*` requires a
36 session belonging to a configured platform admin and is NOT documented here.
37- All endpoints are versioned under `/api/v1`. `/api/v1/internal/*` is guarded
38 by the `x-gitdata-service-key` header and is NOT documented here.
3739- State-transition actions use `POST {resource}/{id}/{verb}` (close/reopen);
3840 idempotent whole-resource state uses `PUT` (default branch, pin, read-state);
3941 partial updates use `PATCH`.
@@ -53,10 +55,9 @@
5355Example: `{"milestone_id": null}` unassigns the milestone; omitting
5456`milestone_id` leaves it untouched.
5557
56### 2FA login exchange
57`POST /auth/login` responds with `needs_2fa` and an opaque one-time
58`challenge` string; pass it to `POST /auth/login/2fa` together with the
59TOTP/recovery code. The challenge is single-use and short-lived.
58### 2FA
59TOTP enrollment, recovery codes and sensitive-operation verification are
60handled by KenOAuth; GitData only proxies the account-management endpoints.
6061
6162### Operations
6263- `GET /healthz` — liveness probe (no auth, outside `/api/v1`).
Modified lib/webapi/src/search.rs lib/webapi/src/search.rs +1 −1 View lib/webapi/src/search.rs
@@ -5,7 +5,7 @@
55use axum::{Json, Router};
66use export::rest::{AppResponseData, NoData};
77use serde::Deserialize;
8use session_layer::{Session, SessionUser};
8use jwt::{Session, SessionUser};
99use utoipa::{IntoParams, OpenApi, ToSchema};
1010use uuid::Uuid;
1111
Modified lib/webapi/Cargo.toml lib/webapi/Cargo.toml +2 −2 View lib/webapi/Cargo.toml
@@ -19,9 +19,9 @@
1919models = { workspace = true }
2020sea-orm = { workspace = true }
2121export = { workspace = true }
22session-layer = { workspace = true }
22jwt = { workspace = true }
23tokio = { workspace = true }
2324axum = { workspace = true }
24session = { workspace = true }
2525tower-http = { workspace = true }
2626tracing = { workspace = true }
2727utoipa = { workspace = true }
Modified scripts/dev.ps1 scripts/dev.ps1 +2 −4 View scripts/dev.ps1
@@ -20,14 +20,13 @@
2020 synchost = @{ Exe = 'synchost'; Port = 4083; Health = $true }
2121 searchhost = @{ Exe = 'searchhost'; Port = 0; Health = $false }
2222 mails = @{ Exe = 'mails'; Port = 4084; Health = $true }
23 adminhost = @{ Exe = 'adminhost'; Port = 3004; Health = $true }
2423 webhook = @{ Exe = 'webhook'; Port = 3005; Health = $true }
2524 aihost = @{ Exe = 'aihost'; Port = 3006; Health = $true }
2625 mcp = @{ Exe = 'mcp'; Port = 3007; Health = $true }
2726 web = @{ Bun = 'web'; Port = 5173; Health = $false }
2827}
29$Order = @('migrate', 'apihost', 'githost', 'im', 'synchost', 'searchhost', 'mails', 'adminhost', 'webhook', 'aihost', 'mcp', 'web')
30$Backends = @('migrate', 'apihost', 'githost', 'im', 'synchost', 'searchhost', 'mails', 'adminhost', 'webhook', 'aihost', 'mcp')
28$Order = @('migrate', 'apihost', 'githost', 'im', 'synchost', 'searchhost', 'mails', 'webhook', 'aihost', 'mcp', 'web')
29$Backends = @('migrate', 'apihost', 'githost', 'im', 'synchost', 'searchhost', 'mails', 'webhook', 'aihost', 'mcp')
3130$Aliases = @{
3231 api = 'apihost'
3332 git = 'githost'
@@ -45,7 +44,6 @@
4544 im = 'ws://127.0.0.1:4082'
4645 synchost = 'http://127.0.0.1:4083/healthz'
4746 mails = 'http://127.0.0.1:4084/healthz'
48 adminhost = 'http://127.0.0.1:3004'
4947 webhook = 'http://127.0.0.1:3005'
5048 aihost = 'http://127.0.0.1:3006/healthz'
5149 mcp = 'http://127.0.0.1:3007/healthz'
Modified scripts/dev.sh scripts/dev.sh +2 −4 View scripts/dev.sh
@@ -12,8 +12,8 @@
1212DEV_HOST_KEY="$DEV_DIR/ssh_host_ed25519"
1313COMPOSE_FILE="$ROOT/docker-compose.dev.yml"
1414
15ORDER="apihost githost im synchost mails adminhost webhook aihost mcp web"
16BACKENDS="apihost githost im synchost mails adminhost webhook aihost mcp"
15ORDER="apihost githost im synchost mails webhook aihost mcp web"
16BACKENDS="apihost githost im synchost mails webhook aihost mcp"
1717INFRA="postgresql:5432 redis:6379 s3:9000 meilisearch:7700"
1818
1919if [ -t 1 ]; then
@@ -42,7 +42,6 @@
4242 im) echo 4082 ;;
4343 synchost) echo 4083 ;;
4444 mails) echo 4084 ;;
45 adminhost) echo 3004 ;;
4645 webhook) echo 3005 ;;
4746 aihost) echo 3006 ;;
4847 mcp) echo 3007 ;;
@@ -79,7 +78,6 @@
7978 im) echo "ws://127.0.0.1:4082" ;;
8079 synchost) echo "http://127.0.0.1:4083/healthz" ;;
8180 mails) echo "http://127.0.0.1:4084/healthz" ;;
82 adminhost) echo "http://127.0.0.1:3004" ;;
8381 webhook) echo "http://127.0.0.1:3005" ;;
8482 aihost) echo "http://127.0.0.1:3006/healthz" ;;
8583 mcp) echo "http://127.0.0.1:3007/healthz" ;;
Modified web/scripts/lint-nav.mjs web/scripts/lint-nav.mjs +2 −7 View web/scripts/lint-nav.mjs
@@ -16,14 +16,9 @@
1616
1717const AUTH_EXEMPT = [
1818 'src/features/auth/',
19 'src/pages/login/',
20 'src/pages/sign-up/',
21 'src/pages/forgot-password/',
22 'src/pages/reset-password/',
23 'src/pages/email-verification/',
24 'src/pages/two-factor/',
19 'src/app/routes/deferred-routes',
2520]
26const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'pow-solver', 'livekit'])
21const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'livekit'])
2722
2823const PATTERNS = [
2924 ['raw <a> anchor', /<a[\s>]/],
Modified web/.oxlintrc.json web/.oxlintrc.json +1 −1 View web/.oxlintrc.json
@@ -1,7 +1,7 @@
11{
22 "$schema": "./node_modules/oxlint/configuration_schema.json",
33 "plugins": ["react", "typescript", "oxc"],
4 "ignorePatterns": ["src/shared/lib/pow-solver/**", "src/shared/livekit/**"],
4 "ignorePatterns": ["src/shared/livekit/**"],
55 "rules": {
66 "react/rules-of-hooks": "error",
77 "react/only-export-components": ["warn", { "allowConstantExport": true }]
Modified web/steiger.config.js web/steiger.config.js +0 −1 View web/steiger.config.js
@@ -74,7 +74,6 @@
7474 '**/node_modules/**',
7575 '**/dist/**',
7676 'src/shared/api/**', // auto-generated by `bun run gen:api`
77 'src/shared/lib/pow-solver/**', // vendored wasm-bindgen build
7877 'src/shared/livekit/**', // vendored @livekit/components-react|core|styles
7978 ],
8079 },
Modified .gitignore .gitignore +2 −1 View .gitignore
@@ -11,4 +11,5 @@
1111admin/node_modules
1212.codegraph
1313sagent-plan.md
14.playwright-mcp
No newline at end of file
14.playwright-mcp
15.codanna
No newline at end of file
Modified Cargo.lock Cargo.lock +85 −338 View Cargo.lock
@@ -15,66 +15,6 @@
1515checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
1616
1717[[package]]
18name = "admin"
19version = "1.0.0"
20dependencies = [
21 "auth",
22 "ci",
23 "config",
24 "export",
25 "git",
26 "imaging",
27 "mcp-api",
28 "models",
29 "redis",
30 "reqwest 0.12.28",
31 "sea-orm",
32 "serde",
33 "serde_json",
34 "storage",
35 "time 0.3.55",
36 "tokio",
37 "tracing",
38 "utoipa",
39 "uuid",
40]
41
42[[package]]
43name = "admin-rpc"
44version = "1.0.0"
45dependencies = [
46 "admin",
47 "config",
48 "export",
49 "models",
50 "prost 0.14.4",
51 "serde",
52 "serde_json",
53 "time 0.3.55",
54 "tokio",
55 "tonic",
56 "tonic-prost",
57 "tonic-prost-build",
58 "tracing",
59 "uuid",
60]
61
62[[package]]
63name = "adminhost"
64version = "1.0.0"
65dependencies = [
66 "admin",
67 "admin-rpc",
68 "anyhow",
69 "axum",
70 "config",
71 "export",
72 "tokio",
73 "tower-http 0.7.0",
74 "tracing",
75]
76
77[[package]]
7818name = "aead"
7919version = "0.5.2"
8020source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,6 +106,7 @@
166106 "futures",
167107 "git",
168108 "i18n",
109 "kenauth",
169110 "models",
170111 "redis",
171112 "reqwest 0.12.28",
@@ -190,13 +131,13 @@
190131 "config",
191132 "export",
192133 "git",
134 "jwt",
193135 "mcp-api",
194136 "models",
195137 "sandbox-rpc",
196138 "sea-orm",
197139 "serde",
198140 "serde_json",
199 "session-layer",
200141 "space",
201142 "time 0.3.55",
202143 "tracing",
@@ -315,8 +256,8 @@
315256 "chat",
316257 "config",
317258 "export",
259 "jwt",
318260 "push",
319 "session-layer",
320261 "tokio",
321262 "tokio-util",
322263 "tracing",
@@ -745,23 +686,23 @@
745686 "hkdf 0.13.0",
746687 "hmac 0.13.0",
747688 "i18n",
689 "jwt",
690 "kenauth",
748691 "maud",
749692 "models",
750 "pow-solver",
751693 "pulldown-cmark",
752694 "rand 0.8.7",
753695 "redis",
696 "reqwest 0.12.28",
754697 "sea-orm",
755698 "serde",
756699 "serde_json",
757 "session-layer",
758 "sha1 0.11.0",
759700 "sha2 0.11.0",
760701 "space",
761 "ssh-key 0.6.7",
762702 "time 0.3.55",
763703 "tokio",
764704 "tracing",
705 "url 2.5.8",
765706 "utoipa",
766707 "uuid",
767708]
@@ -1294,6 +1235,7 @@
12941235 "http 0.2.12",
12951236 "i18n",
12961237 "imaging",
1238 "jwt",
12971239 "livekit-api",
12981240 "livekit-protocol",
12991241 "models",
@@ -1304,7 +1246,6 @@
13041246 "search",
13051247 "serde",
13061248 "serde_json",
1307 "session-layer",
13081249 "sha2 0.11.0",
13091250 "social",
13101251 "space",
@@ -1339,7 +1280,6 @@
13391280 "aes-gcm 0.10.3",
13401281 "anyhow",
13411282 "async-trait",
1342 "auth",
13431283 "axum",
13441284 "base64 0.23.1",
13451285 "ci-protocol",
@@ -1350,6 +1290,7 @@
13501290 "gitbaby",
13511291 "globset",
13521292 "hmac 0.13.0",
1293 "jwt",
13531294 "models",
13541295 "rand 0.8.7",
13551296 "reqwest 0.12.28",
@@ -1358,7 +1299,6 @@
13581299 "serde",
13591300 "serde_json",
13601301 "serde_yaml_ng",
1361 "session-layer",
13621302 "sha2 0.11.0",
13631303 "space",
13641304 "storage",
@@ -1377,8 +1317,6 @@
13771317 "globset",
13781318 "serde",
13791319 "serde_json",
1380 "serde_yaml_ng",
1381 "thiserror 2.0.20",
13821320 "time 0.3.55",
13831321 "utoipa",
13841322 "uuid",
@@ -1557,21 +1495,6 @@
15571495checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
15581496
15591497[[package]]
1560name = "cookie"
1561version = "0.18.2"
1562source = "registry+https://github.com/rust-lang/crates.io-index"
1563checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87"
1564dependencies = [
1565 "aes-gcm 0.10.3",
1566 "base64 0.22.1",
1567 "percent-encoding 2.3.2",
1568 "rand 0.8.7",
1569 "subtle",
1570 "time 0.3.55",
1571 "version_check 0.9.5",
1572]
1573
1574[[package]]
15751498name = "core-foundation"
15761499version = "0.10.1"
15771500source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1775,17 +1698,6 @@
17751698dependencies = [
17761699 "cmov",
17771700 "subtle",
1778]
1779
1780[[package]]
1781name = "cuckoofilter"
1782version = "0.5.0"
1783source = "registry+https://github.com/rust-lang/crates.io-index"
1784checksum = "b810a8449931679f64cd7eef1bbd0fa315801b6d5d9cdc1ace2804d6529eee18"
1785dependencies = [
1786 "byteorder",
1787 "fnv",
1788 "rand 0.7.3",
17891701]
17901702
17911703[[package]]
@@ -2456,12 +2368,11 @@
24562368 "email",
24572369 "http-body-util",
24582370 "imaging",
2459 "jsonwebtoken 9.3.1",
2371 "kenauth",
24602372 "opentelemetry",
24612373 "opentelemetry-http",
24622374 "opentelemetry-otlp",
24632375 "opentelemetry_sdk",
2464 "rand 0.8.7",
24652376 "redis",
24662377 "reqwest 0.12.28",
24672378 "sea-orm",
@@ -2501,18 +2412,6 @@
25012412]
25022413
25032414[[package]]
2504name = "fastbloom-rs"
2505version = "0.5.10"
2506source = "registry+https://github.com/rust-lang/crates.io-index"
2507checksum = "1cedd8cf9a34b304cfa127370e66b51f85c412cf1a9cc15ec39fc1fe2912403a"
2508dependencies = [
2509 "cuckoofilter",
2510 "fastmurmur3",
2511 "xorfilter-rs",
2512 "xxhash-rust",
2513]
2514
2515[[package]]
25162415name = "faster-hex"
25172416version = "0.10.0"
25182417source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2521,12 +2420,6 @@
25212420 "heapless",
25222421 "serde",
25232422]
2524
2525[[package]]
2526name = "fastmurmur3"
2527version = "0.2.0"
2528source = "registry+https://github.com/rust-lang/crates.io-index"
2529checksum = "2d7e9bc68be4cdabbb8938140b01a8b5bc1191937f2c7e7ecc2fcebbe2d749df"
25302423
25312424[[package]]
25322425name = "fastrand"
@@ -2679,16 +2572,6 @@
26792572]
26802573
26812574[[package]]
2682name = "forwarded-header-value"
2683version = "0.1.1"
2684source = "registry+https://github.com/rust-lang/crates.io-index"
2685checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
2686dependencies = [
2687 "nonempty 0.7.0",
2688 "thiserror 1.0.69",
2689]
2690
2691[[package]]
26922575name = "foyer"
26932576version = "0.22.3"
26942577source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2950,17 +2833,6 @@
29502833 "generic-array 0.14.9",
29512834 "rustversion",
29522835 "typenum",
2953]
2954
2955[[package]]
2956name = "getrandom"
2957version = "0.1.16"
2958source = "registry+https://github.com/rust-lang/crates.io-index"
2959checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
2960dependencies = [
2961 "cfg-if",
2962 "libc",
2963 "wasi 0.9.0+wasi-snapshot-preview1",
29642836]
29652837
29662838[[package]]
@@ -3051,6 +2923,7 @@
30512923 "globset",
30522924 "hayro",
30532925 "http-body-util",
2926 "jwt",
30542927 "maud",
30552928 "models",
30562929 "pulldown-cmark",
@@ -3061,10 +2934,8 @@
30612934 "search",
30622935 "serde",
30632936 "serde_json",
3064 "session-layer",
30652937 "sha2 0.11.0",
30662938 "space",
3067 "ssh-key 0.6.7",
30682939 "thiserror 2.0.20",
30692940 "time 0.3.55",
30702941 "tokio",
@@ -3157,7 +3028,7 @@
31573028 "gix-worktree-state",
31583029 "gix-worktree-stream",
31593030 "gix-zlib",
3160 "nonempty 0.12.0",
3031 "nonempty",
31613032 "parking_lot",
31623033 "regex",
31633034 "serde",
@@ -3271,7 +3142,7 @@
32713142 "gix-error",
32723143 "gix-hash",
32733144 "memmap2",
3274 "nonempty 0.12.0",
3145 "nonempty",
32753146 "serde",
32763147]
32773148
@@ -3520,7 +3391,7 @@
35203391checksum = "1c91d8cffac8849493a82233811bd02b2b183b8cf39bf704de0fa0841b737595"
35213392dependencies = [
35223393 "bstr",
3523 "hashbrown 0.16.1",
3394 "hashbrown 0.17.1",
35243395]
35253396
35263397[[package]]
@@ -3722,7 +3593,7 @@
37223593 "gix-shallow",
37233594 "gix-transport",
37243595 "gix-utils",
3725 "nonempty 0.12.0",
3596 "nonempty",
37263597 "serde",
37273598 "thiserror 2.0.20",
37283599]
@@ -3791,7 +3662,7 @@
37913662 "gix-object",
37923663 "gix-revwalk",
37933664 "gix-trace",
3794 "nonempty 0.12.0",
3665 "nonempty",
37953666 "serde",
37963667]
37973668
@@ -3833,7 +3704,7 @@
38333704 "bstr",
38343705 "gix-hash",
38353706 "gix-lock",
3836 "nonempty 0.12.0",
3707 "nonempty",
38373708 "serde",
38383709 "thiserror 2.0.20",
38393710]
@@ -4803,11 +4674,11 @@
48034674 "http-body-util",
48044675 "hyper 1.11.0",
48054676 "hyper-util",
4677 "jwt",
48064678 "redis",
48074679 "sea-orm",
48084680 "serde",
48094681 "serde_json",
4810 "session-layer",
48114682 "time 0.3.55",
48124683 "tokio",
48134684 "tokio-tungstenite",
@@ -4865,12 +4736,12 @@
48654736 "config",
48664737 "export",
48674738 "git",
4739 "jwt",
48684740 "models",
48694741 "reqwest 0.12.28",
48704742 "sea-orm",
48714743 "serde",
48724744 "serde_json",
4873 "session-layer",
48744745 "thiserror 2.0.20",
48754746 "time 0.3.55",
48764747 "tokio",
@@ -4976,12 +4847,12 @@
49764847 "export",
49774848 "http 0.2.12",
49784849 "i18n",
4850 "jwt",
49794851 "models",
49804852 "sea-orm",
49814853 "search",
49824854 "serde",
49834855 "serde_json",
4984 "session-layer",
49854856 "space",
49864857 "time 0.3.55",
49874858 "tracing",
@@ -5180,6 +5051,23 @@
51805051]
51815052
51825053[[package]]
5054name = "jwt"
5055version = "1.0.0"
5056dependencies = [
5057 "axum",
5058 "base64 0.23.1",
5059 "config",
5060 "jsonwebtoken 9.3.1",
5061 "reqwest 0.12.28",
5062 "serde",
5063 "serde_json",
5064 "sha2 0.11.0",
5065 "tokio",
5066 "tracing",
5067 "uuid",
5068]
5069
5070[[package]]
51835071name = "jwt-simple"
51845072version = "0.12.17"
51855073source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5240,6 +5128,23 @@
52405128]
52415129
52425130[[package]]
5131name = "kenauth"
5132version = "1.0.0"
5133dependencies = [
5134 "config",
5135 "prost 0.14.4",
5136 "prost-types 0.14.1",
5137 "thiserror 2.0.20",
5138 "tokio",
5139 "tonic",
5140 "tonic-health",
5141 "tonic-prost",
5142 "tonic-prost-build",
5143 "tracing",
5144 "uuid",
5145]
5146
5147[[package]]
52435148name = "kurbo"
52445149version = "0.13.1"
52455150source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5947,12 +5852,6 @@
59475852
59485853[[package]]
59495854name = "nonempty"
5950version = "0.7.0"
5951source = "registry+https://github.com/rust-lang/crates.io-index"
5952checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
5953
5954[[package]]
5955name = "nonempty"
59565855version = "0.12.0"
59575856source = "registry+https://github.com/rust-lang/crates.io-index"
59585857checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6"
@@ -6492,20 +6391,6 @@
64926391
64936392[[package]]
64946393name = "p521"
6495version = "0.13.3"
6496source = "registry+https://github.com/rust-lang/crates.io-index"
6497checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
6498dependencies = [
6499 "base16ct 0.2.0",
6500 "ecdsa 0.16.9",
6501 "elliptic-curve 0.13.8",
6502 "primeorder 0.13.6",
6503 "rand_core 0.6.4",
6504 "sha2 0.10.9",
6505]
6506
6507[[package]]
6508name = "p521"
65096394version = "0.14.0"
65106395source = "registry+https://github.com/rust-lang/crates.io-index"
65116396checksum = "4ad64cc32c2dc466317c12ee5853e61f159f9eab1fe7efade0395dc2e7b43449"
@@ -7046,15 +6931,6 @@
70466931]
70476932
70486933[[package]]
7049name = "pow-solver"
7050version = "1.0.0"
7051dependencies = [
7052 "serde",
7053 "sha2 0.11.0",
7054 "wasm-bindgen",
7055]
7056
7057[[package]]
70586934name = "powerfmt"
70596935version = "0.2.0"
70606936source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7324,12 +7200,12 @@
73247200 "git",
73257201 "globset",
73267202 "issues",
7203 "jwt",
73277204 "models",
73287205 "sea-orm",
73297206 "search",
73307207 "serde",
73317208 "serde_json",
7332 "session-layer",
73337209 "space",
73347210 "time 0.3.55",
73357211 "tracing",
@@ -7471,19 +7347,6 @@
74717347
74727348[[package]]
74737349name = "rand"
7474version = "0.7.3"
7475source = "registry+https://github.com/rust-lang/crates.io-index"
7476checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
7477dependencies = [
7478 "getrandom 0.1.16",
7479 "libc",
7480 "rand_chacha 0.2.2",
7481 "rand_core 0.5.1",
7482 "rand_hc",
7483]
7484
7485[[package]]
7486name = "rand"
74877350version = "0.8.7"
74887351source = "registry+https://github.com/rust-lang/crates.io-index"
74897352checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
@@ -7516,16 +7379,6 @@
75167379
75177380[[package]]
75187381name = "rand_chacha"
7519version = "0.2.2"
7520source = "registry+https://github.com/rust-lang/crates.io-index"
7521checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
7522dependencies = [
7523 "ppv-lite86",
7524 "rand_core 0.5.1",
7525]
7526
7527[[package]]
7528name = "rand_chacha"
75297382version = "0.3.1"
75307383source = "registry+https://github.com/rust-lang/crates.io-index"
75317384checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
@@ -7546,15 +7399,6 @@
75467399
75477400[[package]]
75487401name = "rand_core"
7549version = "0.5.1"
7550source = "registry+https://github.com/rust-lang/crates.io-index"
7551checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
7552dependencies = [
7553 "getrandom 0.1.16",
7554]
7555
7556[[package]]
7557name = "rand_core"
75587402version = "0.6.4"
75597403source = "registry+https://github.com/rust-lang/crates.io-index"
75607404checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
@@ -7578,15 +7422,6 @@
75787422checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
75797423
75807424[[package]]
7581name = "rand_hc"
7582version = "0.2.0"
7583source = "registry+https://github.com/rust-lang/crates.io-index"
7584checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
7585dependencies = [
7586 "rand_core 0.5.1",
7587]
7588
7589[[package]]
75907425name = "rand_pcg"
75917426version = "0.10.2"
75927427source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8002,7 +7837,7 @@
80027837 "num-bigint 0.4.8",
80037838 "p256 0.14.0",
80047839 "p384 0.14.0",
8005 "p521 0.14.0",
7840 "p521",
80067841 "pageant",
80077842 "pbkdf2",
80087843 "pkcs1 0.8.0-rc.4",
@@ -8022,8 +7857,8 @@
80227857 "sha3 0.12.0",
80237858 "signature 3.0.0",
80247859 "spki 0.8.0",
8025 "ssh-encoding 0.3.0",
8026 "ssh-key 0.7.0-rc.11",
7860 "ssh-encoding",
7861 "ssh-key",
80277862 "subtle",
80287863 "thiserror 2.0.20",
80297864 "tokio",
@@ -8040,7 +7875,7 @@
80407875dependencies = [
80417876 "log 0.4.33",
80427877 "nix 0.31.3",
8043 "ssh-encoding 0.3.0",
7878 "ssh-encoding",
80447879 "windows-sys 0.61.2",
80457880]
80467881
@@ -8740,56 +8575,6 @@
87408575]
87418576
87428577[[package]]
8743name = "session"
8744version = "0.21.0"
8745dependencies = [
8746 "aes-gcm 0.11.0",
8747 "async-trait",
8748 "axum",
8749 "base64 0.23.1",
8750 "bytes",
8751 "chrono",
8752 "cookie",
8753 "dashmap",
8754 "fastbloom-rs",
8755 "forwarded-header-value",
8756 "futures",
8757 "hmac 0.13.0",
8758 "http 1.5.0",
8759 "http-body 1.1.0",
8760 "rand 0.10.2",
8761 "serde",
8762 "serde_json",
8763 "sha2 0.11.0",
8764 "thiserror 2.0.20",
8765 "tokio",
8766 "tower-layer",
8767 "tower-service",
8768 "tracing",
8769 "uuid",
8770]
8771
8772[[package]]
8773name = "session-layer"
8774version = "1.0.0"
8775dependencies = [
8776 "async-trait",
8777 "axum",
8778 "base64 0.23.1",
8779 "chrono",
8780 "config",
8781 "export",
8782 "models",
8783 "redis",
8784 "sea-orm",
8785 "serde",
8786 "session",
8787 "time 0.3.55",
8788 "tracing",
8789 "uuid",
8790]
8791
8792[[package]]
87938578name = "sha1"
87948579version = "0.10.7"
87958580source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -9057,13 +8842,15 @@
90578842 "export",
90588843 "i18n",
90598844 "imaging",
8845 "jwt",
8846 "kenauth",
90608847 "models",
8848 "prost-types 0.14.1",
90618849 "rand 0.8.7",
90628850 "sea-orm",
90638851 "search",
90648852 "serde",
90658853 "serde_json",
9066 "session-layer",
90678854 "storage",
90688855 "time 0.3.55",
90698856 "tracing",
@@ -9308,16 +9095,6 @@
93089095
93099096[[package]]
93109097name = "ssh-cipher"
9311version = "0.2.0"
9312source = "registry+https://github.com/rust-lang/crates.io-index"
9313checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
9314dependencies = [
9315 "cipher 0.4.4",
9316 "ssh-encoding 0.2.0",
9317]
9318
9319[[package]]
9320name = "ssh-cipher"
93219098version = "0.3.0"
93229099source = "registry+https://github.com/rust-lang/crates.io-index"
93239100checksum = "d801accda99469cde6d73da741422610fdf6508a72d9a69d1b55cb241c720597"
@@ -9330,23 +9107,12 @@
93309107 "ctutils",
93319108 "des",
93329109 "poly1305",
9333 "ssh-encoding 0.3.0",
9110 "ssh-encoding",
93349111 "zeroize",
93359112]
93369113
93379114[[package]]
93389115name = "ssh-encoding"
9339version = "0.2.0"
9340source = "registry+https://github.com/rust-lang/crates.io-index"
9341checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15"
9342dependencies = [
9343 "base64ct",
9344 "pem-rfc7468 0.7.0",
9345 "sha2 0.10.9",
9346]
9347
9348[[package]]
9349name = "ssh-encoding"
93509116version = "0.3.0"
93519117source = "registry+https://github.com/rust-lang/crates.io-index"
93529118checksum = "7b54d0ed0498daf3f78d82e00e28c8eec9d75a067c4cfbcc7a0f7d0f4077749e"
@@ -9362,26 +9128,6 @@
93629128
93639129[[package]]
93649130name = "ssh-key"
9365version = "0.6.7"
9366source = "registry+https://github.com/rust-lang/crates.io-index"
9367checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3"
9368dependencies = [
9369 "p256 0.13.2",
9370 "p384 0.13.1",
9371 "p521 0.13.3",
9372 "rand_core 0.6.4",
9373 "rsa 0.9.10",
9374 "sec1 0.7.3",
9375 "sha2 0.10.9",
9376 "signature 2.2.0",
9377 "ssh-cipher 0.2.0",
9378 "ssh-encoding 0.2.0",
9379 "subtle",
9380 "zeroize",
9381]
9382
9383[[package]]
9384name = "ssh-key"
93859131version = "0.7.0-rc.11"
93869132source = "registry+https://github.com/rust-lang/crates.io-index"
93879133checksum = "f9a32fae177b74a22aa9c5b01bf7e68b33545be32d9e381e248058d2adc15ce3"
@@ -9394,15 +9140,15 @@
93949140 "hmac 0.13.0",
93959141 "p256 0.14.0",
93969142 "p384 0.14.0",
9397 "p521 0.14.0",
9143 "p521",
93989144 "rand_core 0.10.1",
93999145 "rsa 0.10.0-rc.18",
94009146 "sec1 0.8.1",
94019147 "sha1 0.11.0",
94029148 "sha2 0.11.0",
94039149 "signature 3.0.0",
9404 "ssh-cipher 0.3.0",
9405 "ssh-encoding 0.3.0",
9150 "ssh-cipher",
9151 "ssh-encoding",
94069152 "zeroize",
94079153]
94089154
@@ -9607,7 +9353,7 @@
96079353checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
96089354dependencies = [
96099355 "fastrand",
9610 "getrandom 0.3.4",
9356 "getrandom 0.4.3",
96119357 "once_cell",
96129358 "rustix",
96139359 "windows-sys 0.61.2",
@@ -9746,7 +9492,6 @@
97469492 "bytes",
97479493 "libc",
97489494 "mio",
9749 "parking_lot",
97509495 "pin-project-lite",
97519496 "signal-hook-registry",
97529497 "socket2 0.6.5",
@@ -9794,6 +9539,7 @@
97949539 "futures-core",
97959540 "pin-project-lite",
97969541 "tokio",
9542 "tokio-util",
97979543]
97989544
97999545[[package]]
@@ -9939,6 +9685,19 @@
99399685]
99409686
99419687[[package]]
9688name = "tonic-health"
9689version = "0.14.5"
9690source = "registry+https://github.com/rust-lang/crates.io-index"
9691checksum = "f4ff0636fef47afb3ec02818f5bceb4377b8abb9d6a386aeade18bd6212f8eb7"
9692dependencies = [
9693 "prost 0.14.4",
9694 "tokio",
9695 "tokio-stream",
9696 "tonic",
9697 "tonic-prost",
9698]
9699
9700[[package]]
99429701name = "tonic-prost"
99439702version = "0.14.5"
99449703source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -10252,7 +10011,7 @@
1025210011source = "registry+https://github.com/rust-lang/crates.io-index"
1025310012checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9"
1025410013dependencies = [
10255 "rand 0.9.5",
10014 "rand 0.10.2",
1025610015]
1025710016
1025810017[[package]]
@@ -10419,13 +10178,13 @@
1041910178 "futures",
1042010179 "i18n",
1042110180 "imaging",
10181 "jwt",
1042210182 "models",
1042310183 "rand 0.8.7",
1042410184 "redis",
1042510185 "sea-orm",
1042610186 "serde",
1042710187 "serde_json",
10428 "session-layer",
1042910188 "sha2 0.11.0",
1043010189 "space",
1043110190 "storage",
@@ -10585,12 +10344,6 @@
1058510344dependencies = [
1058610345 "try-lock",
1058710346]
10588
10589[[package]]
10590name = "wasi"
10591version = "0.9.0+wasi-snapshot-preview1"
10592source = "registry+https://github.com/rust-lang/crates.io-index"
10593checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
1059410347
1059510348[[package]]
1059610349name = "wasi"
@@ -10733,16 +10486,16 @@
1073310486 "git",
1073410487 "import",
1073510488 "issues",
10489 "jwt",
1073610490 "models",
1073710491 "pulls",
1073810492 "sea-orm",
1073910493 "search",
1074010494 "serde",
1074110495 "serde_json",
10742 "session",
10743 "session-layer",
1074410496 "space",
1074510497 "time 0.3.55",
10498 "tokio",
1074610499 "tower-http 0.7.0",
1074710500 "tracing",
1074810501 "user",
@@ -11228,12 +10981,6 @@
1122810981dependencies = [
1122910982 "tap",
1123010983]
11231
11232[[package]]
11233name = "xorfilter-rs"
11234version = "0.5.1"
11235source = "registry+https://github.com/rust-lang/crates.io-index"
11236checksum = "47f9da296a88b6bc150b896d17770a62d4dc6f63ecf0ed10a9c08a1cb3d12f24"
1123710984
1123810985[[package]]
1123910986name = "xxhash-rust"
Modified Cargo.toml Cargo.toml +3 −10 View Cargo.toml
@@ -14,11 +14,8 @@
1414
1515
1616[workspace.dependencies]
17admin = { path = "lib/admin" }
18admin-rpc = { path = "lib/admin-rpc" }
1917agent = { path = "lib/agent" }
2018agents = { path = "lib/agents" }
21aihost = { path = "app/aihost" }
2219auth = { path = "lib/auth" }
2320cache = { path = "lib/cache" }
2421chat = { path = "lib/chat" }
@@ -28,9 +25,7 @@
2825email = { path = "lib/email" }
2926export = { path = "lib/export" }
3027models = { path = "lib/models" }
31pow-solver = { path = "lib/pow-solver" }
3228push = { path = "lib/push" }
33session-layer = { path = "lib/session-layer" }
3429storage = { path = "lib/storage" }
3530user = { path = "lib/user" }
3631space = { path = "lib/space" }
@@ -38,6 +33,8 @@
3833git = { path = "lib/git" }
3934i18n = { path = "lib/i18n" }
4035imaging = { path = "lib/imaging" }
36kenauth = { path = "lib/kenauth" }
37jwt = { path = "lib/jwt" }
4138import = { path = "lib/import" }
4239webapi = { path = "lib/webapi" }
4340mcp-api = { path = "lib/mcp-api" }
@@ -53,7 +50,6 @@
5350serde = "1.0.229"
5451serde_json = "1.0.151"
5552axum = { version = "0.8.9", features = ["ws"] }
56session = { path = "lib/session", features = ["key-store"] }
5753tower = "0.5.3"
5854tower-http = { version = "0.7.0", features = ["trace", "limit", "cors"] }
5955axum-otel-metrics = "0.14.1"
@@ -80,8 +76,6 @@
8076hmac = "0.13"
8177hkdf = "0.13"
8278uuid = { version = "1", features = ["v7", "serde"] }
83hex = "0.4"
84smol_str = { version = "0.3", features = ["serde"] }
8579argon2 = "0.6.0"
8680aes-gcm = "0.10"
8781time = { version = "0.3", features = ["serde", "serde-human-readable"] }
@@ -101,8 +95,6 @@
10195gitbaby = { path = "lib/gitbaby" }
10296foyer = { version = "0.22.3", features = ["runtime-tokio"] }
10397hayro = { version = "0.7.1", features = ["embed-fonts", "embed-cmaps"] }
104wasm-bindgen = "0.2"
105chrono = "0.4.45"
10698maud = { version = "0.27", features = ["axum"] }
10799pulldown-cmark = { version = "0.13", default-features = false, features = ["html"] }
108100lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1-rustls", "ring", "rustls-native-certs", "builder"] }
@@ -136,4 +128,5 @@
136128tonic = { version = "0.14", default-features = false, features = ["channel", "codegen", "router", "server"] }
137129tonic-prost = "0.14"
138130prost = "0.14"
131prost-types = "0.14"
139132tonic-prost-build = "0.14"
Modified docker-compose.dev.yml docker-compose.dev.yml +33 −0 View docker-compose.dev.yml
@@ -80,6 +80,39 @@
8080 timeout: 3s
8181 retries: 20
8282
83 # NATS:KenOAuth 的事件与缓存失效通道(本地开发必需)
84 nats:
85 image: nats:2-alpine
86 container_name: gitdata-dev-nats
87 restart: unless-stopped
88 command: ["-js", "-m", "8222"]
89 ports:
90 - "127.0.0.1:4222:4222"
91 - "127.0.0.1:8222:8222"
92 healthcheck:
93 test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8222/healthz >/dev/null || exit 1"]
94 interval: 5s
95 timeout: 3s
96 retries: 20
97
98 # KenOAuth 认证中心。image 由外部构建;配置从 config/oauth.yaml 只读挂载。
99 # `just infra` 默认不启动,需要时:docker compose -f docker-compose.dev.yml --profile oauth up -d oauth
100 oauth:
101 profiles: ["oauth"]
102 image: harbor.gitdata.me/app/kenoauth:latest
103 container_name: gitdata-dev-oauth
104 restart: unless-stopped
105 network_mode: host
106 volumes:
107 - ./config/oauth.yaml:/app/config/config.yaml:ro
108 depends_on:
109 postgres:
110 condition: service_healthy
111 redis:
112 condition: service_healthy
113 nats:
114 condition: service_started
115
83116 # 本地 S3 bucket 初始化(建出 config/local.toml 里声明的 bucket)
84117 minio-init:
85118 image: minio/mc:latest
Modified Dockerfile Dockerfile +3 −3 View Dockerfile
@@ -43,7 +43,7 @@
4343 /build/target/release/deps/web-* 2>/dev/null; \
4444 cargo build --release --workspace --bins && \
4545 mkdir -p /out && \
46 for b in apihost githost synchost searchhost mails migrate openapi im adminhost webhook runner aihost mcp; do \
46 for b in apihost githost synchost searchhost mails migrate openapi im webhook runner aihost mcp; do \
4747 cp "target/release/$b" /out/; \
4848 done
4949
@@ -79,7 +79,7 @@
7979WORKDIR /app
8080
8181COPY --chown=gitdata:gitdata config/default.toml config/default.toml
82COPY --from=builder /out/apihost /out/githost /out/synchost /out/searchhost /out/mails /out/migrate /out/openapi /out/im /out/adminhost /out/webhook /out/runner /out/aihost /out/mcp /usr/local/bin/
82COPY --from=builder /out/apihost /out/githost /out/synchost /out/searchhost /out/mails /out/migrate /out/openapi /out/im /out/webhook /out/runner /out/aihost /out/mcp /usr/local/bin/
8383
8484COPY --from=builder /out/docker /usr/local/bin/docker
8585
@@ -90,7 +90,7 @@
9090
9191USER gitdata
9292
93EXPOSE 3000 9418 22 3001 3002 3003 3004 3005 3006 3007 3014
93EXPOSE 3000 9418 22 3001 3002 3003 3005 3006 3007
9494
9595CMD ["apihost"]
9696
Modified justfile justfile +1 −2 View justfile
@@ -60,7 +60,7 @@
6060up *svc='':
6161 @{{ invoke }}"{{ dev }}" up {{ svc }}
6262
63# 仅启动后端服务(apihost/githost/im/synchost/mails/adminhost/webhook/aihost)
63# 仅启动后端服务(apihost/githost/im/synchost/mails/webhook/aihost)
6464up-backend:
6565 @{{ invoke }}"{{ dev }}" up backends
6666
@@ -99,7 +99,6 @@
9999 @echo " im ws://127.0.0.1:4082"
100100 @echo " synchost http://127.0.0.1:4083/healthz"
101101 @echo " mails http://127.0.0.1:4084/healthz"
102 @echo " adminhost http://127.0.0.1:3004 (gRPC: 127.0.0.1:3014)"
103102 @echo " webhook http://127.0.0.1:3005"
104103 @echo " aihost http://127.0.0.1:3006/healthz"
105104 @echo " mcp http://127.0.0.1:3007/v1 (healthz: /healthz)"
Modified openapi.json openapi.json View openapi.json
Diff too large to display.