This commit is contained in:
@@ -287,16 +287,41 @@ func HandleKMLList(w http.ResponseWriter, r *http.Request) {
|
||||
if publicSortBy == "" {
|
||||
publicSortBy = "votes"
|
||||
}
|
||||
targetUserID := r.URL.Query().Get("target_user_id")
|
||||
|
||||
// 1. Get My Files
|
||||
myFiles, myTotal, err := fetchKMLList(userID, true, myPage, myLimit, mySortBy)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
// 1. Get My Files (if no specific target user is requested, or if target is me)
|
||||
// logic: If targetUserID is present and != userID, we don't show "My Files" (which implies private/editable).
|
||||
// But the UI might expect the "My Files" block to just be empty.
|
||||
// Actually, the prompt implies "My Files" is the logged-in user's files.
|
||||
// If we are viewing a profile, we just want that user's PUBLIC files.
|
||||
// The current frontend calls this endpoint to populate the standard browser "My Files" and "Public Files" tabs.
|
||||
// We should probably keep this behavior for the standard view.
|
||||
|
||||
// If it's a profile request, we might just be using the public list with a filter.
|
||||
// Let's rely on the client to ask for what it wants.
|
||||
// Existing client: Calls /list without target_user_id. Expects: My Files (all mine), Public Files (all public).
|
||||
|
||||
// New Client (Profile): Needs distinct endpoint or param?
|
||||
// If we add `target_user_id` to the public files query, we can reuse this.
|
||||
|
||||
// 1. Get My Files (Logged in user's files)
|
||||
var myFiles []KMLMetadata
|
||||
var myTotal int
|
||||
var err error
|
||||
|
||||
if targetUserID == "" {
|
||||
myFiles, myTotal, err = fetchKMLList(userID, true, myPage, myLimit, mySortBy)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Get Public Files
|
||||
publicFiles, publicTotal, err := fetchKMLList(userID, false, publicPage, publicLimit, publicSortBy)
|
||||
var publicFiles []KMLMetadata
|
||||
var publicTotal int
|
||||
|
||||
publicFiles, publicTotal, err = fetchKMLListPublic(userID, targetUserID, publicPage, publicLimit, publicSortBy)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -315,15 +340,29 @@ func HandleKMLList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func fetchKMLList(userID string, mineOnly bool, page, limit int, sortBy string) ([]KMLMetadata, int, error) {
|
||||
// Standard fetch for current user or general public list (wrapper)
|
||||
return fetchKMLQuery(userID, "", mineOnly, page, limit, sortBy)
|
||||
}
|
||||
|
||||
func fetchKMLListPublic(requestingUserID, targetUserID string, page, limit int, sortBy string) ([]KMLMetadata, int, error) {
|
||||
return fetchKMLQuery(requestingUserID, targetUserID, false, page, limit, sortBy)
|
||||
}
|
||||
|
||||
func fetchKMLQuery(userID, targetUserID string, mineOnly bool, page, limit int, sortBy string) ([]KMLMetadata, int, error) {
|
||||
offset := (page - 1) * limit
|
||||
|
||||
var whereClause string
|
||||
var args []interface{}
|
||||
|
||||
if mineOnly {
|
||||
whereClause = "WHERE m.user_id = ?"
|
||||
args = append(args, userID)
|
||||
} else {
|
||||
whereClause = "WHERE m.is_public = 1"
|
||||
if targetUserID != "" {
|
||||
whereClause += " AND m.user_id = ?"
|
||||
args = append(args, targetUserID)
|
||||
}
|
||||
}
|
||||
|
||||
// Get total count first
|
||||
@@ -609,3 +648,31 @@ func HandleKMLDownload(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
// HandleUserProfile serves public user profile data
|
||||
func HandleUserProfile(w http.ResponseWriter, r *http.Request) {
|
||||
// Authentication optional? Yes, profiles should be public.
|
||||
// But let's check auth just to be safe if we want to restrict it to logged-in users later.
|
||||
// Current requirement: "implement profile pages... open the KML details overlay... so that you can do it too".
|
||||
// Implies logged in users mostly, but let's allow it generally if the files are public.
|
||||
|
||||
// Get target user ID from URL
|
||||
targetID := r.URL.Query().Get("user_id")
|
||||
if targetID == "" {
|
||||
http.Error(w, "Missing user_id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetPublicUser(targetID)
|
||||
if err != nil {
|
||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,10 @@ func main() {
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
// 6. Start Server
|
||||
// 7. User Profile Endpoint
|
||||
http.HandleFunc("/api/user/profile", RequireAuth(HandleUserProfile))
|
||||
|
||||
// 8. Start Server
|
||||
binding := "0.0.0.0:8080"
|
||||
fmt.Printf("Server starting on http://%s\n", binding)
|
||||
log.Fatal(http.ListenAndServe(binding, RecoveryMiddleware(http.DefaultServeMux)))
|
||||
|
||||
@@ -30,6 +30,21 @@ func GetUser(fitbitUserID string) (*User, error) {
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetPublicUser retrieves public user info by Fitbit user ID
|
||||
func GetPublicUser(fitbitUserID string) (*User, error) {
|
||||
var user User
|
||||
err := db.QueryRow("SELECT fitbit_user_id, display_name, avatar_url, created_at FROM users WHERE fitbit_user_id = ?", fitbitUserID).
|
||||
Scan(&user.FitbitUserID, &user.DisplayName, &user.AvatarURL, &user.CreatedAt)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
// Ensure we don't return sensitive info if struct expands later, though current struct is safe for public
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateUser adds or updates a user in the database
|
||||
func CreateOrUpdateUser(fitbitUserID, displayName, avatarURL string) (*User, error) {
|
||||
_, err := db.Exec(`
|
||||
|
||||
Reference in New Issue
Block a user