implement delete

This commit is contained in:
sim1222 2024-07-18 09:05:34 +09:00
parent 92e5c3f985
commit c9ec5ff082
Signed by: sim1222
GPG Key ID: D1AE30E316E44E5D
4 changed files with 141 additions and 20 deletions

View File

@ -91,9 +91,44 @@ impl Client {
.await
.map_err(Into::into)
}
pub async fn file_detail(
&self,
req: types::request::FileDetailRequest,
) -> anyhow::Result<types::response::FileDetailResponse> {
let client = reqwest::Client::new();
let request = client
.post("https://forest.sendy.jp/cloud/service/file/v1/file")
.bearer_auth(&self.token)
.json(&req);
let response = request.send().await?;
response
.json::<types::response::FileDetailResponse>()
.await
.map_err(Into::into)
}
pub async fn delete_file(
&self,
req: types::request::DeleteFileRequest,
) -> anyhow::Result<types::response::JobKeyResponse> {
let client = reqwest::Client::new();
let request = client
.delete("https://forest.sendy.jp/cloud/service/file/v3/files")
.bearer_auth(&self.token)
.json(&req);
let response = request.send().await?;
response
.json::<types::response::JobKeyResponse>()
.await
.map_err(Into::into)
}
}
// https://www.rakuten-drive.com/api/account/refreshtoken POST RefreshTokenRequest RefreshTokenResponse
// https://forest.sendy.jp/cloud/service/file/v1/file POST FileDetailRequest FileDetailResponse
// https://forest.sendy.jp/cloud/service/file/v1/files POST ListFilesRequest ListFilesResponse
// https://forest.sendy.jp/cloud/service/file/v3/files DELETE DeleteFileRequest JobKeyResponse
// https://forest.sendy.jp/cloud/service/file/v1/files/create POST CreateFolderRequest

View File

@ -19,7 +19,7 @@ use types::response::ListFilesResponseFile;
mod endpoints;
mod types;
const BEARER_TOKEN: &str = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxNTQwYWM3MWJiOTJhYTA2OTNjODI3MTkwYWNhYmU1YjA1NWNiZWMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoi5bm457-8IOW_l-adkSIsInBsYW4iOiJza2YiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vc2VuZHktc2VydmljZSIsImF1ZCI6InNlbmR5LXNlcnZpY2UiLCJhdXRoX3RpbWUiOjE3MjEyMjYwMTUsInVzZXJfaWQiOiJHY2xUN0RybkxGaG83dm5JaXJVemp0TUxoUmsyIiwic3ViIjoiR2NsVDdEcm5MRmhvN3ZuSWlyVXpqdE1MaFJrMiIsImlhdCI6MTcyMTI1NTM1OCwiZXhwIjoxNzIxMjU4OTU4LCJlbWFpbCI6ImtvdXN1a2UxMTIzNjEyNEBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsia291c3VrZTExMjM2MTI0QGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6ImN1c3RvbSJ9fQ.uC-X4XCMTJ-Vv0bmm85cZy65LVdNxRKBlNXxsg8_QqyMV1rRzmpDMQpwWKk10OUDj6xovg1tfmlUW2syL0twANO8hKOSlI_wLZ1Rvvm0TF8EvDLvv8OGFc93nm3OIaSaiZj-xcORZzeJDVHsdraGoYDX3YbYPIJAhDaOsHX5_QbLwuxoz0dxd0fTAoDH7aEpDhcojjTmMImtbGqMzpvUpwNunJaJK2YZTYiHXZtcK7mr9cQLF5b3Exee--R5hGEU9E49jGtXKQNrP_6mkTXVivJh6TdKeFiMCrbc-6xZvuBnkEQ8g0GvU9cERhJTZ73U2jdHLzWbYitCh2nzkbQDNA";
const BEARER_TOKEN: &str = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxNTQwYWM3MWJiOTJhYTA2OTNjODI3MTkwYWNhYmU1YjA1NWNiZWMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoi5bm457-8IOW_l-adkSIsInBsYW4iOiJza2YiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vc2VuZHktc2VydmljZSIsImF1ZCI6InNlbmR5LXNlcnZpY2UiLCJhdXRoX3RpbWUiOjE3MjEyMjYwMTUsInVzZXJfaWQiOiJHY2xUN0RybkxGaG83dm5JaXJVemp0TUxoUmsyIiwic3ViIjoiR2NsVDdEcm5MRmhvN3ZuSWlyVXpqdE1MaFJrMiIsImlhdCI6MTcyMTI2MDk2NSwiZXhwIjoxNzIxMjY0NTY1LCJlbWFpbCI6ImtvdXN1a2UxMTIzNjEyNEBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsia291c3VrZTExMjM2MTI0QGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6ImN1c3RvbSJ9fQ.qgMyyPkP992xCtsTjr28PhzjiqaNBn1O1Z6HYEk3cjgPyVoPRiMV5KaDuWWveP0Z2x5_jvd5We0zsYzpxnOcf1dlf4VmAUqNGrVjCRqWUBuU008EkTAFlKBZAk4yYON0xddNcoUR6OrmYSLKMR5OOOV9FkTbFzd72rydQpcbiy9nmint_uXnN3z_9th0yf0J8oBd4_aXUzGUw2YOG8mBlgJfAoFdO5gMxJCWkC2V9r1TbxYWDMrFwtj7QMLVN4TOz2Bcy8erPiA_T46ap2gc9T0wTaPrE8h436FkTLdiaSJaYBLMEbS7dtXNLZ7SxaA4JOfeIgt2KoN5BrZg4qqt4Q";
const HOST_ID: &str = "GclT7DrnLFho7vnIirUzjtMLhRk2";
const CHUNK_SIZE: usize = 1024 * 1024 * 10; // 10MB
const APP_VERSION: &str = "v21.11.10";
@ -38,7 +38,6 @@ enum Commands {
prefix: Option<String>,
},
Upload {
#[clap(short, long)]
file: PathBuf,
#[clap(short, long)]
prefix: Option<String>,
@ -46,13 +45,16 @@ enum Commands {
recursive: bool,
},
Download {
#[clap(short, long)]
path: String,
#[clap(long)]
prefix: Option<String>,
},
Move {},
Delete {},
Delete {
path: String,
#[clap(long)]
recursive: bool,
},
MkDir {},
}
@ -68,7 +70,7 @@ async fn main() {
match &args.command {
Commands::List { prefix } => {
let res = list_files(prefix.clone()).await.unwrap();
let res = list_files(Some(&prefix.clone().unwrap_or("".to_string()))).await.unwrap();
res.file.iter().for_each(|f| {
let permission_string = if f.is_folder { "d" } else { "-" };
println!(
@ -243,7 +245,7 @@ async fn main() {
let file_path =
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/");
let list = list_files(Some(file_path.clone())).await.unwrap();
let list = list_files(Some(&file_path)).await.unwrap();
let file = list
.file
@ -286,8 +288,40 @@ async fn main() {
Commands::Move {} => {
println!("Move");
}
Commands::Delete {} => {
println!("Delete");
Commands::Delete { path, recursive } => {
let client = endpoints::Client::new(BEARER_TOKEN.to_string(), HOST_ID.to_string());
let file = file_detail(path).await.unwrap();
if file.is_folder && !*recursive {
println!("Use --recursive option for folder delete");
return;
}
let req = types::request::DeleteFileRequest {
file: vec![types::request::FileModifyRequestFile {
last_modified: file.last_modified,
path: file.path,
version_id: file.version_id,
size: file.size,
}],
host_id: client.host_id.clone(),
prefix: "".to_string(),
trash: true,
};
let res = client.delete_file(req).await.unwrap();
loop {
let req = types::request::CheckActionRequest {
key: res.key.clone(),
};
let res = client.check_action(req).await.unwrap();
if res.state == "complete" {
break;
}
std::thread::sleep(std::time::Duration::from_millis(200));
}
println!("Deleted");
}
Commands::MkDir {} => {
println!("MkDir");
@ -440,14 +474,25 @@ async fn multipart_upload(
Ok(())
}
async fn list_files(prefix: Option<String>) -> anyhow::Result<types::response::ListFilesResponse> {
async fn file_detail(path: &str) -> anyhow::Result<types::response::FileDetailResponseFile> {
let client = endpoints::Client::new(BEARER_TOKEN.to_string(), HOST_ID.to_string());
let req = types::request::FileDetailRequest {
host_id: client.host_id.clone(),
path: path.to_string(),
thumbnail_size: 130,
};
let res = client.file_detail(req).await?;
Ok(res.file)
}
async fn list_files(prefix: Option<&str>) -> anyhow::Result<types::response::ListFilesResponse> {
let client = endpoints::Client::new(BEARER_TOKEN.to_string(), HOST_ID.to_string());
let pagination_size = 40;
let mut files = Vec::<ListFilesResponseFile>::new();
let req = types::request::ListFilesRequest {
from: 0,
host_id: client.host_id.clone(),
path: prefix.clone().unwrap_or("".to_string()),
path: prefix.clone().unwrap_or("").to_string(),
sort_type: "path".to_string(),
reverse: false,
thumbnail_size: 130,
@ -463,7 +508,7 @@ async fn list_files(prefix: Option<String>) -> anyhow::Result<types::response::L
let req = types::request::ListFilesRequest {
from: cursor,
host_id: client.host_id.clone(),
path: prefix.clone().unwrap_or("".to_string()),
path: prefix.clone().unwrap_or("").to_string(),
sort_type: "path".to_string(),
reverse: false,
thumbnail_size: 130,

View File

@ -29,7 +29,7 @@ pub struct CreateFolderRequest {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RenameFileRequest {
pub file: Vec<RenameFileRequestFile>,
pub file: Vec<FileModifyRequestFile>,
pub host_id: String,
pub name: String,
pub path: String,
@ -37,7 +37,7 @@ pub struct RenameFileRequest {
#[derive(Debug, Serialize, Deserialize)]
pub struct RenameFileRequestFile {
pub struct FileModifyRequestFile {
pub last_modified: String, // 1970-01-20T22:07:12.804Z
pub path: String,
pub size: i64,
@ -53,7 +53,7 @@ pub struct CheckActionRequest {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct MoveFileRequest {
pub file: Vec<RenameFileRequestFile>,
pub file: Vec<FileModifyRequestFile>,
pub host_id: String,
pub prefix: String,
pub path: String,
@ -96,7 +96,7 @@ pub struct CompleteUploadRequestFile {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DeleteFileRequest {
pub file: Vec<RenameFileRequestFile>,
pub file: Vec<FileModifyRequestFile>,
pub host_id: String,
pub prefix: String,
pub trash: bool,
@ -117,8 +117,10 @@ pub struct GetFileLinkRequestFile {
pub size: i64,
}
// #[derive(Debug, Serialize, Deserialize)]
// pub struct GetFileLinkRequest {
// pub host_id: String,
// pub path: String,
// }
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct FileDetailRequest {
pub host_id: String,
pub path: String,
pub thumbnail_size: i64,
}

View File

@ -109,3 +109,42 @@ pub struct GetFileLinkTokenResponse {
pub struct GetFileLinkResponse {
pub url: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct FileDetailResponse {
pub access_level: String,
pub file: FileDetailResponseFile,
pub owner: String,
pub prefix: String,
pub usage_size: i64,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FileDetailResponseFile {
pub access_level: String,
pub has_child_folder: bool,
#[serde(rename = "HostID")]
pub host_id: String,
pub is_backed_up: bool,
pub is_folder: bool,
pub is_latest: bool,
pub is_share: String,
pub items_count: i64,
pub last_modified: String, // 2024-07-16T06:18:06.595Z
#[serde(rename = "LastModifierID")]
pub last_modifier_id: String,
#[serde(rename = "OwnerID")]
pub owner_id: String, // OwnerID
pub path: String,
pub size: i64,
pub thumbnail: String,
pub version: serde_json::Value, // returns null
#[serde(rename = "VersionID")]
pub version_id: String,
}