1use crate::utils::{
2 get_text, last_modified, parse_filesize, process_shadow_api_response, pubkey_arg,
3 shadow_client_factory, shadow_file_with_basename, storage_object_url,
4 wait_for_user_confirmation, FileMetadata, FILE_UPLOAD_BATCH_SIZE,
5};
6use byte_unit::Byte;
7use clap::Parser;
8use futures::StreamExt;
9use shadow_drive_sdk::{Pubkey, ShadowDriveClient, StorageAccountVersion};
10use shadow_rpc_auth::genesysgo_auth::{authenticate, parse_account_id_from_url};
11use solana_sdk::signature::Signer;
12use std::path::PathBuf;
13
14#[derive(Debug, Parser)]
15pub enum DriveCommand {
16 ShadowRpcAuth,
17 CreateStorageAccount {
23 name: String,
25 #[clap(parse(try_from_str = parse_filesize))]
27 size: Byte,
28 },
29 DeleteStorageAccount {
33 #[clap(parse(try_from_str = pubkey_arg))]
35 storage_account: Pubkey,
36 },
37 CancelDeleteStorageAccount {
39 #[clap(parse(try_from_str = pubkey_arg))]
41 storage_account: Pubkey,
42 },
43 ClaimStake {
45 #[clap(parse(try_from_str = pubkey_arg))]
47 storage_account: Pubkey,
48 },
49 AddStorage {
51 #[clap(parse(try_from_str = pubkey_arg))]
53 storage_account: Pubkey,
54 #[clap(parse(try_from_str = parse_filesize))]
56 size: Byte,
57 },
58 AddImmutableStorage {
60 #[clap(parse(try_from_str = pubkey_arg))]
62 storage_account: Pubkey,
63 #[clap(parse(try_from_str = parse_filesize))]
65 size: Byte,
66 },
67 ReduceStorage {
69 #[clap(parse(try_from_str = pubkey_arg))]
71 storage_account: Pubkey,
72 #[clap(parse(try_from_str = parse_filesize))]
74 size: Byte,
75 },
76 MakeStorageImmutable {
78 #[clap(parse(try_from_str = pubkey_arg))]
80 storage_account: Pubkey,
81 },
82 GetStorageAccount {
84 #[clap(parse(try_from_str = pubkey_arg))]
86 storage_account: Pubkey,
87 },
88 GetStorageAccounts {
91 #[clap(parse(try_from_str = pubkey_arg))]
93 owner: Option<Pubkey>,
94 },
95 ListFiles {
97 #[clap(parse(try_from_str = pubkey_arg))]
99 storage_account: Pubkey,
100 },
101 GetText {
103 #[clap(parse(try_from_str = pubkey_arg))]
105 storage_account: Pubkey,
106 filename: String,
108 },
109 GetObjectData {
111 #[clap(parse(try_from_str = pubkey_arg))]
113 storage_account: Pubkey,
114 file: String,
116 },
117 DeleteFile {
119 #[clap(parse(try_from_str = pubkey_arg))]
121 storage_account: Pubkey,
122 filename: String,
124 },
125 EditFile {
127 #[clap(parse(try_from_str = pubkey_arg))]
129 storage_account: Pubkey,
130 path: PathBuf,
133 },
134 StoreFiles {
136 #[clap(long, default_value_t=FILE_UPLOAD_BATCH_SIZE)]
139 batch_size: usize,
140 #[clap(parse(try_from_str = pubkey_arg))]
142 storage_account: Pubkey,
143 #[clap(min_values = 1)]
145 files: Vec<PathBuf>,
146 },
147}
148
149impl DriveCommand {
150 pub async fn process<T: Signer>(
151 &self,
152 signer: &T,
153 client_signer: T,
154 rpc_url: &str,
155 skip_confirm: bool,
156 auth: Option<String>,
157 ) -> anyhow::Result<()> {
158 let signer_pubkey = signer.pubkey();
159 println!("Signing with {:?}", signer_pubkey);
160 println!("Sending RPC requests to {}", rpc_url);
161 match self {
162 DriveCommand::ShadowRpcAuth => {
163 let account_id = parse_account_id_from_url(rpc_url.to_string())?;
164 let resp = authenticate(signer as &dyn Signer, &account_id).await?;
165 println!("{:#?}", resp);
166 }
167 DriveCommand::CreateStorageAccount { name, size } => {
168 let client = shadow_client_factory(client_signer, rpc_url, auth);
169 println!("Create Storage Account {}: {}", name, size);
170 wait_for_user_confirmation(skip_confirm)?;
171 let response = client
172 .create_storage_account(name, size.clone(), StorageAccountVersion::v2())
173 .await;
174 let resp = process_shadow_api_response(response)?;
175 println!("{:#?}", resp);
176 }
177 DriveCommand::DeleteStorageAccount { storage_account } => {
178 let client = shadow_client_factory(client_signer, rpc_url, auth);
179 println!("Delete Storage Account {}", storage_account.to_string());
180 wait_for_user_confirmation(skip_confirm)?;
181 let response = client.delete_storage_account(storage_account).await;
182
183 let resp = process_shadow_api_response(response)?;
184 println!("{:#?}", resp);
185 }
186 DriveCommand::CancelDeleteStorageAccount { storage_account } => {
187 let client = shadow_client_factory(client_signer, rpc_url, auth);
188 println!(
189 "Cancellation of Delete Storage Account {}",
190 storage_account.to_string()
191 );
192 wait_for_user_confirmation(skip_confirm)?;
193 let response = client.cancel_delete_storage_account(storage_account).await;
194
195 let resp = process_shadow_api_response(response)?;
196 println!("{:#?}", resp);
197 }
198 DriveCommand::ClaimStake { storage_account } => {
199 let client = shadow_client_factory(client_signer, rpc_url, auth);
200 println!(
201 "Claim Stake on Storage Account {}",
202 storage_account.to_string()
203 );
204 wait_for_user_confirmation(skip_confirm)?;
205 let response = client.claim_stake(storage_account).await;
206
207 let resp = process_shadow_api_response(response)?;
208 println!("{:#?}", resp);
209 }
210 DriveCommand::ReduceStorage {
211 storage_account,
212 size,
213 } => {
214 let client = shadow_client_factory(client_signer, rpc_url, auth);
215 println!(
216 "Reduce Storage Capacity {}: {}",
217 storage_account.to_string(),
218 size
219 );
220 wait_for_user_confirmation(skip_confirm)?;
221 let response = client.reduce_storage(storage_account, size.clone()).await;
222
223 let resp = process_shadow_api_response(response)?;
224 println!("{:#?}", resp);
225 }
226 DriveCommand::AddStorage {
227 storage_account,
228 size,
229 } => {
230 let client = shadow_client_factory(client_signer, rpc_url, auth);
231 println!("Increase Storage {}: {}", storage_account.to_string(), size);
232 wait_for_user_confirmation(skip_confirm)?;
233 let response = client.add_storage(storage_account, size.clone()).await;
234
235 let resp = process_shadow_api_response(response)?;
236 println!("{:#?}", resp);
237 }
238 DriveCommand::AddImmutableStorage {
239 storage_account,
240 size,
241 } => {
242 let client = shadow_client_factory(client_signer, rpc_url, auth);
243 println!(
244 "Increase Immutable Storage {}: {}",
245 storage_account.to_string(),
246 size
247 );
248 wait_for_user_confirmation(skip_confirm)?;
249 let response = client
250 .add_immutable_storage(storage_account, size.clone())
251 .await;
252
253 let resp = process_shadow_api_response(response)?;
254 println!("{:#?}", resp);
255 }
256 DriveCommand::MakeStorageImmutable { storage_account } => {
257 let client = shadow_client_factory(client_signer, rpc_url, auth);
258 println!("Make Storage Immutable {}", storage_account.to_string());
259 wait_for_user_confirmation(skip_confirm)?;
260 let response = client.make_storage_immutable(storage_account).await;
261
262 let resp = process_shadow_api_response(response)?;
263 println!("{:#?}", resp);
264 }
265 DriveCommand::GetStorageAccount { storage_account } => {
266 let client = ShadowDriveClient::new(client_signer, rpc_url);
267 println!("Get Storage Account {}", storage_account.to_string());
268 let response = client.get_storage_account(storage_account).await;
269
270 let act = process_shadow_api_response(response)?;
271 println!("{:#?}", act);
272 }
273 DriveCommand::GetStorageAccounts { owner } => {
274 let client = shadow_client_factory(client_signer, rpc_url, auth.clone());
275 let owner = owner.as_ref().unwrap_or(&signer_pubkey);
276 println!("Get Storage Accounts Owned By {}", owner.to_string());
277 let response = client.get_storage_accounts(owner).await;
278 let accounts = process_shadow_api_response(response)?;
279 println!("{:#?}", accounts);
280 }
281 DriveCommand::ListFiles { storage_account } => {
282 let client = ShadowDriveClient::new(client_signer, rpc_url);
283 println!(
284 "List Files for Storage Account {}",
285 storage_account.to_string()
286 );
287 let response = client.list_objects(storage_account).await;
288 let files = process_shadow_api_response(response)?;
289 println!("{:#?}", files);
290 }
291 DriveCommand::GetText {
292 storage_account,
293 filename,
294 } => {
295 let url = storage_object_url(storage_account, filename);
296 let resp = get_text(&url).await?;
297 let last_modified = last_modified(resp.headers())?;
298 println!("Get Text at {}", &url);
299 println!("Last Modified: {}", last_modified);
300 println!("");
301 println!("{}", resp.text().await?);
302 }
303 DriveCommand::DeleteFile {
304 storage_account,
305 filename,
306 } => {
307 let client = ShadowDriveClient::new(client_signer, rpc_url);
308 let url = storage_object_url(storage_account, filename);
309 println!("Delete file {}", &url);
310 wait_for_user_confirmation(skip_confirm)?;
311 let response = client.delete_file(storage_account, url.clone()).await;
312 let resp = process_shadow_api_response(response)?;
313 println!("{:#?}", resp);
314 }
315 DriveCommand::EditFile {
316 storage_account,
317 path,
318 } => {
319 let client = ShadowDriveClient::new(client_signer, rpc_url);
320 let shadow_file = shadow_file_with_basename(path);
321 println!(
322 "Edit file {} {}",
323 storage_account.to_string(),
324 path.display()
325 );
326 wait_for_user_confirmation(skip_confirm)?;
327 let response = client.edit_file(storage_account, shadow_file).await;
328 let resp = process_shadow_api_response(response)?;
329 println!("{:#?}", resp);
330 }
331 DriveCommand::GetObjectData {
332 storage_account,
333 file,
334 } => {
335 let url = storage_object_url(storage_account, file);
336 println!("Get object data {} {}", storage_account.to_string(), file);
337 let http_client = reqwest::Client::new();
338 let response = http_client.head(url).send().await?;
339 let data = FileMetadata::from_headers(response.headers())?;
340 println!("{:#?}", data);
341 }
342 DriveCommand::StoreFiles {
343 batch_size,
344 storage_account,
345 files,
346 } => {
347 let client = ShadowDriveClient::new(client_signer, rpc_url);
348 println!("Store Files {} {:#?}", storage_account.to_string(), files);
349 println!(
350 "WARNING: This CLI does not add any encryption on its own. \
351 The files in their current state become public as soon as they're uploaded."
352 );
353 wait_for_user_confirmation(skip_confirm)?;
354 let mut responses = Vec::new();
355 for chunk in files.chunks(*batch_size) {
356 let response = async {
357 let resp = client
358 .store_files(
359 &storage_account,
360 chunk
361 .into_iter()
362 .map(|path: &PathBuf| shadow_file_with_basename(path))
363 .collect(),
364 )
365 .await;
366 let resp = process_shadow_api_response(resp).unwrap();
367 println!("{:#?}", resp);
368 };
369 responses.push(response);
370 }
371 futures::stream::iter(responses)
372 .buffer_unordered(100)
373 .collect::<Vec<_>>()
374 .await;
375 }
376 }
377 Ok(())
378 }
379}