245 lines
7.4 KiB
Rust
245 lines
7.4 KiB
Rust
use db::sqlx;
|
|
use git::rpc::{proto as p, proto::blob_service_client::BlobServiceClient};
|
|
use session::Session;
|
|
|
|
use crate::{AppService, error::AppError, git::rpc_err};
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
|
|
pub struct ContentResponse {
|
|
pub path: String,
|
|
pub name: String,
|
|
#[serde(rename = "type")]
|
|
pub content_type: String,
|
|
pub size: i64,
|
|
pub encoding: Option<String>,
|
|
pub content: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct CreateContent {
|
|
pub message: String,
|
|
pub content: String,
|
|
pub branch: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
|
|
pub struct UpdateContent {
|
|
pub message: String,
|
|
pub content: String,
|
|
pub sha: String,
|
|
pub branch: Option<String>,
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn git_contents_get_by_name(
|
|
&self,
|
|
ctx: &Session,
|
|
wk: &str,
|
|
repo: &str,
|
|
path: &str,
|
|
ref_name: Option<&str>,
|
|
) -> Result<ContentResponse, AppError> {
|
|
let _ = self.git_require_member(ctx, wk, repo).await?;
|
|
self.git_contents_get(ctx, wk, repo, path, ref_name).await
|
|
}
|
|
pub async fn git_contents_create_by_name(
|
|
&self,
|
|
ctx: &Session,
|
|
wk: &str,
|
|
repo: &str,
|
|
path: &str,
|
|
params: CreateContent,
|
|
) -> Result<ContentResponse, AppError> {
|
|
let _ = self.git_require_member(ctx, wk, repo).await?;
|
|
self.git_contents_create(ctx, wk, repo, path, params).await
|
|
}
|
|
pub async fn git_contents_update_by_name(
|
|
&self,
|
|
ctx: &Session,
|
|
wk: &str,
|
|
repo: &str,
|
|
path: &str,
|
|
params: UpdateContent,
|
|
) -> Result<ContentResponse, AppError> {
|
|
let _ = self.git_require_member(ctx, wk, repo).await?;
|
|
self.git_contents_update(ctx, wk, repo, path, params).await
|
|
}
|
|
pub async fn git_contents_delete_by_name(
|
|
&self,
|
|
ctx: &Session,
|
|
wk: &str,
|
|
repo: &str,
|
|
path: &str,
|
|
msg: &str,
|
|
sha: &str,
|
|
branch: Option<&str>,
|
|
) -> Result<(), AppError> {
|
|
let _ = self.git_require_member(ctx, wk, repo).await?;
|
|
self.git_contents_delete(ctx, wk, repo, path, msg, sha, branch)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn git_contents_get(
|
|
&self,
|
|
ctx: &Session,
|
|
wk_name: &str,
|
|
repo_name: &str,
|
|
path: &str,
|
|
_ref_name: Option<&str>,
|
|
) -> Result<ContentResponse, AppError> {
|
|
let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
|
|
let mut blob_client = BlobServiceClient::new(self.git.clone());
|
|
|
|
let empty_oid = p::ObjectId {
|
|
value: String::new(),
|
|
};
|
|
|
|
let resp = blob_client
|
|
.blob_load(tonic::Request::new(p::BlobLoadRequest {
|
|
repo_id: repo.id.to_string(),
|
|
id: Some(empty_oid.clone()),
|
|
path: path.to_string(),
|
|
}))
|
|
.await
|
|
.map_err(rpc_err)?
|
|
.into_inner();
|
|
|
|
let is_binary = blob_client
|
|
.blob_is_binary(tonic::Request::new(p::BlobIsBinaryRequest {
|
|
repo_id: repo.id.to_string(),
|
|
id: Some(empty_oid.clone()),
|
|
}))
|
|
.await
|
|
.map(|r| r.into_inner().is_binary)
|
|
.unwrap_or(false);
|
|
|
|
let size_resp = blob_client
|
|
.blob_size(tonic::Request::new(p::BlobSizeRequest {
|
|
repo_id: repo.id.to_string(),
|
|
id: Some(empty_oid),
|
|
path: String::new(),
|
|
}))
|
|
.await
|
|
.map_err(rpc_err)?
|
|
.into_inner();
|
|
|
|
let blob_data = resp.blob;
|
|
let size = size_resp.size as i64;
|
|
|
|
let (encoding, content) = if is_binary {
|
|
(Some("base64".to_string()), Some(base64_encode(&blob_data)))
|
|
} else {
|
|
(None, Some(String::from_utf8_lossy(&blob_data).to_string()))
|
|
};
|
|
|
|
Ok(ContentResponse {
|
|
path: path.to_string(),
|
|
name: path.rsplit('/').next().unwrap_or(path).to_string(),
|
|
content_type: "file".to_string(),
|
|
size,
|
|
encoding,
|
|
content,
|
|
})
|
|
}
|
|
|
|
pub async fn git_contents_create(
|
|
&self,
|
|
ctx: &Session,
|
|
wk_name: &str,
|
|
repo_name: &str,
|
|
path: &str,
|
|
params: CreateContent,
|
|
) -> Result<ContentResponse, AppError> {
|
|
let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
|
|
let user = crate::session_user(ctx)?;
|
|
|
|
let user_model = sqlx::query_as::<_, model::users::UserModel>(
|
|
"SELECT id, username, display_name, avatar_url, website_url, allow_use, can_search, \
|
|
last_sign_in_at, created_at, updated_at FROM \"user\" WHERE id = $1",
|
|
)
|
|
.bind(user)
|
|
.fetch_optional(self.db.reader())
|
|
.await
|
|
.map_err(|e| AppError::DatabaseError(e.to_string()))?
|
|
.ok_or(AppError::UserNotFound)?;
|
|
|
|
let display = user_model.display_name.clone();
|
|
let username = user_model.username.clone();
|
|
let author_name = if display.is_empty() {
|
|
username.clone()
|
|
} else {
|
|
display
|
|
};
|
|
|
|
let file_size = params.content.len() as i64;
|
|
let content_bytes = params.content.clone().into_bytes();
|
|
let mut client = p::commit_service_client::CommitServiceClient::new(
|
|
self.git.clone(),
|
|
);
|
|
let resp = client
|
|
.create_commit(tonic::Request::new(p::CreateCommitRequest {
|
|
repo_id: repo.id.to_string(),
|
|
branch: params
|
|
.branch
|
|
.unwrap_or_else(|| repo.default_branch.clone()),
|
|
message: params.message,
|
|
author_name: author_name.clone(),
|
|
author_email: format!("{username}@gitdata.ai"),
|
|
committer_name: "redpanda".to_string(),
|
|
committer_email: "redpanda@gitdata.ai".to_string(),
|
|
files: vec![p::FileChange {
|
|
path: path.to_string(),
|
|
content: content_bytes,
|
|
}],
|
|
}))
|
|
.await
|
|
.map_err(rpc_err)?
|
|
.into_inner();
|
|
|
|
let _oid = resp.oid.map(|o| o.value).unwrap_or_default();
|
|
Ok(ContentResponse {
|
|
path: path.to_string(),
|
|
name: path.rsplit('/').next().unwrap_or(path).to_string(),
|
|
content_type: "file".to_string(),
|
|
size: file_size,
|
|
encoding: None,
|
|
content: Some(params.content),
|
|
})
|
|
}
|
|
|
|
pub async fn git_contents_update(
|
|
&self,
|
|
_ctx: &Session,
|
|
_wk: &str,
|
|
_repo: &str,
|
|
_path: &str,
|
|
_params: UpdateContent,
|
|
) -> Result<ContentResponse, AppError> {
|
|
Err(AppError::InternalServerError(
|
|
"contents update not yet implemented".to_string(),
|
|
))
|
|
}
|
|
|
|
pub async fn git_contents_delete(
|
|
&self,
|
|
_ctx: &Session,
|
|
_wk: &str,
|
|
_repo: &str,
|
|
_path: &str,
|
|
_message: &str,
|
|
_sha: &str,
|
|
_branch: Option<&str>,
|
|
) -> Result<(), AppError> {
|
|
Err(AppError::InternalServerError(
|
|
"contents delete not yet implemented".to_string(),
|
|
))
|
|
}
|
|
}
|
|
|
|
fn base64_encode(data: &[u8]) -> String {
|
|
use base64::Engine as _;
|
|
base64::engine::general_purpose::STANDARD.encode(data)
|
|
}
|