+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,
+126
appview/state/profile.go
+126
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
+
profile, err := db.GetProfile(s.db, user.Did)
806
+
if err != nil {
807
+
l.Warn("getting profile data from DB", "err", err)
808
+
profile = &models.Profile{Did: user.Did}
809
+
}
810
+
811
+
profile.Avatar = uploadBlobResp.Blob.Ref.String()
812
+
813
+
profileRecord := &tangled.ActorProfile{
814
+
Avatar: uploadBlobResp.Blob,
815
+
Bluesky: profile.IncludeBluesky,
816
+
}
817
+
818
+
if profile.Description != "" {
819
+
profileRecord.Description = &profile.Description
820
+
}
821
+
if profile.Location != "" {
822
+
profileRecord.Location = &profile.Location
823
+
}
824
+
if profile.Pronouns != "" {
825
+
profileRecord.Pronouns = &profile.Pronouns
826
+
}
827
+
828
+
for _, link := range profile.Links {
829
+
if link != "" {
830
+
profileRecord.Links = append(profileRecord.Links, link)
831
+
}
832
+
}
833
+
834
+
for _, stat := range profile.Stats {
835
+
if stat.Kind != "" {
836
+
profileRecord.Stats = append(profileRecord.Stats, string(stat.Kind))
837
+
}
838
+
}
839
+
840
+
for _, pin := range profile.PinnedRepos {
841
+
if pin != "" {
842
+
profileRecord.PinnedRepositories = append(profileRecord.PinnedRepositories, string(pin))
843
+
}
844
+
}
845
+
846
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
847
+
Collection: tangled.ActorProfileNSID,
848
+
Repo: user.Did,
849
+
Rkey: "self",
850
+
Record: &lexutil.LexiconTypeDecoder{Val: profileRecord},
851
+
SwapRecord: getRecordResp.Cid,
852
+
})
853
+
854
+
if err != nil {
855
+
l.Error("failed to update profile record", "err", err)
856
+
w.WriteHeader(http.StatusInternalServerError)
857
+
fmt.Fprintf(w, "Failed to update profile on your PDS")
858
+
return
859
+
}
860
+
861
+
l.Info("successfully updated profile with avatar")
862
+
863
+
s.pages.HxRefresh(w)
864
+
w.WriteHeader(http.StatusOK)
865
+
}