feat: accept usernames or UUIDs in follow management

This commit is contained in:
Patrick Britton 2026-02-09 10:05:46 -06:00
parent a4909723d9
commit fa0cca9b34
2 changed files with 27 additions and 6 deletions

View file

@ -512,7 +512,7 @@ function FollowManager({ userId }: { userId: string }) {
{/* Add */}
<div className="flex items-center gap-2 mb-3">
<input type="text" placeholder="User ID to add" value={addHandle}
<input type="text" placeholder="Username or user ID" value={addHandle}
onChange={(e) => setAddHandle(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleAdd()}
className="flex-1 px-2 py-1.5 border border-warm-300 rounded text-sm" />

View file

@ -689,6 +689,22 @@ func (h *AdminHandler) AdminUpdateProfile(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Profile updated"})
}
// resolveUserID accepts either a UUID or a handle and returns the user's UUID.
func (h *AdminHandler) resolveUserID(ctx context.Context, input string) (string, error) {
// If it parses as a UUID, return as-is
if _, err := uuid.Parse(input); err == nil {
return input, nil
}
// Otherwise, treat as a handle and look it up
handle := strings.TrimPrefix(input, "@")
var id uuid.UUID
err := h.pool.QueryRow(ctx, `SELECT id FROM profiles WHERE handle = $1`, handle).Scan(&id)
if err != nil {
return "", fmt.Errorf("user not found: %s", input)
}
return id.String(), nil
}
// AdminManageFollow adds or removes follow relationships for official accounts.
func (h *AdminHandler) AdminManageFollow(c *gin.Context) {
ctx := c.Request.Context()
@ -696,7 +712,7 @@ func (h *AdminHandler) AdminManageFollow(c *gin.Context) {
var req struct {
Action string `json:"action"` // "add" or "remove"
UserID string `json:"user_id"` // the other user in the relationship
UserID string `json:"user_id"` // UUID or handle of the other user
Relation string `json:"relation"` // "follower" (user follows target) or "following" (target follows user)
}
if err := c.ShouldBindJSON(&req); err != nil {
@ -713,16 +729,21 @@ func (h *AdminHandler) AdminManageFollow(c *gin.Context) {
return
}
// Resolve handle or UUID to a UUID
resolvedID, err := h.resolveUserID(ctx, req.UserID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Determine follower_id and following_id
var followerID, followingID string
if req.Relation == "follower" {
// "user follows target" — user is follower, target is following
followerID = req.UserID
followerID = resolvedID
followingID = targetUserID
} else {
// "target follows user" — target is follower, user is following
followerID = targetUserID
followingID = req.UserID
followingID = resolvedID
}
if req.Action == "add" {