forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1use std::env;
2
3use actix_web::HttpResponse;
4use anyhow::Error;
5use reqwest::Client;
6use serde_json::json;
7
8use crate::types::{
9 file::{Entry, EntryList, TemporaryLink},
10 token::AccessToken,
11};
12
13pub const BASE_URL: &str = "https://api.dropboxapi.com/2";
14pub const CONTENT_URL: &str = "https://content.dropboxapi.com/2";
15
16pub async fn get_access_token(refresh_token: &str) -> Result<AccessToken, Error> {
17 let client = Client::new();
18 let res = client
19 .post("https://api.dropboxapi.com/oauth2/token")
20 .header("Content-Type", "application/x-www-form-urlencoded")
21 .query(&[
22 ("grant_type", "refresh_token"),
23 ("refresh_token", refresh_token),
24 ("client_id", &env::var("DROPBOX_CLIENT_ID")?),
25 ("client_secret", &env::var("DROPBOX_CLIENT_SECRET")?),
26 ])
27 .send()
28 .await?;
29
30 Ok(res.json::<AccessToken>().await?)
31}
32
33pub struct DropboxClient {
34 pub access_token: String,
35}
36
37impl DropboxClient {
38 pub async fn new(refresh_token: &str) -> Result<Self, Error> {
39 let res = get_access_token(refresh_token).await?;
40 Ok(DropboxClient {
41 access_token: res.access_token,
42 })
43 }
44
45 pub async fn create_music_folder(&self) -> Result<(), Error> {
46 let client = Client::new();
47 client
48 .post(&format!("{}/files/create_folder_v2", BASE_URL))
49 .bearer_auth(&self.access_token)
50 .json(&json!({ "path": "/Music" }))
51 .send()
52 .await?;
53
54 Ok(())
55 }
56
57 pub async fn get_metadata(&self, path: &str) -> Result<Entry, Error> {
58 let client = Client::new();
59 let res = client
60 .post(&format!("{}/files/get_metadata", BASE_URL))
61 .bearer_auth(&self.access_token)
62 .json(&json!({ "path": path }))
63 .send()
64 .await?;
65
66 Ok(res.json::<Entry>().await?)
67 }
68
69 pub async fn get_files(&self, path: &str) -> Result<EntryList, Error> {
70 let client = Client::new();
71 let res = client
72 .post(&format!("{}/files/list_folder", BASE_URL))
73 .bearer_auth(&self.access_token)
74 .json(&json!({
75 "path": path,
76 "recursive": false,
77 "include_media_info": true,
78 "include_deleted": false,
79 "include_has_explicit_shared_members": false,
80 "include_mounted_folders": true,
81 "include_non_downloadable_files": true,
82 }))
83 .send()
84 .await?;
85
86 Ok(res.json::<EntryList>().await?)
87 }
88
89 pub async fn download_file(&self, path: &str) -> Result<HttpResponse, Error> {
90 let client = Client::new();
91 let res = client
92 .post(&format!("{}/files/download", CONTENT_URL))
93 .bearer_auth(&self.access_token)
94 .header("Dropbox-API-Arg", &json!({ "path": path }).to_string())
95 .send()
96 .await?;
97
98 let mut actix_response = HttpResponse::Ok();
99
100 // Forward headers
101 for (key, value) in res.headers().iter() {
102 actix_response.append_header((key.as_str(), value.to_str().unwrap_or("")));
103 }
104
105 // Forward body
106 let body = res.bytes_stream();
107
108 Ok(actix_response.streaming(body))
109 }
110
111 pub async fn get_temporary_link(&self, path: &str) -> Result<TemporaryLink, Error> {
112 let client = Client::new();
113 let res = client
114 .post(&format!("{}/files/get_temporary_link", BASE_URL))
115 .bearer_auth(&self.access_token)
116 .json(&json!({ "path": path }))
117 .send()
118 .await?;
119
120 Ok(res.json::<TemporaryLink>().await?)
121 }
122}