# 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](docs/PREREQUISITES.md) 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](docs/FFMPEG_SETUP.md)
- **libvips** (for image watermarking) - See [Prerequisites Guide](docs/PREREQUISITES.md)
- **Resend API Key** (for email verification)
- **Network-accessible storage** (for photo uploads)

### Installation

**Quick Setup (Recommended):**
```bash
# 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:**
   ```bash
   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:
   ```bash
   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:
   ```bash
   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):**
   ```bash
   PGPASSWORD=punimtag_password psql -h localhost -U punimtag -d punimtag -f grant_readonly_permissions.sql
   ```
   
   **Alternative methods:**
   ```bash
   # 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:**
   ```bash
   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)
   ```bash
   # 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)
   ```bash
   # Run as PostgreSQL superuser:
   psql -U postgres -d punimtag -f create_write_user.sql
   ```
   Then add to your `.env` file:
   ```bash
   DATABASE_URL_WRITE="postgresql://viewer_write:password@localhost:5432/punimtag"
   ```

4. **Create database tables for authentication:**
   ```bash
   # Run as PostgreSQL superuser:
   psql -U postgres -d punimtag_auth -f create_auth_tables.sql
   ```
   
   **Add pending_photos table for photo uploads:**
   ```bash
   # Run as PostgreSQL superuser:
   psql -U postgres -d punimtag_auth -f migrations/add-pending-photos-table.sql
   ```
   
   **Add email verification columns:**
   ```bash
   # Run as PostgreSQL superuser:
   psql -U postgres -d punimtag_auth -f migrations/add-email-verification-columns.sql
   ```
   
   Then grant permissions to your write user:
   ```sql
   -- 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:**
   ```bash
   npx prisma generate
   ```

6. **Run development server:**
   ```bash
   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:

```sql
-- 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](https://resend.com)
   - Create an API key in your dashboard
   - Add it to your `.env` file:
     ```bash
     RESEND_API_KEY="re_your_api_key_here"
     RESEND_FROM_EMAIL="noreply@yourdomain.com"
     ```

2. **Run the Database Migration:**
   ```bash
   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:**
   ```bash
   ssh user@db-server.example.com
   sudo mkdir -p /var/punimtag/uploads/pending-photos
   ```

2. **Mount database server on web server (via SSHFS):**
   ```bash
   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:**
   ```bash
   UPLOAD_DIR="/mnt/db-server-uploads/pending-photos"
   ```

**See full setup guide:** [`docs/NETWORK_SHARE_SETUP.md`](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:
   ```sql
   SELECT * FROM pending_photos WHERE status = 'pending' ORDER BY submitted_at;
   ```
3. **Update status** after review:
   ```sql
   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:
- [Quick Start Guide](../../punimtag/docs/PHOTO_VIEWER_QUICKSTART.md)
- [Complete Plan](../../punimtag/docs/PHOTO_VIEWER_PLAN.md)
- [Architecture](../../punimtag/docs/PHOTO_VIEWER_ARCHITECTURE.md)

## 🛠️ 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:**
   ```bash
   npm run check:permissions
   ```

2. **Grant permissions (WORKING METHOD - tested and confirmed):**
   ```bash
   PGPASSWORD=punimtag_password psql -h localhost -U punimtag -d punimtag -f grant_readonly_permissions.sql
   ```
   
   **Alternative methods:**
   ```bash
   # 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:**
   ```bash
   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.ts` → `remotePatterns`

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
