feat(api): phase3f+3g pipelines persistence with query endpoints, account deletion executor
ZhenYi committed
Modified app/apihost/src/main.rs app/apihost/src/main.rs +1 −0 View app/apihost/src/main.rs
@@ -28,6 +28,7 @@
2828 .map_err(|err| anyhow::anyhow!("failed to init meter provider: {err}"))?;
2929
3030 tokio::spawn(chat::worker::run_background_tasks(state.clone()));
31 tokio::spawn(auth::worker::run_deletion_executor(state.clone()));
3132
3233 let api_router: Router = router(state)
3334 .await
Modified app/apihost/Cargo.toml app/apihost/Cargo.toml +1 −0 View app/apihost/Cargo.toml
@@ -6,6 +6,7 @@
66
77[dependencies]
88webapi = { workspace = true }
9auth = { workspace = true }
910chat = { workspace = true }
1011export = { workspace = true }
1112session = { workspace = true }
Modified lib/auth/src/lib.rs lib/auth/src/lib.rs +1 −0 View lib/auth/src/lib.rs
@@ -6,3 +6,4 @@
66pub mod services;
77pub mod share;
88pub mod templates;
9pub mod worker;
Added lib/auth/src/worker.rs lib/auth/src/worker.rs +143 −0 View lib/auth/src/worker.rs
@@ -0,0 +1,143 @@
1use std::time::Duration;
2
3use export::AppState;
4use models::users::user_access_keys;
5use models::users::user_deletion_requests::{self, UserDeletionRequestStatus};
6use models::users::user_emails;
7use models::users::user_recovery_codes;
8use models::users::user_sessions;
9use models::users::user_ssh_keys;
10use models::users::{user, user_profiles};
11use sea_orm::sea_query::Expr;
12use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, Set};
13use time::OffsetDateTime;
14
15use crate::services::AuthService;
16
17const CHECK_INTERVAL_SECS: u64 = 3600;
18
19impl AuthService {
20 pub async fn execute_due_deletions(&self) -> Result<u64, crate::errors::AuthError> {
21 let db = self.db();
22 let now = OffsetDateTime::now_utc();
23 let due = user_deletion_requests::Entity::find()
24 .filter(user_deletion_requests::Column::Status.eq(UserDeletionRequestStatus::Scheduled))
25 .filter(user_deletion_requests::Column::ScheduledFor.lte(now))
26 .all(db.read())
27 .await?;
28
29 let mut executed = 0u64;
30 for request in due {
31 let uid = request.user_id;
32 let Some(user_row) = user::Entity::find_by_id(uid).one(db.write()).await? else {
33 let mut am: user_deletion_requests::ActiveModel = request.into();
34 am.status = Set(UserDeletionRequestStatus::Completed);
35 am.completed_at = Set(Some(now));
36 am.update(db.write()).await?;
37 executed += 1;
38 continue;
39 };
40
41 let anon_name = format!("deleted-{}", &uid.simple().to_string()[..8]);
42 let mut active = user_row.into_active_model();
43 active.username = Set(anon_name.clone());
44 active.lower_name = Set(anon_name.clone());
45 active.display_name = Set("deleted user".to_string());
46 active.avatar_url = Set(String::new());
47 active.is_active = Set(false);
48 active.prohibit_login = Set(true);
49 active.passwd_hash = Set(None);
50 active.password_last_changed_at = Set(None);
51 active.passwd_version = Set(Some(OffsetDateTime::now_utc().unix_timestamp()));
52 active.totp_encrypted_secret = Set(None);
53 active.totp_enabled = Set(false);
54 active.timezone = Set(None);
55 active.locale = Set(None);
56 active.language = Set(None);
57 active.updated_at = Set(now);
58 active.update(db.write()).await?;
59
60 if let Some(profile) = user_profiles::Entity::find_by_id(uid)
61 .one(db.write())
62 .await?
63 {
64 let mut am: user_profiles::ActiveModel = profile.into();
65 am.real_name = Set(None);
66 am.bio = Set(None);
67 am.pronouns = Set(None);
68 am.website = Set(None);
69 am.location = Set(None);
70 am.company = Set(None);
71 am.date_of_birth = Set(None);
72 am.banner_url = Set(None);
73 am.banner_hash = Set(None);
74 am.banner_color = Set(None);
75 am.accent_color = Set(None);
76 am.custom_status_emoji = Set(None);
77 am.custom_status_text = Set(None);
78 am.custom_status_expires_at = Set(None);
79 am.updated_at = Set(now);
80 am.update(db.write()).await?;
81 }
82
83 user_emails::Entity::update_many()
84 .col_expr(
85 user_emails::Column::Email,
86 Expr::value(format!("{anon_name}@deleted.invalid")),
87 )
88 .col_expr(
89 user_emails::Column::LowerEmail,
90 Expr::value(format!("{anon_name}@deleted.invalid")),
91 )
92 .col_expr(user_emails::Column::DeletedAt, Expr::value(Some(now)))
93 .col_expr(user_emails::Column::UpdatedAt, Expr::value(now))
94 .filter(user_emails::Column::UserId.eq(uid))
95 .exec(db.write())
96 .await?;
97
98 user_sessions::Entity::update_many()
99 .col_expr(user_sessions::Column::IsRevoked, Expr::value(true))
100 .col_expr(user_sessions::Column::RevokedAt, Expr::value(Some(now)))
101 .col_expr(user_sessions::Column::UpdatedAt, Expr::value(now))
102 .filter(user_sessions::Column::UserId.eq(uid))
103 .filter(user_sessions::Column::IsRevoked.eq(false))
104 .exec(db.write())
105 .await?;
106
107 user_access_keys::Entity::delete_many()
108 .filter(user_access_keys::Column::UserId.eq(uid))
109 .exec(db.write())
110 .await?;
111 user_ssh_keys::Entity::delete_many()
112 .filter(user_ssh_keys::Column::UserId.eq(uid))
113 .exec(db.write())
114 .await?;
115 user_recovery_codes::Entity::delete_many()
116 .filter(user_recovery_codes::Column::UserId.eq(uid))
117 .exec(db.write())
118 .await?;
119
120 let mut am: user_deletion_requests::ActiveModel = request.into();
121 am.status = Set(UserDeletionRequestStatus::Completed);
122 am.completed_at = Set(Some(now));
123 am.update(db.write()).await?;
124
125 executed += 1;
126 }
127 Ok(executed)
128 }
129}
130
131pub async fn run_deletion_executor(state: AppState) {
132 let service = AuthService::new(state);
133 let mut interval = tokio::time::interval(Duration::from_secs(CHECK_INTERVAL_SECS));
134 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
135 loop {
136 interval.tick().await;
137 match service.execute_due_deletions().await {
138 Ok(0) => {}
139 Ok(n) => tracing::info!(count = n, "executed due account deletions"),
140 Err(error) => tracing::warn!(%error, "account deletion executor failed"),
141 }
142 }
143}
Modified lib/auth/Cargo.toml lib/auth/Cargo.toml +1 −0 View lib/auth/Cargo.toml
@@ -12,6 +12,7 @@
1212pow-solver = { workspace = true }
1313axum = { workspace = true }
1414sea-orm = { workspace = true }
15tokio = { workspace = true }
1516redis = { workspace = true }
1617serde = { workspace = true, features = ["derive"] }
1718utoipa = { workspace = true }
Added lib/git/src/handler/pipelines/get_run.rs lib/git/src/handler/pipelines/get_run.rs +44 −0 View lib/git/src/handler/pipelines/get_run.rs
@@ -0,0 +1,44 @@
1use axum::extract::{Path, State};
2use export::rest::AppResponseData;
3use session::Session;
4
5use crate::errors::{ApiError, GitApiError};
6use crate::handler::ensure_read_access;
7use crate::handler::RepoPath;
8use crate::service::GitService;
9use crate::service::pipelines::PipelineRunDto;
10use crate::share::errors::GitError;
11
12#[utoipa::path(
13 get,
14 path = "/repos/{space_name}/{repo_name}/pipelines/{job_id}",
15 tag = "pipelines",
16 params(
17 ("space_name" = String, Path),
18 ("repo_name" = String, Path),
19 ("job_id" = String, Path, description = "pipeline run id")
20 ),
21 responses(
22 (status = 200, description = "pipeline run detail", body = AppResponseData<PipelineRunDto>),
23 (status = 404, description = "not found", body = AppResponseData<export::rest::NoData>)
24 )
25)]
26pub async fn get_run(
27 State(service): State<GitService>,
28 session: Session,
29 Path((space_name, repo_name, job_id)): Path<(String, String, String)>,
30) -> Result<AppResponseData<PipelineRunDto>, ApiError> {
31 let ctx = service
32 .context(space_name.clone(), repo_name.clone())
33 .await
34 .map_err(GitApiError::from)?;
35 ensure_read_access(&service, &session, &ctx).await?;
36 let run = service
37 .get_pipeline_run(job_id)
38 .await
39 .map_err(GitApiError::from)?;
40 if run.repo_id != ctx.model.id {
41 return Err(GitApiError::from(GitError::NotFound).into());
42 }
43 Ok(AppResponseData::ok(run))
44}
Added lib/git/src/handler/pipelines/list_runs.rs lib/git/src/handler/pipelines/list_runs.rs +54 −0 View lib/git/src/handler/pipelines/list_runs.rs
@@ -0,0 +1,54 @@
1use axum::extract::{Path, Query, State};
2use export::rest::AppResponseData;
3use session::Session;
4
5use crate::errors::{ApiError, GitApiError};
6use crate::handler::ensure_read_access;
7use crate::handler::RepoPath;
8use crate::service::GitService;
9use crate::service::pipelines::PipelineRunDto;
10use models::repo::pipeline_run::PipelineRunState;
11
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
13pub struct ListRunsQuery {
14 pub commit: Option<String>,
15 pub branch: Option<String>,
16 pub state: Option<PipelineRunState>,
17 pub limit: Option<u64>,
18 pub offset: Option<u64>,
19}
20
21#[utoipa::path(
22 get,
23 path = "/repos/{space_name}/{repo_name}/pipelines",
24 tag = "pipelines",
25 params(RepoPath, ListRunsQuery),
26 responses(
27 (status = 200, description = "list pipeline runs", body = AppResponseData<Vec<PipelineRunDto>>)
28 )
29)]
30pub async fn list_runs(
31 State(service): State<GitService>,
32 session: Session,
33 Path(path): Path<RepoPath>,
34 Query(query): Query<ListRunsQuery>,
35) -> Result<AppResponseData<Vec<PipelineRunDto>>, ApiError> {
36 let ctx = service
37 .context(path.space_name.clone(), path.repo_name.clone())
38 .await
39 .map_err(GitApiError::from)?;
40 ensure_read_access(&service, &session, &ctx).await?;
41 let runs = service
42 .list_pipeline_runs(
43 path.space_name,
44 path.repo_name,
45 query.commit,
46 query.branch,
47 query.state,
48 query.limit,
49 query.offset,
50 )
51 .await
52 .map_err(GitApiError::from)?;
53 Ok(AppResponseData::ok(runs))
54}
Modified lib/git/src/handler/pipelines/mod.rs lib/git/src/handler/pipelines/mod.rs +3 −0 View lib/git/src/handler/pipelines/mod.rs
@@ -1,3 +1,6 @@
11pub mod cancel;
2pub mod get_run;
3pub mod list_runs;
4pub mod report_run;
25pub mod schedule;
36pub mod trigger;
Added lib/git/src/handler/pipelines/report_run.rs lib/git/src/handler/pipelines/report_run.rs +40 −0 View lib/git/src/handler/pipelines/report_run.rs
@@ -0,0 +1,40 @@
1use axum::Json;
2use axum::extract::{Path, State};
3use export::rest::AppResponseData;
4use models::repo::pipeline_run::PipelineRunState;
5use time::OffsetDateTime;
6
7use crate::errors::{ApiError, GitApiError};
8use crate::service::GitService;
9use crate::service::pipelines::PipelineRunDto;
10
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
12pub struct PipelineReportBody {
13 pub state: PipelineRunState,
14 #[serde(default)]
15 pub started_at: Option<OffsetDateTime>,
16 #[serde(default)]
17 pub finished_at: Option<OffsetDateTime>,
18}
19
20#[utoipa::path(
21 post,
22 path = "/repos/_/pipelines/{job_id}/report",
23 tag = "pipelines",
24 params(("job_id" = String, Path)),
25 request_body = PipelineReportBody,
26 responses(
27 (status = 200, description = "pipeline run updated", body = AppResponseData<PipelineRunDto>)
28 )
29)]
30pub async fn report_run(
31 State(service): State<GitService>,
32 Path(job_id): Path<String>,
33 Json(body): Json<PipelineReportBody>,
34) -> Result<AppResponseData<PipelineRunDto>, ApiError> {
35 let run = service
36 .report_pipeline_run(job_id, body.state, body.started_at, body.finished_at)
37 .await
38 .map_err(GitApiError::from)?;
39 Ok(AppResponseData::ok(run))
40}
Modified lib/git/src/handler/pipelines/schedule.rs lib/git/src/handler/pipelines/schedule.rs +7 −7 View lib/git/src/handler/pipelines/schedule.rs
@@ -6,7 +6,7 @@
66use crate::errors::{ApiError, GitApiError};
77use crate::handler::{RepoPath, require_writer};
88use crate::service::GitService;
9use crate::service::pipelines::JobDto;
9use crate::service::pipelines::{JobDto, TriggerResultDto};
1010
1111#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct PipelineScheduleQuery {
@@ -25,7 +25,7 @@
2525 params(RepoPath, PipelineScheduleQuery),
2626 request_body = PipelineScheduleBody,
2727 responses(
28 (status = 200, description = "schedule pipeline job", body = AppResponseData<NoData>)
28 (status = 200, description = "schedule pipeline job", body = AppResponseData<TriggerResultDto>)
2929 )
3030)]
3131pub async fn schedule(
@@ -34,17 +34,17 @@
3434 Path(path): Path<RepoPath>,
3535 Query(query): Query<PipelineScheduleQuery>,
3636 Json(body): Json<PipelineScheduleBody>,
37) -> Result<AppResponseData<NoData>, ApiError> {
38 require_writer(
37) -> Result<AppResponseData<TriggerResultDto>, ApiError> {
38 let (_ctx, caller) = require_writer(
3939 &service,
4040 &session,
4141 path.space_name.clone(),
4242 path.repo_name.clone(),
4343 )
4444 .await?;
45 service
46 .pipeline_schedule(path.space_name, path.repo_name, query.commit, body.job)
45 let out = service
46 .pipeline_schedule(caller, path.space_name, path.repo_name, query.commit, body.job)
4747 .await
4848 .map_err(GitApiError::from)?;
49 Ok(AppResponseData::ok(NoData))
49 Ok(AppResponseData::ok(out))
5050}
Modified lib/git/src/handler/pipelines/trigger.rs lib/git/src/handler/pipelines/trigger.rs +2 −2 View lib/git/src/handler/pipelines/trigger.rs
@@ -28,7 +28,7 @@
2828 Path(path): Path<RepoPath>,
2929 Query(query): Query<PipelineTriggerQuery>,
3030) -> Result<AppResponseData<TriggerResultDto>, ApiError> {
31 require_writer(
31 let (_ctx, caller) = require_writer(
3232 &service,
3333 &session,
3434 path.space_name.clone(),
@@ -36,7 +36,7 @@
3636 )
3737 .await?;
3838 let out = service
39 .pipeline_trigger(path.space_name, path.repo_name, query.commit, query.branch)
39 .pipeline_trigger(caller, path.space_name, path.repo_name, query.commit, query.branch)
4040 .await
4141 .map_err(GitApiError::from)?;
4242 Ok(AppResponseData::ok(out))
Modified lib/git/src/handler/mod.rs lib/git/src/handler/mod.rs +2 −1 View lib/git/src/handler/mod.rs
@@ -32,5 +32,6 @@
3232pub mod whitelists;
3333
3434pub use share::{
35 RepoPath, RepoPathRev, ensure_caller, require_repo_admin, require_writer, resolve_reader,
35 RepoPath, RepoPathRev, ensure_caller, ensure_read_access, require_repo_admin, require_writer,
36 resolve_reader,
3637};
Modified lib/git/src/service/pipelines.rs lib/git/src/service/pipelines.rs +164 −18 View lib/git/src/service/pipelines.rs
@@ -1,3 +1,5 @@
1use models::repo::pipeline_run::{self, PipelineRunState};
2use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, Set};
13use serde::{Deserialize, Serialize};
24use time::OffsetDateTime;
35use uuid::Uuid;
@@ -40,6 +42,35 @@
4042 pub queued: bool,
4143}
4244
45#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
46pub struct PipelineRunDto {
47 pub id: Uuid,
48 pub repo_id: Uuid,
49 pub trigger_user_id: Uuid,
50 pub commit_sha: String,
51 pub branch: String,
52 pub state: PipelineRunState,
53 pub started_at: Option<OffsetDateTime>,
54 pub finished_at: Option<OffsetDateTime>,
55 pub created_at: OffsetDateTime,
56}
57
58impl From<pipeline_run::Model> for PipelineRunDto {
59 fn from(m: pipeline_run::Model) -> Self {
60 Self {
61 id: m.id,
62 repo_id: m.repo_id,
63 trigger_user_id: m.trigger_user_id,
64 commit_sha: m.commit_sha,
65 branch: m.branch,
66 state: m.state,
67 started_at: m.started_at,
68 finished_at: m.finished_at,
69 created_at: m.created_at,
70 }
71 }
72}
73
4374#[derive(Debug, Clone, Serialize)]
4475pub struct StatusListDto {
4576 pub statuses: Vec<CommitStatusDto>,
@@ -47,34 +78,149 @@
4778}
4879
4980impl GitService {
81 async fn insert_run(
82 &self,
83 repo_id: Uuid,
84 caller: Uuid,
85 commit_sha: String,
86 branch: String,
87 ) -> Result<Uuid, GitError> {
88 let id = Uuid::now_v7();
89 let now = OffsetDateTime::now_utc();
90 pipeline_run::ActiveModel {
91 id: Set(id),
92 repo_id: Set(repo_id),
93 trigger_user_id: Set(caller),
94 commit_sha: Set(commit_sha),
95 branch: Set(branch),
96 state: Set(PipelineRunState::Queued),
97 started_at: Set(None),
98 finished_at: Set(None),
99 created_at: Set(now),
100 updated_at: Set(now),
101 }
102 .insert(self.db_write())
103 .await?;
104 Ok(id)
105 }
106
50107 pub async fn pipeline_trigger(
51108 &self,
52 _space_name: String,
53 _repo_name: String,
54 _commit: String,
55 _branch: String,
109 caller: Uuid,
110 space_name: String,
111 repo_name: String,
112 commit: String,
113 branch: String,
56114 ) -> Result<TriggerResultDto, GitError> {
115 let ctx = self.context(space_name, repo_name).await?;
116 let job_id = self
117 .insert_run(ctx.model.id, caller, commit, branch)
118 .await?;
57119 Ok(TriggerResultDto {
58 job_id: Uuid::now_v7().to_string(),
59 queued: false,
120 job_id: job_id.to_string(),
121 queued: true,
60122 })
61123 }
62124
63125 pub async fn pipeline_schedule(
64126 &self,
65 _space_name: String,
66 _repo_name: String,
67 _commit: String,
68 _job: JobDto,
69 ) -> Result<(), GitError> {
70 Err(GitError::Internal(
71 "pipeline scheduling not supported by this backend".to_string(),
72 ))
127 caller: Uuid,
128 space_name: String,
129 repo_name: String,
130 commit: String,
131 job: JobDto,
132 ) -> Result<TriggerResultDto, GitError> {
133 let ctx = self.context(space_name, repo_name).await?;
134 let job_id = self
135 .insert_run(ctx.model.id, caller, commit, job.name)
136 .await?;
137 Ok(TriggerResultDto {
138 job_id: job_id.to_string(),
139 queued: true,
140 })
141 }
142
143 pub async fn pipeline_cancel(&self, job_id: String) -> Result<(), GitError> {
144 let id = Uuid::parse_str(&job_id).map_err(|_| GitError::NotFound)?;
145 let row = pipeline_run::Entity::find_by_id(id)
146 .one(self.db_read())
147 .await?
148 .ok_or(GitError::NotFound)?;
149 match row.state {
150 PipelineRunState::Queued | PipelineRunState::Running => {}
151 _ => {
152 return Err(GitError::Conflict(
153 "pipeline run already finished".to_string(),
154 ))
155 }
156 }
157 let mut am: pipeline_run::ActiveModel = row.into();
158 am.state = Set(PipelineRunState::CancelRequested);
159 am.updated_at = Set(OffsetDateTime::now_utc());
160 am.update(self.db_write()).await?;
161 Ok(())
162 }
163
164 pub async fn list_pipeline_runs(
165 &self,
166 space_name: String,
167 repo_name: String,
168 commit: Option<String>,
169 branch: Option<String>,
170 state: Option<PipelineRunState>,
171 limit: Option<u64>,
172 offset: Option<u64>,
173 ) -> Result<Vec<PipelineRunDto>, GitError> {
174 let ctx = self.context(space_name, repo_name).await?;
175 let mut q = pipeline_run::Entity::find()
176 .filter(pipeline_run::Column::RepoId.eq(ctx.model.id))
177 .order_by_desc(pipeline_run::Column::CreatedAt);
178 if let Some(commit) = commit.filter(|c| !c.is_empty()) {
179 q = q.filter(pipeline_run::Column::CommitSha.eq(commit));
180 }
181 if let Some(branch) = branch.filter(|b| !b.is_empty()) {
182 q = q.filter(pipeline_run::Column::Branch.eq(branch));
183 }
184 if let Some(state) = state {
185 q = q.filter(pipeline_run::Column::State.eq(state));
186 }
187 let limit = limit.unwrap_or(50).clamp(1, 100);
188 let offset = offset.unwrap_or(0).min(5000);
189 let rows = q.limit(limit).offset(offset).all(self.db_read()).await?;
190 Ok(rows.into_iter().map(PipelineRunDto::from).collect())
73191 }
74192
75 pub async fn pipeline_cancel(&self, _job_id: String) -> Result<(), GitError> {
76 Err(GitError::Internal(
77 "pipeline cancellation not supported by this backend".to_string(),
78 ))
193 pub async fn get_pipeline_run(&self, job_id: String) -> Result<PipelineRunDto, GitError> {
194 let id = Uuid::parse_str(&job_id).map_err(|_| GitError::NotFound)?;
195 let row = pipeline_run::Entity::find_by_id(id)
196 .one(self.db_read())
197 .await?
198 .ok_or(GitError::NotFound)?;
199 Ok(PipelineRunDto::from(row))
200 }
201
202 pub async fn report_pipeline_run(
203 &self,
204 job_id: String,
205 state: PipelineRunState,
206 started_at: Option<OffsetDateTime>,
207 finished_at: Option<OffsetDateTime>,
208 ) -> Result<PipelineRunDto, GitError> {
209 let id = Uuid::parse_str(&job_id).map_err(|_| GitError::NotFound)?;
210 let row = pipeline_run::Entity::find_by_id(id)
211 .one(self.db_read())
212 .await?
213 .ok_or(GitError::NotFound)?;
214 let mut am: pipeline_run::ActiveModel = row.into();
215 am.state = Set(state);
216 if let Some(t) = started_at {
217 am.started_at = Set(Some(t));
218 }
219 if let Some(t) = finished_at {
220 am.finished_at = Set(Some(t));
221 }
222 am.updated_at = Set(OffsetDateTime::now_utc());
223 let updated = am.update(self.db_write()).await?;
224 Ok(PipelineRunDto::from(updated))
79225 }
80226}
Modified lib/git/src/openapi.rs lib/git/src/openapi.rs +4 −0 View lib/git/src/openapi.rs
@@ -61,6 +61,8 @@
6161 handler::patches::generate::patch_generate,
6262 handler::patches::parse::patch_parse,
6363 handler::path_log::path_log,
64 handler::pipelines::get_run::get_run,
65 handler::pipelines::list_runs::list_runs,
6466 handler::pipelines::schedule::schedule,
6567 handler::pipelines::trigger::trigger,
6668 handler::remotes::add::add,
@@ -160,6 +162,8 @@
160162 crate::service::pipelines::CommitStatusState,
161163 crate::service::pipelines::JobDto,
162164 crate::service::pipelines::TriggerResultDto,
165 crate::service::pipelines::PipelineRunDto,
166 models::repo::pipeline_run::PipelineRunState,
163167 crate::service::refs::BranchInfoDto,
164168 crate::service::refs::HeadInfoDto,
165169 crate::service::refs::UpdateRefResultDto,
Modified lib/git/src/routes.rs lib/git/src/routes.rs +6 −0 View lib/git/src/routes.rs
@@ -64,6 +64,9 @@
6464use crate::handler::patches::parse::patch_parse;
6565use crate::handler::path_log::path_log;
6666use crate::handler::pipelines::cancel::cancel;
67use crate::handler::pipelines::get_run::get_run;
68use crate::handler::pipelines::list_runs::list_runs;
69use crate::handler::pipelines::report_run::report_run;
6770use crate::handler::pipelines::schedule::schedule;
6871use crate::handler::pipelines::trigger::trigger;
6972use crate::handler::refs::branches::refs_branches;
@@ -300,6 +303,8 @@
300303 post(execute_script),
301304 )
302305 .route("/{space_name}/{repo_name}/pipelines/trigger", post(trigger))
306 .route("/{space_name}/{repo_name}/pipelines", get(list_runs))
307 .route("/{space_name}/{repo_name}/pipelines/{job_id}", get(get_run))
303308 .route(
304309 "/{space_name}/{repo_name}/pipelines/schedule",
305310 post(schedule),
@@ -393,6 +398,7 @@
393398 )
394399 .route("/{space_name}/{repo_name}/patch/apply", post(patch_apply))
395400 .route("/_/pipelines/cancel", post(cancel))
401 .route("/_/pipelines/{job_id}/report", post(report_run))
396402 .route("/{space_name}/{repo_name}/packs/stats", get(pack_stats))
397403 .route("/{space_name}/{repo_name}/packs/ingest", post(pack_ingest))
398404 .route("/{space_name}/{repo_name}/packs/repack", post(pack_repack))
Modified Cargo.lock Cargo.lock +2 −0 View Cargo.lock
@@ -158,6 +158,7 @@
158158version = "1.0.0"
159159dependencies = [
160160 "anyhow",
161 "auth",
161162 "axum",
162163 "chat",
163164 "config",
@@ -599,6 +600,7 @@
599600 "sha2 0.11.0",
600601 "ssh-key 0.6.7",
601602 "time 0.3.55",
603 "tokio",
602604 "tracing",
603605 "utoipa",
604606 "uuid",