+6
appview/ingester.go
+6
appview/ingester.go
···
285
285
return err
286
286
}
287
287
288
+
avatar := ""
289
+
if record.Avatar != nil {
290
+
avatar = record.Avatar.Ref.String()
291
+
}
292
+
288
293
description := ""
289
294
if record.Description != nil {
290
295
description = *record.Description
···
325
330
326
331
profile := models.Profile{
327
332
Did: did,
333
+
Avatar: avatar,
328
334
Description: description,
329
335
IncludeBluesky: includeBluesky,
330
336
Location: location,
+124
appview/state/profile.go
+124
appview/state/profile.go
···
737
737
AllRepos: allRepos,
738
738
})
739
739
}
740
+
741
+
func (s *State) UploadProfileAvatar(w http.ResponseWriter, r *http.Request) {
742
+
l := s.logger.With("handler", "UploadProfileAvatar")
743
+
user := s.oauth.GetUser(r)
744
+
l = l.With("did", user.Did)
745
+
746
+
// Parse multipart form (10MB max)
747
+
if err := r.ParseMultipartForm(10 << 20); err != nil {
748
+
l.Error("failed to parse form", "err", err)
749
+
w.WriteHeader(http.StatusBadRequest)
750
+
fmt.Fprintf(w, "Failed to parse form")
751
+
return
752
+
}
753
+
754
+
file, handler, err := r.FormFile("avatar")
755
+
if err != nil {
756
+
l.Error("failed to read avatar file", "err", err)
757
+
w.WriteHeader(http.StatusBadRequest)
758
+
fmt.Fprintf(w, "Failed to read avatar file")
759
+
return
760
+
}
761
+
defer file.Close()
762
+
763
+
if handler.Size > 1000000 {
764
+
l.Warn("avatar file too large", "size", handler.Size)
765
+
w.WriteHeader(http.StatusBadRequest)
766
+
fmt.Fprintf(w, "Avatar file too large (max 1MB)")
767
+
return
768
+
}
769
+
770
+
contentType := handler.Header.Get("Content-Type")
771
+
if contentType != "image/png" && contentType != "image/jpeg" {
772
+
l.Warn("invalid image type", "contentType", contentType)
773
+
w.WriteHeader(http.StatusBadRequest)
774
+
fmt.Fprintf(w, "Invalid image type (only PNG and JPEG allowed)")
775
+
return
776
+
}
777
+
778
+
client, err := s.oauth.AuthorizedClient(r)
779
+
if err != nil {
780
+
l.Error("failed to get PDS client", "err", err)
781
+
w.WriteHeader(http.StatusInternalServerError)
782
+
fmt.Fprintf(w, "Failed to connect to your PDS")
783
+
return
784
+
}
785
+
786
+
uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file)
787
+
if err != nil {
788
+
l.Error("failed to upload avatar blob", "err", err)
789
+
w.WriteHeader(http.StatusInternalServerError)
790
+
fmt.Fprintf(w, "Failed to upload avatar to your PDS")
791
+
return
792
+
}
793
+
794
+
l.Info("uploaded avatar blob", "cid", uploadBlobResp.Blob.Ref.String())
795
+
796
+
// get current profile record from PDS to get its CID for swap
797
+
getRecordResp, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self")
798
+
if err != nil {
799
+
l.Error("failed to get current profile record", "err", err)
800
+
w.WriteHeader(http.StatusInternalServerError)
801
+
fmt.Fprintf(w, "Failed to get current profile from your PDS")
802
+
return
803
+
}
804
+
805
+
var profileRecord *tangled.ActorProfile
806
+
if getRecordResp.Value != nil {
807
+
if val, ok := getRecordResp.Value.Val.(*tangled.ActorProfile); ok {
808
+
profileRecord = val
809
+
} else {
810
+
l.Warn("profile record type assertion failed, creating new record")
811
+
profileRecord = &tangled.ActorProfile{}
812
+
}
813
+
} else {
814
+
l.Warn("no existing profile record, creating new record")
815
+
profileRecord = &tangled.ActorProfile{}
816
+
}
817
+
818
+
profileRecord.Avatar = uploadBlobResp.Blob
819
+
820
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
821
+
Collection: tangled.ActorProfileNSID,
822
+
Repo: user.Did,
823
+
Rkey: "self",
824
+
Record: &lexutil.LexiconTypeDecoder{Val: profileRecord},
825
+
SwapRecord: getRecordResp.Cid,
826
+
})
827
+
828
+
if err != nil {
829
+
l.Error("failed to update profile record", "err", err)
830
+
w.WriteHeader(http.StatusInternalServerError)
831
+
fmt.Fprintf(w, "Failed to update profile on your PDS")
832
+
return
833
+
}
834
+
835
+
l.Info("successfully updated profile with avatar")
836
+
837
+
profile, err := db.GetProfile(s.db, user.Did)
838
+
if err != nil {
839
+
l.Warn("getting profile data from DB", "err", err)
840
+
profile = &models.Profile{Did: user.Did}
841
+
}
842
+
profile.Avatar = uploadBlobResp.Blob.Ref.String()
843
+
844
+
tx, err := s.db.BeginTx(r.Context(), nil)
845
+
if err != nil {
846
+
l.Error("failed to start transaction", "err", err)
847
+
s.pages.HxRefresh(w)
848
+
w.WriteHeader(http.StatusOK)
849
+
return
850
+
}
851
+
852
+
err = db.UpsertProfile(tx, profile)
853
+
if err != nil {
854
+
l.Error("failed to update profile in DB", "err", err)
855
+
tx.Rollback()
856
+
s.pages.HxRefresh(w)
857
+
w.WriteHeader(http.StatusOK)
858
+
return
859
+
}
860
+
861
+
w.Header().Set("HX-Redirect", r.Header.Get("Referer"))
862
+
w.WriteHeader(http.StatusOK)
863
+
}