+89
-36
upload/src/main.rs
+89
-36
upload/src/main.rs
···
1
1
use clap::{ArgAction, Parser};
2
-
use jacquard::api::com_atproto::repo::apply_writes::{self, ApplyWrites, ApplyWritesWritesItem};
3
-
use jacquard::atproto;
2
+
use jacquard::api::com_atproto::repo::apply_writes::{
3
+
self, ApplyWrites, ApplyWritesOutput, ApplyWritesWritesItem,
4
+
};
4
5
use jacquard::client::MemorySessionStore;
5
-
use jacquard::prelude::XrpcClient;
6
+
use jacquard::client::{AgentSessionExt};
7
+
use jacquard::oauth::loopback::LoopbackConfig;
8
+
use jacquard::oauth::types::AuthorizeOptions;
6
9
use jacquard::types::string::{AtStrError, RecordKey, Rkey};
10
+
use jacquard::{atproto, oauth};
7
11
use jacquard::{
8
12
Data,
9
13
api::com_atproto::{self, repo::list_records::ListRecords},
···
13
17
types::{ident::AtIdentifier, nsid::Nsid, string::AtprotoStr, uri::Uri},
14
18
xrpc::XrpcExt,
15
19
};
16
-
use miette::{ErrReport, IntoDiagnostic, Result};
20
+
use miette::{IntoDiagnostic, Result};
17
21
use std::{collections::HashMap, fs, path::PathBuf};
18
22
19
23
use crate::sitemap::{BlobRef, Sitemap, SitemapNode};
···
21
25
mod sitemap;
22
26
mod utils;
23
27
24
-
#[derive(Parser, Debug)]
28
+
#[derive(Parser, Debug, Clone)]
25
29
#[command(version, about, long_about = None)]
26
30
struct Config {
27
31
/// Handle or DID to authenticate
28
-
#[arg(verbatim_doc_comment, short, long)]
32
+
#[arg(verbatim_doc_comment)]
29
33
user: String,
34
+
30
35
/// App password to authenticate the client
31
36
/// Normal passwords also work but are not advised
32
-
#[arg(verbatim_doc_comment, short, long)]
33
-
password: String,
37
+
/// If ommited, oauth will be used instead
38
+
/// Oauth is reccomended where possible.
39
+
#[arg(verbatim_doc_comment, short = 'p', long = "password")]
40
+
password: Option<String>,
34
41
35
42
/// Include dotfiles in upload
36
43
/// Default: false
···
48
55
dir: PathBuf,
49
56
}
50
57
51
-
#[tokio::main]
52
-
async fn main() -> Result<(), miette::Error> {
53
-
env_logger::init();
54
-
// get config items
55
-
let config = Config::parse();
56
-
57
-
// get local site info
58
-
let local_sitemap = sitemap::local_sitemap(config.dir, config.all_files, config.git_ignore)?;
59
-
60
-
// create session
61
-
let client = JacquardResolver::default();
62
-
let store = MemorySessionStore::default();
63
-
let session = CredentialSession::new(store.into(), client.into());
64
-
65
-
let auth = session
66
-
.login(config.user.into(), config.password.into(), None, None, None)
67
-
.await?;
68
-
println!("Authenticated as {}", auth.did);
69
-
70
-
71
-
let agent = Agent::from(session);
72
-
58
+
async fn live_records(agent: &impl AgentSessionExt, config: Config) -> Result<Vec<String>> {
73
59
// find live site records
74
60
let mut cursor = None;
75
61
let mut remote_records = Vec::new();
62
+
let user = config.user.clone();
63
+
let user = if user.contains(":") {
64
+
AtIdentifier::Did(user.into())
65
+
} else {
66
+
AtIdentifier::Handle(user.into())
67
+
};
76
68
loop {
77
69
let req = com_atproto::repo::list_records::ListRecords::new()
78
70
.collection(
79
71
Nsid::new("dev.atcities.route").expect("failed to generate dev.atcities.route nsid"),
80
72
)
81
-
.repo(AtIdentifier::Did(auth.did.clone()))
73
+
.repo(user.clone())
82
74
.limit(100)
83
75
.maybe_cursor(cursor)
84
76
.build();
···
120
112
break;
121
113
}
122
114
}
115
+
Ok(remote_records)
116
+
}
123
117
118
+
async fn upload_site_blobs(
119
+
agent: &impl AgentSessionExt,
120
+
_config: Config,
121
+
local_sitemap: Sitemap,
122
+
) -> Result<Sitemap> {
124
123
// upload local site blobs
125
124
let mut new_sitemap: Sitemap = HashMap::new();
126
125
for (k, v) in local_sitemap {
···
131
130
}
132
131
};
133
132
let blob = fs::read(blob).into_diagnostic()?;
134
-
// let res = agent
135
-
// .upload_blob(blob, v.mime_type.clone().into())
136
-
// .await?;
137
133
138
134
let req = com_atproto::repo::upload_blob::UploadBlob::new()
139
135
.body(blob.into())
···
149
145
);
150
146
}
151
147
148
+
Ok(new_sitemap)
149
+
}
150
+
151
+
async fn update_remote_site(
152
+
agent: &impl AgentSessionExt,
153
+
config: Config,
154
+
remote_records: Vec<String>,
155
+
new_sitemap: Sitemap,
156
+
) -> Result<ApplyWritesOutput<'static>> {
152
157
// batch delete/upload records
153
158
let mut writes = Vec::new();
154
159
let mut delete_records = remote_records
···
198
203
writes.append(&mut create_records);
199
204
200
205
let req = com_atproto::repo::apply_writes::ApplyWrites::new()
201
-
.repo(AtIdentifier::Did(auth.did.clone()))
206
+
.repo(AtIdentifier::Did(config.user.into()))
202
207
.writes(writes)
203
208
.build();
204
209
···
208
213
.await?
209
214
.into_output()?;
210
215
211
-
println!("res: {res:#?}");
216
+
Ok(res)
217
+
}
218
+
219
+
#[tokio::main]
220
+
async fn main() -> Result<(), miette::Error> {
221
+
env_logger::init();
222
+
// get config items
223
+
let config = Config::parse();
224
+
225
+
// get local site info
226
+
let local_sitemap =
227
+
sitemap::local_sitemap(config.dir.clone(), config.all_files, config.git_ignore)?;
228
+
229
+
// create session
230
+
if let Some(password) = config.password.clone() {
231
+
let password = password.into();
232
+
let client = JacquardResolver::default();
233
+
let store = MemorySessionStore::default();
234
+
let session = CredentialSession::new(store.into(), client.into());
235
+
236
+
let _ = session
237
+
.login(config.user.clone().into(), password, None, None, None)
238
+
.await?;
239
+
240
+
let agent = Agent::from(session);
241
+
242
+
let remote_sitemap = live_records(&agent, config.clone()).await?;
243
+
let new_sitemap = upload_site_blobs(&agent, config.clone(), local_sitemap).await?;
244
+
let writes_output =
245
+
update_remote_site(&agent, config.clone(), remote_sitemap, new_sitemap).await?;
246
+
println!("{writes_output:#?}");
247
+
} else {
248
+
let oauth = oauth::client::OAuthClient::with_memory_store();
249
+
let session = oauth
250
+
.login_with_local_server(
251
+
config.user.clone(),
252
+
AuthorizeOptions::default(),
253
+
LoopbackConfig::default(),
254
+
)
255
+
.await?;
256
+
257
+
let agent = Agent::from(session);
258
+
259
+
let remote_sitemap = live_records(&agent, config.clone()).await?;
260
+
let new_sitemap = upload_site_blobs(&agent, config.clone(), local_sitemap).await?;
261
+
let writes_output =
262
+
update_remote_site(&agent, config.clone(), remote_sitemap, new_sitemap).await?;
263
+
println!("{writes_output:#?}");
264
+
};
212
265
213
266
Ok(())
214
267
}