punimtag/viewer-frontend
tanyar09 830c7bcaa6
Some checks failed
CI / skip-ci-check (pull_request) Successful in 6s
CI / python-lint (pull_request) Has been cancelled
CI / test-backend (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / secret-scanning (pull_request) Has been cancelled
CI / dependency-scan (pull_request) Has been cancelled
CI / sast-scan (pull_request) Has been cancelled
CI / workflow-summary (pull_request) Has been cancelled
CI / lint-and-type-check (pull_request) Has been cancelled
feat: Update video thumbnail handling in viewer frontend
- Refactored video thumbnail generation logic to fetch thumbnails from the backend instead of generating them locally.
- Removed unused placeholder generation code for remote video URLs.
- Improved error handling for backend thumbnail fetch failures, returning appropriate responses.
- Enhanced caching strategy for fetched thumbnails to optimize performance.

These changes streamline the video thumbnail process and improve the overall user experience in the viewer interface.
2026-01-28 17:55:58 +00:00
..

PunimTag Photo Viewer

A modern, fast, and beautiful photo viewing website that connects to your PunimTag PostgreSQL database.

🚀 Quick Start

Prerequisites

See the Prerequisites Guide for a complete list of required and optional software.

Required:

  • Node.js 20+ (currently using 18.19.1 - may need upgrade)
  • PostgreSQL database with PunimTag schema
  • Read-only database user (see setup below)

Optional:

  • FFmpeg (for video thumbnail generation) - See FFmpeg Setup Guide
  • libvips (for image watermarking) - See Prerequisites Guide
  • Resend API Key (for email verification)
  • Network-accessible storage (for photo uploads)

Installation

Quick Setup (Recommended):

# Run the comprehensive setup script
npm run setup

This will:

  • Install all npm dependencies
  • Set up Sharp library (for image processing)
  • Generate Prisma clients
  • Set up database tables (if DATABASE_URL_AUTH is configured)
  • Create admin user (if needed)
  • Verify the setup

Manual Setup:

  1. Install dependencies:

    npm run install:deps
    # Or manually:
    npm install
    npm run prisma:generate:all
    

    The install script will:

    • Check Node.js version
    • Install npm dependencies
    • Set up Sharp library (for image processing)
    • Generate Prisma clients
    • Check for optional system dependencies (libvips, FFmpeg)
  2. Set up environment variables: Create a .env file in the root directory:

    DATABASE_URL="postgresql://viewer_readonly:password@localhost:5432/punimtag"
    DATABASE_URL_WRITE="postgresql://viewer_write:password@localhost:5432/punimtag"
    DATABASE_URL_AUTH="postgresql://viewer_write:password@localhost:5432/punimtag_auth"
    NEXTAUTH_SECRET="your-secret-key-here"
    NEXTAUTH_URL="http://localhost:3001"
    NEXT_PUBLIC_SITE_NAME="PunimTag Photo Viewer"
    NEXT_PUBLIC_SITE_DESCRIPTION="Family Photo Gallery"
    # Email verification (Resend)
    RESEND_API_KEY="re_your_resend_api_key_here"
    RESEND_FROM_EMAIL="noreply@yourdomain.com"
    # Optional: Override base URL for email links (defaults to NEXTAUTH_URL)
    # NEXT_PUBLIC_APP_URL="http://localhost:3001"
    # Upload directory for pending photos (REQUIRED - must be network-accessible)
    # RECOMMENDED: Use the same server as your database (see docs/NETWORK_SHARE_SETUP.md)
    # Examples:
    #   Database server via SSHFS: /mnt/db-server-uploads/pending-photos
    #   Separate network share: /mnt/shared/pending-photos
    #   Windows: \\server\share\pending-photos (mapped to drive)
    UPLOAD_DIR="/mnt/db-server-uploads/pending-photos"
    # Or use PENDING_PHOTOS_DIR as an alias
    # PENDING_PHOTOS_DIR="/mnt/network-share/pending-photos"
    

    Note: Generate a secure NEXTAUTH_SECRET using:

    openssl rand -base64 32
    
  3. Grant read-only permissions on main database tables:

    The read-only user needs SELECT permissions on all main tables. If you see "permission denied" errors, run:

    WORKING METHOD (tested and confirmed):

    PGPASSWORD=punimtag_password psql -h localhost -U punimtag -d punimtag -f grant_readonly_permissions.sql
    

    Alternative methods:

    # Using postgres user:
    PGPASSWORD=postgres_password psql -h localhost -U postgres -d punimtag -f grant_readonly_permissions.sql
    
    # Using sudo:
    sudo -u postgres psql -d punimtag -f grant_readonly_permissions.sql
    

    Check permissions:

    npm run check:permissions
    

    This will verify all required permissions and provide instructions if any are missing.

    For Face Identification (Write Access):

    You have two options to enable write access for face identification:

    Option 1: Grant write permissions to existing user (simpler)

    # Run as PostgreSQL superuser:
    psql -U postgres -d punimtag -f grant_write_permissions.sql
    

    Then use the same DATABASE_URL for both read and write operations.

    Option 2: Create a separate write user (more secure)

    # Run as PostgreSQL superuser:
    psql -U postgres -d punimtag -f create_write_user.sql
    

    Then add to your .env file:

    DATABASE_URL_WRITE="postgresql://viewer_write:password@localhost:5432/punimtag"
    
  4. Create database tables for authentication:

    # Run as PostgreSQL superuser:
    psql -U postgres -d punimtag_auth -f create_auth_tables.sql
    

    Add pending_photos table for photo uploads:

    # Run as PostgreSQL superuser:
    psql -U postgres -d punimtag_auth -f migrations/add-pending-photos-table.sql
    

    Add email verification columns:

    # Run as PostgreSQL superuser:
    psql -U postgres -d punimtag_auth -f migrations/add-email-verification-columns.sql
    

    Then grant permissions to your write user:

    -- If using viewer_write user:
    GRANT SELECT, INSERT, UPDATE ON TABLE users TO viewer_write;
    GRANT SELECT, INSERT, UPDATE ON TABLE pending_identifications TO viewer_write;
    GRANT SELECT, INSERT, UPDATE ON TABLE pending_photos TO viewer_write;
    GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO viewer_write;
    GRANT USAGE, SELECT ON SEQUENCE pending_identifications_id_seq TO viewer_write;
    GRANT USAGE, SELECT ON SEQUENCE pending_photos_id_seq TO viewer_write;
    
    -- Or if using viewer_readonly with write permissions:
    GRANT SELECT, INSERT, UPDATE ON TABLE users TO viewer_readonly;
    GRANT SELECT, INSERT, UPDATE ON TABLE pending_identifications TO viewer_readonly;
    GRANT SELECT, INSERT, UPDATE ON TABLE pending_photos TO viewer_readonly;
    GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO viewer_readonly;
    GRANT USAGE, SELECT ON SEQUENCE pending_identifications_id_seq TO viewer_readonly;
    GRANT USAGE, SELECT ON SEQUENCE pending_photos_id_seq TO viewer_readonly;
    
  5. Generate Prisma client:

    npx prisma generate
    
  6. Run development server:

    npm run dev
    
  7. Open your browser: Navigate to http://localhost:3000

📁 Project Structure

punimtag-viewer/
├── app/                    # Next.js 14 App Router
│   ├── layout.tsx         # Root layout
│   ├── page.tsx           # Home page (photo grid with search)
│   ├── HomePageContent.tsx # Client component for home page
│   ├── search/            # Search page
│   │   ├── page.tsx       # Search page
│   │   └── SearchContent.tsx # Search content component
│   └── api/               # API routes
│       ├── search/        # Search API endpoint
│       └── photos/        # Photo API endpoints
├── components/            # React components
│   ├── PhotoGrid.tsx     # Photo grid with tooltips
│   ├── search/           # Search components
│   │   ├── CollapsibleSearch.tsx # Collapsible search bar
│   │   ├── FilterPanel.tsx      # Filter panel
│   │   ├── PeopleFilter.tsx     # People filter
│   │   ├── DateRangeFilter.tsx  # Date range filter
│   │   ├── TagFilter.tsx        # Tag filter
│   │   └── SearchBar.tsx        # Search bar component
│   └── ui/               # shadcn/ui components
├── lib/                   # Utilities
│   ├── db.ts             # Prisma client
│   └── queries.ts        # Database query helpers
├── prisma/
│   └── schema.prisma     # Database schema
└── public/               # Static assets

🔐 Database Setup

Create Read-Only User

On your PostgreSQL server, run:

-- Create read-only user
CREATE USER viewer_readonly WITH PASSWORD 'your_secure_password';

-- Grant permissions
GRANT CONNECT ON DATABASE punimtag TO viewer_readonly;
GRANT USAGE ON SCHEMA public TO viewer_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO viewer_readonly;

-- Grant on future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public 
GRANT SELECT ON TABLES TO viewer_readonly;

-- Verify no write permissions
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM viewer_readonly;

🎨 Features

  • Photo grid with responsive layout
  • Image optimization with Next.js Image
  • Read-only database access
  • Type-safe queries with Prisma
  • Modern, clean design
  • Collapsible search bar on main page with filters
  • Search functionality - Search by people, dates, and tags
  • Photo tooltips - Hover over photos to see people names
  • Search page - Dedicated search page at /search
  • Filter panel - People, date range, and tag filters

✉️ Email Verification

The application includes email verification for new user registrations. Users must verify their email address before they can sign in.

Setup

  1. Get a Resend API Key:

    • Sign up at resend.com
    • Create an API key in your dashboard
    • Add it to your .env file:
      RESEND_API_KEY="re_your_api_key_here"
      RESEND_FROM_EMAIL="noreply@yourdomain.com"
      
  2. Run the Database Migration:

    psql -U postgres -d punimtag_auth -f migrations/add-email-verification-columns.sql
    
  3. Configure Email Domain (Optional):

    • For production, verify your domain in Resend
    • Update RESEND_FROM_EMAIL to use your verified domain
    • For development, you can use Resend's test domain (onboarding@resend.dev)

How It Works

  1. Registration: When a user signs up, they receive a confirmation email with a verification link
  2. Verification: Users click the link to verify their email address
  3. Login: Users must verify their email before they can sign in
  4. Resend: Users can request a new confirmation email if needed

Features

  • Secure token-based verification (24-hour expiration)
  • Email verification required before login
  • Resend confirmation email functionality
  • User-friendly error messages
  • Backward compatible (existing users are auto-verified)

📤 Photo Uploads

Users can upload photos for admin review. Uploaded photos are stored on a network-accessible location (required) and tracked in the database.

Storage Location

Uploaded photos are stored in a directory structure organized by user ID:

{UPLOAD_DIR}/
  └── {userId}/
      └── {timestamp}-{filename}

Configuration (REQUIRED):

  • Must set UPLOAD_DIR or PENDING_PHOTOS_DIR environment variable
  • Must point to a network-accessible location (database server recommended)
  • The directory will be created automatically if it doesn't exist

Recommended: Use Database Server

The simplest setup is to use the same server where your PostgreSQL database is located:

  1. Create directory on database server:

    ssh user@db-server.example.com
    sudo mkdir -p /var/punimtag/uploads/pending-photos
    
  2. Mount database server on web server (via SSHFS):

    sudo apt-get install sshfs
    sudo mkdir -p /mnt/db-server-uploads
    sudo sshfs user@db-server.example.com:/var/punimtag/uploads /mnt/db-server-uploads
    
  3. Set in .env:

    UPLOAD_DIR="/mnt/db-server-uploads/pending-photos"
    

See full setup guide: docs/NETWORK_SHARE_SETUP.md

Important:

  • Ensure the web server process has read/write permissions
  • The approval system must have read access to the same location
  • Test network connectivity and permissions before deploying

Database Tracking

Upload metadata is stored in the pending_photos table in the punimtag_auth database:

  • File location and metadata
  • User who uploaded
  • Status: pending, approved, rejected
  • Review information (when reviewed, by whom, rejection reason)

Access for Approval System

The approval system can:

  1. Read files from disk using the file_path from the database
  2. Query the database for pending photos:
    SELECT * FROM pending_photos WHERE status = 'pending' ORDER BY submitted_at;
    
  3. Update status after review:
    UPDATE pending_photos 
    SET status = 'approved', reviewed_at = NOW(), reviewed_by = {admin_user_id}
    WHERE id = {photo_id};
    

🚧 Coming Soon

  • Photo detail page with lightbox
  • Infinite scroll
  • Favorites system
  • People and tags browsers
  • Authentication (optional)

📚 Documentation

For complete documentation, see:

🛠️ Development

Available Scripts

  • npm run dev - Start development server
  • npm run build - Build for production
  • npm run start - Start production server
  • npm run lint - Run ESLint
  • npm run check:permissions - Check database permissions and provide fix instructions

Prisma Commands

  • npx prisma generate - Generate Prisma client
  • npx prisma studio - Open Prisma Studio (database browser)
  • npx prisma db pull - Pull schema from database

🔍 Troubleshooting

Permission Denied Errors

If you see "permission denied for table photos" errors:

  1. Check permissions:

    npm run check:permissions
    
  2. Grant permissions (WORKING METHOD - tested and confirmed):

    PGPASSWORD=punimtag_password psql -h localhost -U punimtag -d punimtag -f grant_readonly_permissions.sql
    

    Alternative methods:

    # Using postgres user:
    PGPASSWORD=postgres_password psql -h localhost -U postgres -d punimtag -f grant_readonly_permissions.sql
    
    # Using sudo:
    sudo -u postgres psql -d punimtag -f grant_readonly_permissions.sql
    
  3. Or check health endpoint:

    curl http://localhost:3001/api/health
    

Database Connection Issues

  • Verify DATABASE_URL is set correctly in .env
  • Check that the database user exists and has the correct password
  • Ensure PostgreSQL is running and accessible

⚠️ Known Issues

  • Node.js version: Currently using Node 18.19.1, but Next.js 16 requires >=20.9.0
    • Solution: Upgrade Node.js or use Node Version Manager (nvm)

📝 Notes

Image Serving (Hybrid Approach)

The application automatically detects and handles two types of photo storage:

  1. HTTP/HTTPS URLs (SharePoint, CDN, etc.)

    • If photo.path starts with http:// or https://, images are served directly
    • Next.js Image optimization is applied automatically
    • Configure allowed domains in next.config.tsremotePatterns
  2. File System Paths (Local storage)

    • If photo.path is a file system path, images are served via API proxy
    • Make sure photo file paths are accessible from the Next.js server
    • No additional configuration needed

Benefits:

  • Works with both SharePoint URLs and local file system
  • Automatic detection - no configuration needed per photo
  • Optimal performance for both storage types
  • No N+1 database queries (path passed via query parameter)

Search Features

The application includes a powerful search system:

  1. Collapsible Search Bar (Main Page)

    • Minimized by default to save space
    • Click to expand and reveal full filter panel
    • Shows active filter count badge
    • Filters photos in real-time
  2. Search Filters

    • People Filter: Multi-select searchable dropdown
    • Date Range Filter: Presets (Today, This Week, This Month, This Year) or custom range
    • Tag Filter: Multi-select searchable tag filter
    • All filters work together with AND logic
  3. Photo Tooltips

    • Hover over any photo to see people names
    • Shows "People: Name1, Name2" if people are identified
    • Falls back to filename if no people identified
  4. Search Page (/search)

    • Dedicated search page with full filter panel
    • URL query parameter sync for shareable search links
    • Pagination support

🤝 Contributing

This is a private project. For questions or issues, refer to the main PunimTag documentation.


Built with: Next.js 14, React, TypeScript, Prisma, Tailwind CSS