mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-31 13:17:09 +00:00
110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"html/template"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"atcr.io/pkg/appview/middleware"
|
|
"atcr.io/pkg/appview/storage"
|
|
"atcr.io/pkg/atproto"
|
|
"atcr.io/pkg/auth/oauth"
|
|
)
|
|
|
|
// SettingsHandler handles the settings page
|
|
type SettingsHandler struct {
|
|
Templates *template.Template
|
|
Refresher *oauth.Refresher
|
|
RegistryURL string
|
|
}
|
|
|
|
func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
user := middleware.GetUser(r)
|
|
if user == nil {
|
|
http.Redirect(w, r, "/auth/oauth/login?return_to=/settings", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
// Create ATProto client with session provider (uses DoWithSession for DPoP nonce safety)
|
|
client := atproto.NewClientWithSessionProvider(user.PDSEndpoint, user.DID, h.Refresher)
|
|
|
|
// Fetch sailor profile
|
|
profile, err := storage.GetProfile(r.Context(), client)
|
|
if err != nil {
|
|
// Error fetching profile - log out user
|
|
slog.Warn("Failed to fetch profile, logging out", "component", "settings", "did", user.DID, "error", err)
|
|
http.Redirect(w, r, "/auth/logout", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
if profile == nil {
|
|
// Profile doesn't exist yet (404) - user needs to log out and back in to create it
|
|
slog.Warn("Profile doesn't exist, logging out", "component", "settings", "did", user.DID)
|
|
http.Redirect(w, r, "/auth/logout", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
slog.Debug("Fetched profile", "component", "settings", "did", user.DID, "default_hold", profile.DefaultHold)
|
|
|
|
data := struct {
|
|
PageData
|
|
Profile struct {
|
|
Handle string
|
|
DID string
|
|
PDSEndpoint string
|
|
DefaultHold string
|
|
}
|
|
}{
|
|
PageData: NewPageData(r, h.RegistryURL),
|
|
}
|
|
|
|
data.Profile.Handle = user.Handle
|
|
data.Profile.DID = user.DID
|
|
data.Profile.PDSEndpoint = user.PDSEndpoint
|
|
data.Profile.DefaultHold = profile.DefaultHold
|
|
|
|
if err := h.Templates.ExecuteTemplate(w, "settings", data); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
// UpdateDefaultHoldHandler handles updating the default hold
|
|
type UpdateDefaultHoldHandler struct {
|
|
Refresher *oauth.Refresher
|
|
}
|
|
|
|
func (h *UpdateDefaultHoldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
user := middleware.GetUser(r)
|
|
if user == nil {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
holdEndpoint := r.FormValue("hold_endpoint")
|
|
|
|
// Create ATProto client with session provider (uses DoWithSession for DPoP nonce safety)
|
|
client := atproto.NewClientWithSessionProvider(user.PDSEndpoint, user.DID, h.Refresher)
|
|
|
|
// Fetch existing profile or create new one
|
|
profile, err := storage.GetProfile(r.Context(), client)
|
|
if err != nil || profile == nil {
|
|
// Profile doesn't exist, create new one
|
|
profile = atproto.NewSailorProfileRecord(holdEndpoint)
|
|
} else {
|
|
// Update existing profile
|
|
profile.DefaultHold = holdEndpoint
|
|
profile.UpdatedAt = time.Now()
|
|
}
|
|
|
|
// Save profile
|
|
if err := storage.UpdateProfile(r.Context(), client, profile); err != nil {
|
|
http.Error(w, "Failed to update profile: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write([]byte(`<div class="success"><i data-lucide="check"></i> Default hold updated successfully!</div>`))
|
|
}
|