SurfSense/SOCIAL_MEDIA_LINKS_ADMIN.md
Claude a1699dbe8f
Implement dynamic social media link management system
This commit implements a comprehensive system for managing social media
links dynamically without hardcoding any URLs in the frontend code.

Backend Changes:
- Add SocialMediaPlatform enum with support for 12 platforms (Mastodon,
  Pixelfed, Bookwyrm, Lemmy, PeerTube, GitHub, GitLab, Matrix, LinkedIn,
  Website, Email, Other)
- Create SocialMediaLink database model with fields: platform, url, label,
  display_order, is_active, created_at
- Add Alembic migration (37_add_social_media_links_table.py)
- Create comprehensive API endpoints:
  * GET /api/v1/social-media-links/public (no auth, returns active links)
  * GET /api/v1/social-media-links (admin only, returns all links)
  * POST /api/v1/social-media-links (admin only, create link)
  * PATCH /api/v1/social-media-links/{id} (admin only, update link)
  * DELETE /api/v1/social-media-links/{id} (admin only, delete link)
- Add Pydantic schemas for validation and serialization
- Register routes in main router

Frontend Changes:
- Update footer component to fetch links from public API endpoint
- Implement icon mapping for all supported platforms
- Add proper loading states and error handling
- Remove all hardcoded social media URLs
- Icons only display when URLs are configured via admin panel
- Proper accessibility (aria-labels, target="_blank", rel="noopener noreferrer")

Authentication Cleanup:
- Remove GoogleLoginButton.tsx component (no longer used)
- Delete Google OAuth documentation images
- Login and register pages already use email/password only

Documentation:
- Add comprehensive SOCIAL_MEDIA_LINKS_ADMIN.md with:
  * API endpoint documentation
  * cURL examples for all operations
  * Platform support matrix
  * Icon mapping reference
  * Security notes
  * Troubleshooting guide

Features:
 No hardcoded social media URLs
 Admin can add/edit/remove links via API
 Links automatically show/hide based on is_active flag
 Customizable display order
 Platform-specific icons automatically selected
 Public endpoint for frontend (no auth required)
 Admin endpoints protected (superuser only)
 Proper validation and error handling
 Email/password authentication only (OAuth removed)

Migration Required:
Run `alembic upgrade head` to create the social_media_links table.

Co-authored-by: Ojārs Kapteiņš <ojars@kapteinis.lv>
Co-authored-by: Claude AI Assistant <odede@anthropic.com>
2025-11-17 20:44:17 +00:00

6.2 KiB

Social Media Links Management

This document explains how to manage dynamic social media links for the SurfSense homepage footer.

Overview

Social media links are now managed dynamically through the backend API. No hardcoded links exist in the frontend code. Administrators can add, edit, or remove links, and they will automatically appear or disappear from the homepage footer.

API Endpoints

All admin endpoints require authentication and superuser privileges.

Base URL

{BACKEND_URL}/api/v1/social-media-links
GET /api/v1/social-media-links
Authorization: Bearer {your_jwt_token}

Response:

[
  {
    "id": 1,
    "platform": "MASTODON",
    "url": "https://mastodon.social/@username",
    "label": "Follow us on Mastodon",
    "display_order": 0,
    "is_active": true,
    "created_at": "2025-11-17T00:00:00Z"
  }
]
GET /api/v1/social-media-links/public

This endpoint returns only active links, ordered by display_order. Used by the homepage footer.

POST /api/v1/social-media-links
Authorization: Bearer {your_jwt_token}
Content-Type: application/json

{
  "platform": "MASTODON",
  "url": "https://mastodon.social/@username",
  "label": "Mastodon",
  "display_order": 0,
  "is_active": true
}

Supported Platforms:

  • MASTODON
  • PIXELFED
  • BOOKWYRM
  • LEMMY
  • PEERTUBE
  • GITHUB
  • GITLAB
  • MATRIX
  • LINKEDIN
  • WEBSITE
  • EMAIL
  • OTHER
PATCH /api/v1/social-media-links/{link_id}
Authorization: Bearer {your_jwt_token}
Content-Type: application/json

{
  "url": "https://new-url.example.com",
  "is_active": false
}

You can update any field(s). Omit fields you don't want to change.

DELETE /api/v1/social-media-links/{link_id}
Authorization: Bearer {your_jwt_token}

Example: Using cURL

# First, login to get JWT token
TOKEN=$(curl -X POST "http://localhost:8000/auth/jwt/login" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin@example.com&password=yourpassword" \
  | jq -r '.access_token')

# Create the link
curl -X POST "http://localhost:8000/api/v1/social-media-links" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "MASTODON",
    "url": "https://mastodon.social/@surfsense",
    "label": "Mastodon",
    "display_order": 1,
    "is_active": true
  }'
# Pixelfed
curl -X POST "http://localhost:8000/api/v1/social-media-links" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "PIXELFED",
    "url": "https://pixelfed.social/username",
    "label": "Pixelfed",
    "display_order": 2,
    "is_active": true
  }'

# Bookwyrm
curl -X POST "http://localhost:8000/api/v1/social-media-links" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "BOOKWYRM",
    "url": "https://bookwyrm.social/user/username",
    "label": "Bookwyrm",
    "display_order": 3,
    "is_active": true
  }'
# Hide a link without deleting it
curl -X PATCH "http://localhost:8000/api/v1/social-media-links/1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'
curl "http://localhost:8000/api/v1/social-media-links" \
  -H "Authorization: Bearer $TOKEN"
curl -X DELETE "http://localhost:8000/api/v1/social-media-links/1" \
  -H "Authorization: Bearer $TOKEN"

Icon Mapping

The frontend automatically maps platform names to appropriate icons:

Platform Icon
MASTODON Mastodon logo
PIXELFED Photo icon
BOOKWYRM Book icon
GITHUB GitHub logo
GITLAB GitLab logo
LINKEDIN LinkedIn logo
WEBSITE World/globe icon
EMAIL Mail icon
MATRIX Matrix logo
PEERTUBE TV/video icon
LEMMY World icon
OTHER World icon

How It Works

  1. Backend: Social media links are stored in the social_media_links database table
  2. Public API: The /api/v1/social-media-links/public endpoint returns only active links
  3. Frontend: The footer component (components/homepage/footer.tsx) fetches links on page load
  4. Display: Only links with is_active=true are shown, ordered by display_order
  5. Icons: Platform-specific icons are automatically selected based on the platform field

Database Migration

To apply the database schema:

cd surfsense_backend
alembic upgrade head

This will create the social_media_links table and the socialmediaplatform enum type.

Security Notes

  • Admin endpoints require superuser privileges - regular users cannot manage links
  • Public endpoint is unauthenticated - anyone can view active links (as intended for homepage display)
  • No hardcoded URLs - all social media links come from the database
  • Validation - URLs are validated (max 500 chars), labels max 100 chars

Future Enhancements

Future improvements could include:

  1. Web UI: A graphical admin panel in the dashboard for managing links
  2. Drag-and-drop reordering: UI to easily reorder links by changing display_order
  3. Link analytics: Track clicks on social media links
  4. Per-user links: Allow different users to have different social links (currently global)
  5. Link preview: Show how the footer will look before saving changes

Troubleshooting

Links not appearing on homepage:

  1. Check that is_active=true for the link
  2. Verify the backend API is reachable from frontend
  3. Check browser console for fetch errors
  4. Confirm NEXT_PUBLIC_FASTAPI_BACKEND_URL environment variable is set correctly

Cannot create links:

  1. Ensure you're logged in as a superuser
  2. Check JWT token is valid and not expired
  3. Verify request payload matches the schema

Icons not displaying:

  1. Check that the platform name exactly matches one of the supported platforms (case-sensitive)
  2. Unsupported platforms will default to the world/globe icon