tangled
alpha
login
or
join now
serendipty01.dev
/
smokesignal
forked from
smokesignal.events/smokesignal
0
fork
atom
The smokesignal.events web application
0
fork
atom
overview
issues
pulls
pipelines
refactor: improving profile content and blob handling
Nick Gerakines
3 months ago
13c74bbb
b295fb4b
+175
-7
2 changed files
expand all
collapse all
unified
split
src
http
handle_blob.rs
handle_settings.rs
+150
-5
src/http/handle_blob.rs
···
25
25
middleware_i18n::Language,
26
26
},
27
27
select_template,
28
28
-
storage::profile::profile_get_by_aturi,
28
28
+
storage::profile::{profile_get_by_aturi, profile_insert},
29
29
};
30
30
31
31
use serde::{Deserialize, Serialize};
···
42
42
width: u32,
43
43
height: u32,
44
44
size: usize,
45
45
+
}
46
46
+
47
47
+
#[derive(Deserialize)]
48
48
+
struct PutRecordSuccessResponse {
49
49
+
uri: String,
50
50
+
cid: String,
45
51
}
46
52
47
53
/// Upload a blob to the PDS and return the TypedBlob reference
···
138
144
)
139
145
.await?;
140
146
147
147
+
// Store the avatar image locally in content storage immediately
148
148
+
// This ensures the image is available when the page reloads, without waiting for Jetstream
149
149
+
let avatar_cid = &blob.inner.ref_.link;
150
150
+
let image_path = format!("{}.png", avatar_cid);
151
151
+
if let Err(err) = web_context
152
152
+
.content_storage
153
153
+
.write_content(&image_path, &processed_data)
154
154
+
.await
155
155
+
{
156
156
+
tracing::warn!(
157
157
+
?err,
158
158
+
cid = %avatar_cid,
159
159
+
"Failed to store avatar image locally, will be fetched via Jetstream"
160
160
+
);
161
161
+
}
162
162
+
141
163
// Get the profile aturi
142
164
let profile_aturi = format!(
143
165
"at://{}/events.smokesignal.profile/self",
···
193
215
let put_record_url = format!("{}/xrpc/com.atproto.repo.putRecord", pds_endpoint);
194
216
let record_value = serde_json::to_value(&put_record_request)
195
217
.map_err(|e| BlobError::PutRecordSerializeFailed(e.to_string()))?;
196
196
-
let _response = match post_dpop_json(
218
218
+
let response = match post_dpop_json(
197
219
&web_context.http_client,
198
220
&dpop_auth,
199
221
&put_record_url,
···
223
245
}
224
246
};
225
247
248
248
+
// Update the profile in the local database immediately
249
249
+
// This ensures the profile is visible without waiting for Jetstream
250
250
+
if let Ok(put_response) = serde_json::from_value::<PutRecordSuccessResponse>(response) {
251
251
+
let display_name_for_db = profile
252
252
+
.display_name
253
253
+
.as_ref()
254
254
+
.filter(|s| !s.trim().is_empty())
255
255
+
.map(|s| s.as_str())
256
256
+
.unwrap_or(¤t_handle.handle);
257
257
+
258
258
+
if let Err(err) = profile_insert(
259
259
+
&web_context.pool,
260
260
+
&put_response.uri,
261
261
+
&put_response.cid,
262
262
+
¤t_handle.did,
263
263
+
display_name_for_db,
264
264
+
&profile,
265
265
+
)
266
266
+
.await
267
267
+
{
268
268
+
tracing::warn!(
269
269
+
?err,
270
270
+
"Failed to update local profile after avatar upload, will be synced via Jetstream"
271
271
+
);
272
272
+
}
273
273
+
}
274
274
+
226
275
// Redirect back to settings page
227
276
Ok((
228
277
StatusCode::OK,
···
287
336
"image/png",
288
337
)
289
338
.await?;
339
339
+
340
340
+
// Store the banner image locally in content storage immediately
341
341
+
// This ensures the image is available when the page reloads, without waiting for Jetstream
342
342
+
let banner_cid = &blob.inner.ref_.link;
343
343
+
let image_path = format!("{}.png", banner_cid);
344
344
+
if let Err(err) = web_context
345
345
+
.content_storage
346
346
+
.write_content(&image_path, &processed_data)
347
347
+
.await
348
348
+
{
349
349
+
tracing::warn!(
350
350
+
?err,
351
351
+
cid = %banner_cid,
352
352
+
"Failed to store banner image locally, will be fetched via Jetstream"
353
353
+
);
354
354
+
}
355
355
+
290
356
let profile_aturi = format!(
291
357
"at://{}/events.smokesignal.profile/self",
292
358
current_handle.did
···
334
400
let put_record_url = format!("{}/xrpc/com.atproto.repo.putRecord", pds_endpoint);
335
401
let record_value = serde_json::to_value(&put_record_request)
336
402
.map_err(|e| BlobError::PutRecordSerializeFailed(e.to_string()))?;
337
337
-
let _response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
403
403
+
let response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
338
404
Ok(response) => response,
339
405
Err(e) => {
340
406
let err_string = e.to_string();
···
346
412
}
347
413
};
348
414
415
415
+
// Update the profile in the local database immediately
416
416
+
// This ensures the profile is visible without waiting for Jetstream
417
417
+
if let Ok(put_response) = serde_json::from_value::<PutRecordSuccessResponse>(response) {
418
418
+
let display_name_for_db = profile
419
419
+
.display_name
420
420
+
.as_ref()
421
421
+
.filter(|s| !s.trim().is_empty())
422
422
+
.map(|s| s.as_str())
423
423
+
.unwrap_or(¤t_handle.handle);
424
424
+
425
425
+
if let Err(err) = profile_insert(
426
426
+
&web_context.pool,
427
427
+
&put_response.uri,
428
428
+
&put_response.cid,
429
429
+
¤t_handle.did,
430
430
+
display_name_for_db,
431
431
+
&profile,
432
432
+
)
433
433
+
.await
434
434
+
{
435
435
+
tracing::warn!(
436
436
+
?err,
437
437
+
"Failed to update local profile after banner upload, will be synced via Jetstream"
438
438
+
);
439
439
+
}
440
440
+
}
441
441
+
349
442
Ok((
350
443
StatusCode::OK,
351
444
HxRetarget("/settings".to_string()),
···
418
511
let put_record_url = format!("{}/xrpc/com.atproto.repo.putRecord", pds_endpoint);
419
512
let record_value = serde_json::to_value(&put_record_request)
420
513
.map_err(|e| BlobError::PutRecordSerializeFailed(e.to_string()))?;
421
421
-
let _response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
514
514
+
let response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
422
515
Ok(response) => response,
423
516
Err(e) => {
424
517
let err_string = e.to_string();
···
430
523
}
431
524
};
432
525
526
526
+
// Update the profile in the local database immediately
527
527
+
if let Ok(put_response) = serde_json::from_value::<PutRecordSuccessResponse>(response) {
528
528
+
let display_name_for_db = profile
529
529
+
.display_name
530
530
+
.as_ref()
531
531
+
.filter(|s| !s.trim().is_empty())
532
532
+
.map(|s| s.as_str())
533
533
+
.unwrap_or(¤t_handle.handle);
534
534
+
535
535
+
if let Err(err) = profile_insert(
536
536
+
&web_context.pool,
537
537
+
&put_response.uri,
538
538
+
&put_response.cid,
539
539
+
¤t_handle.did,
540
540
+
display_name_for_db,
541
541
+
&profile,
542
542
+
)
543
543
+
.await
544
544
+
{
545
545
+
tracing::warn!(
546
546
+
?err,
547
547
+
"Failed to update local profile after avatar deletion, will be synced via Jetstream"
548
548
+
);
549
549
+
}
550
550
+
}
551
551
+
433
552
Ok((
434
553
StatusCode::OK,
435
554
HxRetarget("/settings".to_string()),
···
502
621
let put_record_url = format!("{}/xrpc/com.atproto.repo.putRecord", pds_endpoint);
503
622
let record_value = serde_json::to_value(&put_record_request)
504
623
.map_err(|e| BlobError::PutRecordSerializeFailed(e.to_string()))?;
505
505
-
let _response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
624
624
+
let response = match post_dpop_json(&web_context.http_client, &dpop_auth, &put_record_url, record_value).await {
506
625
Ok(response) => response,
507
626
Err(e) => {
508
627
let err_string = e.to_string();
···
513
632
return Err(BlobError::PutRecordFailed(e.to_string()).into());
514
633
}
515
634
};
635
635
+
636
636
+
// Update the profile in the local database immediately
637
637
+
if let Ok(put_response) = serde_json::from_value::<PutRecordSuccessResponse>(response) {
638
638
+
let display_name_for_db = profile
639
639
+
.display_name
640
640
+
.as_ref()
641
641
+
.filter(|s| !s.trim().is_empty())
642
642
+
.map(|s| s.as_str())
643
643
+
.unwrap_or(¤t_handle.handle);
644
644
+
645
645
+
if let Err(err) = profile_insert(
646
646
+
&web_context.pool,
647
647
+
&put_response.uri,
648
648
+
&put_response.cid,
649
649
+
¤t_handle.did,
650
650
+
display_name_for_db,
651
651
+
&profile,
652
652
+
)
653
653
+
.await
654
654
+
{
655
655
+
tracing::warn!(
656
656
+
?err,
657
657
+
"Failed to update local profile after banner deletion, will be synced via Jetstream"
658
658
+
);
659
659
+
}
660
660
+
}
516
661
517
662
Ok((
518
663
StatusCode::OK,
+25
-2
src/http/handle_settings.rs
···
31
31
notification::{
32
32
notification_get, notification_reset_confirmation, notification_set_preference,
33
33
},
34
34
-
profile::profile_get_by_did,
34
34
+
profile::{profile_get_by_did, profile_insert},
35
35
webhook::{webhook_delete, webhook_list_by_did, webhook_toggle_enabled, webhook_upsert},
36
36
},
37
37
task_webhooks::TaskWork,
···
886
886
Ok(PutRecordResponse::StrongRef { uri, cid, .. }) => {
887
887
tracing::info!("Profile updated successfully: {} {}", uri, cid);
888
888
889
889
-
// The profile will be picked up by Jetstream and stored in our database
889
889
+
// Update the profile in the local database immediately
890
890
+
// This ensures the profile is visible without waiting for Jetstream
891
891
+
let display_name_for_db = profile
892
892
+
.display_name
893
893
+
.as_ref()
894
894
+
.filter(|s| !s.trim().is_empty())
895
895
+
.map(|s| s.as_str())
896
896
+
.unwrap_or(¤t_handle.handle);
897
897
+
898
898
+
if let Err(err) = profile_insert(
899
899
+
&web_context.pool,
900
900
+
&uri,
901
901
+
&cid,
902
902
+
¤t_handle.did,
903
903
+
display_name_for_db,
904
904
+
&profile,
905
905
+
)
906
906
+
.await
907
907
+
{
908
908
+
tracing::warn!(
909
909
+
?err,
910
910
+
"Failed to update local profile, will be synced via Jetstream"
911
911
+
);
912
912
+
}
890
913
891
914
// Return updated profile section
892
915
Ok((