Humanize admin user guide and archive scratch plans

Rewrite the admin USER_GUIDE opening, strip emoji decoration from live
guides, and keep historical PHOTO_VIEWER/FIX plans under docs/archive.
This commit is contained in:
2026-08-05 15:33:22 -04:00
parent 655a12963b
commit 8fd0aa8c0a
16 changed files with 98 additions and 5999 deletions
+3 -3
View File
@@ -6,7 +6,7 @@ Snapshot after Chabad Blue chrome parity (navy sidebar, theme toggle, shared tok
- Same brand tokens as viewer (`#0038A8`, gold `#C4A35A`, Rubik/Heebo).
- Light/dark toggle (persisted `punimtag-admin-theme`) + FOUC-safe boot script.
- Navy rail makes admin *read* as JRCC immediately (buttons alone were too weak).
- Navy rail makes admin read as JRCC immediately (buttons alone were too weak).
- Skip link, `:focus-visible`, `prefers-reduced-motion`, clearer page titles (no emoji soup).
- Merge people, Auto-Match progress, Identify keyboard nav already shipped.
@@ -21,7 +21,7 @@ Snapshot after Chabad Blue chrome parity (navy sidebar, theme toggle, shared tok
| P2 | iOS hamburger only — no desktop collapse | Optional collapsed icon rail for large monitors |
| P3 | Viewer filter comboboxes lack accessible names (axe `button-name`) | Label each Select trigger; then restore full-page axe on gallery home |
| Done | Admin Home was a SaaS marketing splash (orange/blue hero, emoji placeholders) | Quiet ops home: welcome + workflow links + recent processed strip |
| P3 | Emoji leftovers in Help / some toasts | Plain language + lucide/SVG icons over time |
| P3 | Emoji leftovers in Help / some toasts (remove over time) | Plain language + lucide/SVG icons over time |
## Accessibility status
@@ -33,7 +33,7 @@ Snapshot after Chabad Blue chrome parity (navy sidebar, theme toggle, shared tok
| Systematic axe CI | Smoke (`e2e/tests/a11y.smoke.spec.ts`) | Smoke (viewer home) |
| Form labels / live regions | Partial (login, toasts, some dialogs) | Stronger on gallery controls |
## What’s next (product)
## Next (product)
1. **People hub IA** (Identify / Auto-Match / Modify tabs) — biggest daily UX win.
2. **Phase 5 junk reject** (blur/pose) + soak Immich gates with real `match_decisions`.
-680
View File
@@ -1,680 +0,0 @@
# Auto-Match Automation Plan
**Version:** 1.0
**Created:** November 2025
**Status:** Planning Phase
---
## Executive Summary
This plan outlines the implementation of automated face matching in the Auto-Match feature. Currently, users must manually select faces on the right panel to match with the person on the left panel. This plan automates that process by automatically identifying faces that meet specific criteria:
1. **Reference face (left panel)** must be frontal (not portrait)
2. **Match faces (right panel)** must be frontal (not portrait)
3. **Match similarity** must be high (configurable threshold, default ≥70%)
When these conditions are met, faces are automatically identified to the person without user intervention.
---
## Current State Analysis
### Current Auto-Match Workflow
**Frontend Flow:**
1. User navigates to Auto-Match tab
2. Auto-match starts automatically (calls `POST /api/v1/faces/auto-match`)
3. Results display:
- **Left Panel:** Identified person with reference face
- **Right Panel:** Unidentified faces that match the reference face
4. User manually selects faces via checkboxes
5. User clicks "Save changes" button
6. Frontend calls `POST /api/v1/people/{person_id}/accept-matches` with selected face IDs
**Backend Flow:**
1. `POST /api/v1/faces/auto-match` endpoint:
- Gets all identified people (one reference face per person)
- For each person, finds similar unidentified faces using cosine similarity
- Filters by tolerance (default 0.6, maps to confidence ≥40%)
- Returns matches with similarity percentage (0-100%)
2. `POST /api/v1/people/{person_id}/accept-matches` endpoint:
- Accepts list of face IDs
- Sets `person_id` on each face
- Creates `person_encodings` records
- Commits changes
### Data Available
**Face Model Fields:**
- `id`: Face ID
- `person_id`: Person ID (NULL for unidentified)
- `photo_id`: Photo ID
- `encoding`: Face encoding (512-dimensional vector)
- `location`: Face bounding box (JSON)
- `confidence`: Detection confidence
- `quality_score`: Quality score (0.0-1.0)
- `pose_mode`: Pose classification (default: 'frontal')
- `yaw_angle`: Yaw angle in degrees (optional)
- `pitch_angle`: Pitch angle in degrees (optional)
- `roll_angle`: Roll angle in degrees (optional)
**AutoMatchFaceItem Schema:**
```python
{
"id": int,
"photo_id": int,
"photo_filename": str,
"location": str,
"quality_score": float,
"similarity": float, # 0-100 percentage
"distance": float
}
```
**AutoMatchPersonItem Schema:**
```python
{
"person_id": int,
"person_name": str,
"reference_face_id": int,
"reference_photo_id": int,
"reference_photo_filename": str,
"reference_location": str,
"face_count": int,
"matches": [AutoMatchFaceItem],
"total_matches": int
}
```
### Limitations
1. **No pose_mode in API response:** Current API doesn't return `pose_mode` for faces
2. **No automatic acceptance:** All matches require manual selection
3. **No filtering by pose:** Cannot filter out profile/portrait faces
4. **No high-confidence threshold:** No automatic filtering by match confidence
---
## Requirements
### Functional Requirements
1. **Automated Face Matching:**
- When "Start Auto-Match" button is clicked, automatically identify faces that meet criteria
- Only process persons whose reference face is frontal (`pose_mode == 'frontal'`)
- Only match with unidentified faces that are frontal (`pose_mode == 'frontal'`)
- Only match faces with similarity ≥70% (configurable threshold)
- Automatically call accept-matches API for qualifying faces
2. **Pose-Based Filtering:**
- Check reference face `pose_mode` field
- Check match face `pose_mode` field
- Skip matching if reference face is not frontal
- Skip matching if match face is not frontal
3. **Similarity Threshold:**
- Configurable threshold (default: 70%)
- Only auto-match faces with similarity ≥ threshold
- Threshold should be adjustable via UI (optional for Phase 1)
4. **User Feedback:**
- Show progress/status during auto-matching
- Display summary: how many faces were auto-matched vs. skipped
- Show which persons were processed and which were skipped (due to non-frontal reference)
5. **Backward Compatibility:**
- Keep existing manual selection workflow
- Auto-match should be opt-in (button click)
- Manual selection should still work after auto-match
### Technical Requirements
1. **API Enhancements:**
- Add `pose_mode` to `AutoMatchFaceItem` response
- Add `pose_mode` to `AutoMatchPersonItem` reference face info
- Add optional `auto_accept` parameter to auto-match endpoint
- Add `auto_accept_threshold` parameter (default: 70.0)
2. **Backend Logic:**
- Filter reference faces by `pose_mode == 'frontal'` in auto-match query
- Filter match faces by `pose_mode == 'frontal'` in similarity search
- Filter matches by similarity threshold
- Automatically call `accept_auto_match_matches` for qualifying faces
3. **Frontend Changes:**
- Update "Start Auto-Match" button to trigger auto-acceptance
- Add loading state during auto-matching
- Display results summary (auto-matched count, skipped count)
- Update UI to show pose_mode for faces (optional)
---
## Technical Approach
### Architecture Overview
```
┌─────────────────────────────────────────────────────────┐
│ Auto-Match Automation Flow │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. User clicks "Start Auto-Match" button │
│ └─> Frontend: POST /api/v1/faces/auto-match │
│ with auto_accept=true, threshold=70 │
│ │
│ 2. Backend: Find all identified people │
│ └─> Filter: Only persons with frontal reference face │
│ (pose_mode == 'frontal') │
│ │
│ 3. For each person (with frontal reference): │
│ └─> Find similar unidentified faces │
│ └─> Filter: Only frontal faces │
│ (pose_mode == 'frontal') │
│ └─> Filter: Similarity ≥ threshold (70%) │
│ └─> Auto-accept: Call accept_auto_match_matches │
│ │
│ 4. Return summary: │
│ └─> Total persons processed │
│ └─> Total faces auto-matched │
│ └─> Persons skipped (non-frontal reference) │
│ └─> Matches found but not auto-matched │
│ │
└─────────────────────────────────────────────────────────┘
```
### Phase 1: Backend API Enhancements
#### Step 1.1: Update Auto-Match Request Schema
**File: `src/web/schemas/faces.py`**
Add optional parameters to `AutoMatchRequest`:
```python
class AutoMatchRequest(BaseModel):
tolerance: float
auto_accept: bool = False # New: Enable auto-acceptance
auto_accept_threshold: float = 70.0 # New: Similarity threshold (0-100)
```
#### Step 1.2: Update Auto-Match Response Schema
**File: `src/web/schemas/faces.py`**
Add `pose_mode` to `AutoMatchFaceItem`:
```python
class AutoMatchFaceItem(BaseModel):
id: int
photo_id: int
photo_filename: str
location: str
quality_score: float
similarity: float # Confidence percentage (0-100)
distance: float
pose_mode: str = "frontal" # New: Pose classification
```
Add `pose_mode` to `AutoMatchPersonItem` reference face:
```python
class AutoMatchPersonItem(BaseModel):
person_id: int
person_name: str
reference_face_id: int
reference_photo_id: int
reference_photo_filename: str
reference_location: str
reference_pose_mode: str = "frontal" # New: Reference face pose
face_count: int
matches: list[AutoMatchFaceItem]
total_matches: int
```
Add summary fields to `AutoMatchResponse`:
```python
class AutoMatchResponse(BaseModel):
people: list[AutoMatchPersonItem]
total_people: int
total_matches: int
# New: Auto-accept summary
auto_accepted: bool = False
auto_accepted_faces: int = 0
skipped_persons: int = 0 # Persons with non-frontal reference
skipped_matches: int = 0 # Matches that didn't meet criteria
```
#### Step 1.3: Update Auto-Match Service Function
**File: `src/web/services/face_service.py`**
Modify `find_auto_match_matches` to:
1. Filter reference faces by `pose_mode == 'frontal'`
2. Include `pose_mode` in results
```python
def find_auto_match_matches(
db: Session,
tolerance: float = None,
filter_frontal_only: bool = True, # New parameter
) -> List[Tuple[int, int, Face, List[Tuple[Face, float, float]]]]:
"""Find auto-match matches for all identified people.
Args:
tolerance: Similarity tolerance (default: 0.6)
filter_frontal_only: Only include persons with frontal reference face
"""
# ... existing code ...
# Filter reference faces by pose_mode if requested
if filter_frontal_only:
identified_faces = [
f for f in identified_faces
if f.pose_mode == 'frontal'
]
# ... rest of existing code ...
```
#### Step 1.4: Update Auto-Match API Endpoint
**File: `src/web/api/faces.py`**
Modify `auto_match_faces` endpoint to:
1. Accept `auto_accept` and `auto_accept_threshold` parameters
2. Filter matches by pose_mode and similarity threshold
3. Auto-accept qualifying faces
4. Return summary statistics
```python
@router.post("/auto-match", response_model=AutoMatchResponse)
def auto_match_faces(
request: AutoMatchRequest,
db: Session = Depends(get_db),
) -> AutoMatchResponse:
"""Start auto-match process with optional auto-acceptance.
If auto_accept=True:
- Only processes persons with frontal reference faces
- Only matches with frontal unidentified faces
- Only auto-accepts matches with similarity ≥ threshold
"""
from src.web.db.models import Person, Photo, Face
# Find matches for all identified people
# Filter by frontal reference faces if auto_accept enabled
matches_data = find_auto_match_matches(
db,
tolerance=request.tolerance,
filter_frontal_only=request.auto_accept
)
# Track statistics
auto_accepted_faces = 0
skipped_persons = 0
skipped_matches = 0
# If auto_accept enabled, process matches automatically
if request.auto_accept:
for person_id, reference_face_id, reference_face, similar_faces in matches_data:
# Filter matches by criteria:
# 1. Match face must be frontal
# 2. Similarity must be ≥ threshold
qualifying_faces = []
for face, distance, confidence_pct in similar_faces:
# Check pose_mode
if face.pose_mode != 'frontal':
skipped_matches += 1
continue
# Check similarity threshold
if confidence_pct < request.auto_accept_threshold:
skipped_matches += 1
continue
qualifying_faces.append(face.id)
# Auto-accept qualifying faces
if qualifying_faces:
try:
identified_count, updated_count = accept_auto_match_matches(
db, person_id, qualifying_faces
)
auto_accepted_faces += identified_count
except Exception as e:
print(f"Error auto-accepting matches for person {person_id}: {e}")
# Build response (existing logic)
people_items = []
total_matches = 0
for person_id, reference_face_id, reference_face, similar_faces in matches_data:
# ... existing person building logic ...
# Add pose_mode to reference face info
reference_pose_mode = reference_face.pose_mode or 'frontal'
# Build matches list with pose_mode
match_items = []
for face, distance, confidence_pct in similar_faces:
match_photo = db.query(Photo).filter(Photo.id == face.photo_id).first()
if not match_photo:
continue
match_items.append(
AutoMatchFaceItem(
id=face.id,
photo_id=face.photo_id,
photo_filename=match_photo.filename,
location=face.location,
quality_score=float(face.quality_score),
similarity=confidence_pct,
distance=distance,
pose_mode=face.pose_mode or 'frontal', # New
)
)
if match_items:
people_items.append(
AutoMatchPersonItem(
person_id=person_id,
person_name=person_name,
reference_face_id=reference_face_id,
reference_photo_id=reference_face.photo_id,
reference_photo_filename=reference_photo.filename,
reference_location=reference_face.location,
reference_pose_mode=reference_pose_mode, # New
face_count=face_count,
matches=match_items,
total_matches=len(match_items),
)
)
total_matches += len(match_items)
return AutoMatchResponse(
people=people_items,
total_people=len(people_items),
total_matches=total_matches,
auto_accepted=request.auto_accept,
auto_accepted_faces=auto_accepted_faces,
skipped_persons=skipped_persons,
skipped_matches=skipped_matches,
)
```
#### Step 1.5: Update Similar Face Search to Include Pose Mode
**File: `src/web/services/face_service.py`**
Ensure `find_similar_faces` returns faces with pose_mode information:
```python
def find_similar_faces(
db: Session,
reference_face_id: int,
limit: int = 100,
tolerance: float = None,
filter_frontal_only: bool = False, # New parameter
) -> List[Tuple[Face, float, float]]:
"""Find similar faces to reference face.
Args:
filter_frontal_only: Only return frontal faces
"""
# ... existing similarity search ...
# Filter by pose_mode if requested
if filter_frontal_only:
similar_faces = [
(face, distance, confidence_pct)
for face, distance, confidence_pct in similar_faces
if face.pose_mode == 'frontal'
]
return similar_faces
```
### Phase 2: Frontend Updates
#### Step 2.1: Update AutoMatch API Interface
**File: `frontend/src/api/faces.ts`**
Update `AutoMatchRequest` interface:
```typescript
export interface AutoMatchRequest {
tolerance: number
auto_accept?: boolean // New: Enable auto-acceptance
auto_accept_threshold?: number // New: Similarity threshold (0-100)
}
```
Update `AutoMatchFaceItem` interface:
```typescript
export interface AutoMatchFaceItem {
id: number
photo_id: number
photo_filename: string
location: string
quality_score: number
similarity: number // Confidence percentage (0-100)
distance: number
pose_mode?: string // New: Pose classification
}
```
Update `AutoMatchPersonItem` interface:
```typescript
export interface AutoMatchPersonItem {
person_id: number
person_name: string
reference_face_id: number
reference_photo_id: number
reference_photo_filename: string
reference_location: string
reference_pose_mode?: string // New: Reference face pose
face_count: number
matches: AutoMatchFaceItem[]
total_matches: number
}
```
Update `AutoMatchResponse` interface:
```typescript
export interface AutoMatchResponse {
people: AutoMatchPersonItem[]
total_people: number
total_matches: number
auto_accepted?: boolean // New
auto_accepted_faces?: number // New
skipped_persons?: number // New
skipped_matches?: number // New
}
```
#### Step 2.2: Update AutoMatch Component
**File: `frontend/src/pages/AutoMatch.tsx`**
1. Add state for auto-accept threshold:
```typescript
const [autoAcceptThreshold, setAutoAcceptThreshold] = useState(70)
```
2. Update `startAutoMatch` function to use auto-accept:
```typescript
const startAutoMatch = async () => {
if (tolerance < 0 || tolerance > 1) {
alert('Please enter a valid tolerance value between 0.0 and 1.0.')
return
}
setBusy(true)
try {
const response = await facesApi.autoMatch({
tolerance,
auto_accept: true, // Enable auto-acceptance
auto_accept_threshold: autoAcceptThreshold
})
// Show summary
if (response.auto_accepted) {
const summary = [
`✅ Auto-matched ${response.auto_accepted_faces || 0} faces`,
response.skipped_persons ? `⚠️ Skipped ${response.skipped_persons} persons (non-frontal reference)` : '',
response.skipped_matches ? `ℹ️ Skipped ${response.skipped_matches} matches (didn't meet criteria)` : ''
].filter(Boolean).join('\n')
alert(summary)
}
if (response.people.length === 0) {
alert('🔍 No similar faces found for auto-identification')
setBusy(false)
return
}
setPeople(response.people)
// ... rest of existing code ...
} catch (error) {
console.error('Auto-match failed:', error)
alert('Failed to start auto-match. Please try again.')
} finally {
setBusy(false)
}
}
```
3. Add threshold input field (optional):
```tsx
<div className="flex items-center gap-2">
<label className="text-sm font-medium text-gray-700">Auto-Accept Threshold:</label>
<input
type="number"
min="0"
max="100"
step="5"
value={autoAcceptThreshold}
onChange={(e) => setAutoAcceptThreshold(parseInt(e.target.value) || 70)}
disabled={isActive}
className="w-20 px-2 py-1 border border-gray-300 rounded text-sm"
/>
<span className="text-xs text-gray-500">% (min similarity)</span>
</div>
```
4. Update UI to show pose_mode for faces (optional visual indicator):
```tsx
{/* Show pose mode badge if not frontal */}
{match.pose_mode && match.pose_mode !== 'frontal' && (
<span className="px-2 py-1 rounded text-xs bg-orange-100 text-orange-800">
{match.pose_mode}
</span>
)}
```
### Phase 3: Testing & Validation
#### Step 3.1: Unit Tests
**File: `tests/test_auto_match_automation.py`**
1. Test auto-match with auto_accept enabled
2. Test filtering by frontal faces only
3. Test similarity threshold filtering
4. Test skipping non-frontal reference faces
5. Test summary statistics
#### Step 3.2: Integration Tests
1. Test full auto-match flow with auto-acceptance
2. Test with mixed frontal/non-frontal faces
3. Test with various similarity thresholds
4. Verify database updates (person_id, person_encodings)
#### Step 3.3: Manual Testing
1. Create test data with frontal and non-frontal faces
2. Run auto-match with auto_accept enabled
3. Verify only frontal faces are matched
4. Verify only high-similarity faces are auto-accepted
5. Verify summary statistics are correct
---
## Implementation Plan
### Phase 1: Backend (2-3 days)
1. ✅ Update schemas (AutoMatchRequest, AutoMatchResponse, etc.)
2. ✅ Update `find_auto_match_matches` to filter by pose_mode
3. ✅ Update auto-match API endpoint with auto-accept logic
4. ✅ Update `find_similar_faces` to filter by pose_mode
5. ✅ Add unit tests for filtering logic
### Phase 2: Frontend (1-2 days)
1. ✅ Update API interfaces
2. ✅ Update AutoMatch component to use auto-accept
3. ✅ Add threshold input field (optional)
4. ✅ Add summary display
5. ✅ Add pose_mode indicators (optional)
### Phase 3: Testing (1-2 days)
1. ✅ Write unit tests
2. ✅ Write integration tests
3. ✅ Manual testing with real data
4. ✅ Performance testing
**Total Estimated Time: 4-7 days**
---
## Configuration
### Default Values
- **Auto-accept threshold:** 70% similarity
- **Pose mode filter:** Only frontal faces (`pose_mode == 'frontal'`)
- **Tolerance:** 0.6 (existing default)
### Future Enhancements (Not in Phase 1)
1. **Configurable pose mode filter:** Allow users to specify which pose modes to include
2. **Per-person threshold:** Different thresholds for different people
3. **Batch processing:** Process multiple persons in parallel
4. **Progress tracking:** Real-time progress updates during auto-matching
5. **Undo functionality:** Ability to undo auto-accepted matches
6. **Logging/Audit trail:** Track which faces were auto-matched and when
---
## Edge Cases & Error Handling
1. **No frontal reference faces:** Skip person, show in summary
2. **No frontal match faces:** Skip matches, show in summary
3. **Database errors:** Rollback transaction, show error message
4. **Missing pose_mode data:** Default to 'frontal' (backward compatibility)
5. **Concurrent updates:** Use database transactions to prevent conflicts
---
## Success Criteria
1. ✅ Auto-match automatically identifies faces meeting criteria
2. ✅ Only frontal faces (reference and matches) are processed
3. ✅ Only high-similarity matches (≥70%) are auto-accepted
4. ✅ Summary statistics are accurate
5. ✅ Manual selection still works after auto-match
6. ✅ No breaking changes to existing API
---
## Notes
- This plan assumes portrait detection is already implemented (which it is)
- The `pose_mode` field should already exist in the database
- Backward compatibility is maintained: existing API calls without `auto_accept` work as before
- The "Start Auto-Match" button will trigger auto-acceptance when clicked
- Users can still manually select/deselect faces after auto-matching
-465
View File
@@ -1,465 +0,0 @@
# Database Architecture Review
**Created:** 2024
**Purpose:** Comprehensive review of main database and auth database architecture, configuration, and production deployment options.
---
## Table of Contents
1. [Main Database (punimtag)](#main-database-punimtag)
2. [Auth Database (punimtag_auth)](#auth-database-punimtag_auth)
3. [SQLite vs PostgreSQL Configuration](#sqlite-vs-postgresql-configuration)
4. [Production Deployment Architecture](#production-deployment-architecture)
5. [Key Differences Summary](#key-differences-summary)
6. [Connection Management](#connection-management)
7. [Decision Points](#decision-points)
---
## Main Database (punimtag)
### Configuration
- **Environment Variable:** `DATABASE_URL`
- **Default:** SQLite (`sqlite:///data/punimtag.db`)
- **Production:** PostgreSQL (`postgresql+psycopg2://punimtag:<choose-a-password>@localhost:5432/punimtag`)
- **Connection Pool (PostgreSQL):** pool_size=10, max_overflow=20, pool_recycle=3600
### Purpose
Stores all photo metadata, face detections, people records, tags, and backend user accounts.
### Schema Overview
The main database contains the core application data with the following tables:
#### Core Tables
1. **`photos`** - Photo metadata
- `id`, `path` (unique), `filename`, `date_added`, `date_taken`, `processed`, `file_hash`, `media_type`
- Relationships: faces, photo_tags, favorites, video_people
2. **`people`** - Person records
- `id`, `first_name`, `last_name`, `middle_name`, `maiden_name`, `date_of_birth`, `created_date`
- Unique constraint on (first_name, last_name, middle_name, maiden_name, date_of_birth)
- Relationships: faces, person_encodings, video_photos
3. **`faces`** - Face detection data
- `id`, `photo_id` (FK), `person_id` (FK), `encoding` (BLOB), `location`, `confidence`, `quality_score`
- Additional fields: `detector_backend`, `model_name`, `face_confidence`, `exif_orientation`, `pose_mode`
- Angles: `yaw_angle`, `pitch_angle`, `roll_angle`
- `landmarks` (JSON string), `identified_by_user_id` (FK), `excluded` (boolean)
- Relationships: photo, person, person_encodings
4. **`person_encodings`** - Person face encodings for matching
- `id`, `person_id` (FK), `face_id` (FK), `encoding` (BLOB), `quality_score`
- `detector_backend`, `model_name`, `created_date`
- Relationships: person, face
5. **`tags`** - Tag definitions
- `id`, `tag_name` (unique), `created_date`
- Relationships: photo_tags
6. **`phototaglinkage`** - Photo-tag relationships
- `linkage_id` (PK), `photo_id` (FK), `tag_id` (FK), `linkage_type` (0=single, 1=bulk), `created_date`
- Unique constraint on (photo_id, tag_id)
- Relationships: photo, tag
7. **`users`** (Main Database) - Backend user accounts
- `id`, `username` (unique), `password_hash`, `email` (unique), `full_name`
- `is_active`, `is_admin`, `role`, `password_change_required`
- `created_date`, `last_login`
- Relationships: faces (identified_by_user_id), photo_person_linkage
8. **`photo_favorites`** - User-specific favorites
- `id`, `username`, `photo_id` (FK), `created_date`
- Unique constraint on (username, photo_id)
- Relationships: photo
9. **`photo_person_linkage`** - Direct photo-person associations (for videos)
- `id`, `photo_id` (FK), `person_id` (FK), `identified_by_user_id` (FK), `created_date`
- Unique constraint on (photo_id, person_id)
- Relationships: photo, person, user
10. **`role_permissions`** - Role-to-feature permission matrix
- `id`, `role`, `feature_key`, `allowed`
- Unique constraint on (role, feature_key)
---
## Auth Database (punimtag_auth)
### Configuration
- **Environment Variable:** `DATABASE_URL_AUTH`
- **Database Type:** PostgreSQL (always required)
- **Connection String:** `postgresql+psycopg2://punimtag:<choose-a-password>@localhost:5432/punimtag_auth`
- **Connection Pool:** pool_size=10, max_overflow=20, pool_recycle=3600
### Purpose
Separate database for frontend website users and pending operations, keeping the main database read-only.
### Schema Overview
#### Tables
1. **`users`** (Auth Database) - Frontend website user accounts
- `id`, `email` (unique), `name`, `password_hash`
- `is_admin`, `has_write_access`, `email_verified`
- `email_confirmation_token`, `email_confirmation_token_expiry`
- `password_reset_token`, `password_reset_token_expiry`
- `is_active`, `created_at`, `updated_at`
- Relationships: pending_identifications, pending_photos, inappropriate_photo_reports, pending_linkages, photo_favorites
2. **`pending_identifications`** - Face identifications pending approval
- `id`, `face_id` (references main DB, no FK), `user_id` (FK), `first_name`, `last_name`
- `middle_name`, `maiden_name`, `date_of_birth`, `status` (pending/approved/rejected)
- `created_at`, `updated_at`
- Relationships: user
- **Note:** `face_id` references faces in main database, validated in application code (no cross-database FK)
3. **`pending_photos`** - Photos pending moderation
- `id`, `user_id` (FK), `filename`, `original_filename`, `file_path`, `file_size`, `mime_type`
- `status` (pending/approved/rejected), `submitted_at`, `reviewed_at`, `reviewed_by`, `rejection_reason`
- Relationships: user
4. **`inappropriate_photo_reports`** - Reported photos for review
- `id`, `photo_id` (references main DB, no FK), `user_id` (FK), `status` (pending/reviewed/dismissed)
- `reported_at`, `reviewed_at`, `reviewed_by`, `review_notes`, `report_comment`
- Unique constraint on (photo_id, user_id) - prevents duplicate reports
- Relationships: user
- **Note:** `photo_id` references photos in main database, validated in application code
5. **`pending_linkages`** - Tag submissions pending approval
- `id`, `photo_id` (references main DB), `tag_id`, `tag_name`, `user_id` (FK)
- `status` (pending/approved/rejected), `notes`, `created_at`, `updated_at`
- Relationships: user
6. **`photo_favorites`** (Auth Database) - User favorites
- `id`, `photo_id` (references main DB, no FK), `user_id` (FK), `favorited_at`
- Unique constraint on (photo_id, user_id)
- Relationships: user
- **Note:** `photo_id` references photos in main database, validated in application code
---
## SQLite vs PostgreSQL Configuration
### SQLite (Development Default)
**Connection String:**
```
sqlite:///data/punimtag.db
```
**What it means:**
- `sqlite:///` - SQLite protocol
- `data/punimtag.db` - Relative file path (single file)
**Characteristics:**
- ✅ File-based database (single `.db` file)
- ✅ No separate server process needed
- ✅ Zero configuration
- ✅ Perfect for development and small deployments
- ❌ Limited concurrency (file-level locking)
- ❌ No network access (local only)
- ❌ Lower performance under heavy load
**When to use:**
- Local development
- Testing
- Small deployments
- Single-user scenarios
### PostgreSQL (Production)
**Connection String:**
```
postgresql+psycopg2://punimtag:<choose-a-password>@localhost:5432/punimtag
```
**What it means:**
- `postgresql+psycopg2://` - PostgreSQL with psycopg2 driver
- `punimtag` - Username
- `<choose-a-password>` - Password
- `localhost:5432` - Host and port
- `punimtag` - Database name
**Characteristics:**
- ✅ Server-based database
- ✅ Requires running PostgreSQL server
- ✅ Excellent concurrency (row-level locking)
- ✅ Network access
- ✅ High performance under load
- ✅ ACID compliance
- ✅ Advanced features (full-text search, JSON, etc.)
- ✅ Best for multi-user production
**When to use:**
- Production environments
- Multi-user access
- High concurrency requirements
- Large datasets
- Network access needed
### Key Differences
| Feature | SQLite | PostgreSQL |
|---------|--------|------------|
| **Setup** | No setup needed | Requires server installation |
| **File vs Server** | Single file | Server process |
| **Concurrency** | Limited (file locking) | Excellent (row-level locking) |
| **Network Access** | No | Yes |
| **Performance** | Good for small/medium | Excellent for large scale |
| **Configuration** | Zero config | Requires setup |
| **Best For** | Development, small apps | Production, multi-user |
### Migration Path
You can switch from SQLite to PostgreSQL by:
1. Installing PostgreSQL
2. Creating the database
3. Updating `DATABASE_URL` in `.env`
4. Running migration scripts (if needed)
The application code remains the same; SQLAlchemy handles the differences between database types.
---
## Production Deployment Architecture
### Question: Will main and auth databases sit on the same server in production?
**Answer: Yes, they can be on the same server. There are multiple deployment options:**
### Option 1: Same PostgreSQL Server, Different Databases (Most Common) ⭐
Both databases run on the same PostgreSQL instance as separate databases:
```
PostgreSQL Server (localhost:5432)
├── Database: punimtag (main database)
└── Database: punimtag_auth (auth database)
```
**Connection strings:**
```bash
# Main database
DATABASE_URL=postgresql+psycopg2://punimtag:password@localhost:5432/punimtag
# Auth database (same server, different database)
DATABASE_URL_AUTH=postgresql+psycopg2://punimtag:password@localhost:5432/punimtag_auth
```
**Advantages:**
- ✅ Simple setup and management
- ✅ Shared resources (memory, connections)
- ✅ Single backup target
- ✅ Lower overhead
**When to use:**
- Standard production deployments
- Small to medium scale
- Single-server setups
**Setup:**
```bash
# Create both databases on same PostgreSQL instance
sudo -u postgres psql -c "CREATE DATABASE punimtag OWNER punimtag;"
sudo -u postgres psql -c "CREATE DATABASE punimtag_auth OWNER punimtag;"
```
### Option 2: Different Servers (For Separation/High Availability)
Each database runs on its own PostgreSQL server:
```
Server 1: PostgreSQL (db-server-1:5432)
└── Database: punimtag
Server 2: PostgreSQL (db-server-2:5432)
└── Database: punimtag_auth
```
**Connection strings:**
```bash
# Main database
DATABASE_URL=postgresql+psycopg2://punimtag:password@db-server-1:5432/punimtag
# Auth database (different server)
DATABASE_URL_AUTH=postgresql+psycopg2://punimtag:password@db-server-2:5432/punimtag_auth
```
**Advantages:**
- ✅ Complete isolation and security
- ✅ Independent scaling
- ✅ Better fault tolerance
- ✅ Compliance/regulatory separation
**When to use:**
- High availability requirements
- Security/compliance needs
- Large scale deployments
- Multi-region setups
### Option 3: Same Server, Different PostgreSQL Instances (Rare)
Both instances on the same machine:
```
Server
├── PostgreSQL Instance 1 (port 5432)
│ └── Database: punimtag
└── PostgreSQL Instance 2 (port 5433)
└── Database: punimtag_auth
```
**When to use:**
- Special requirements
- Resource isolation needs
- Testing scenarios
### Deployment Options Summary
| Setup | Same Server? | Same PostgreSQL Instance? | Use Case |
|-------|--------------|---------------------------|----------|
| **Option 1** | ✅ Yes | ✅ Yes (different databases) | **Most common** - Standard production |
| **Option 2** | ❌ No | ❌ No | High availability, security separation |
| **Option 3** | ✅ Yes | ❌ No (different ports) | Rare - special requirements |
**Recommendation:** Start with **Option 1** (same server, same PostgreSQL instance, different databases). It's simpler, easier to manage, and sufficient for most deployments. You can move to separate servers later if needed.
The application supports all three options - just configure via `DATABASE_URL` and `DATABASE_URL_AUTH`.
---
## Key Differences Summary
| Feature | Main DB | Auth DB |
|---------|---------|---------|
| **Database Type** | SQLite (dev) / PostgreSQL (prod) | PostgreSQL (always) |
| **Purpose** | Photo metadata, faces, people, tags | User accounts, pending operations |
| **Read/Write** | Read-write | Read-write |
| **Foreign Keys** | Full FK support | Cross-database references validated in code |
| **User Table** | Backend users (username, email, role) | Frontend users (email, name, password_hash) |
| **Initialization** | Auto-created on startup | Requires manual setup or frontend script |
| **Required in Production** | Yes | Yes (for frontend website) |
---
## Connection Management
The application automatically detects which database type to use based on the `DATABASE_URL`:
### Main Database Connection
```python
def get_database_url() -> str:
"""Fetch database URL from environment or defaults."""
db_url = os.getenv("DATABASE_URL")
if db_url:
return db_url
# Default to SQLite for development
return "sqlite:///data/punimtag.db"
```
**Connection behavior:**
- **SQLite:** Uses `check_same_thread=False` for multi-threading
- **PostgreSQL:** Uses connection pooling (pool_size=10, max_overflow=20)
### Auth Database Connection
```python
def get_auth_database_url() -> str:
"""Fetch auth database URL from environment."""
db_url = os.getenv("DATABASE_URL_AUTH")
if not db_url:
raise ValueError("DATABASE_URL_AUTH environment variable not set")
return db_url
```
**Connection behavior:**
- **PostgreSQL only:** Uses connection pooling (pool_size=10, max_overflow=20)
- **Required:** Must be configured for frontend website functionality
### Connection Pool Settings
Both databases use connection pooling for PostgreSQL:
- `pool_size=10` - Base connection pool size
- `max_overflow=20` - Maximum overflow connections
- `pool_recycle=3600` - Recycle connections after 1 hour
- `pool_pre_ping=True` - Verify connections before use
---
## Decision Points
### For Development
1. **Main Database:**
- ✅ Use SQLite (`sqlite:///data/punimtag.db`)
- Simple, no setup required
- Fast for development
2. **Auth Database:**
- ✅ Use PostgreSQL (`postgresql+psycopg2://...@localhost:5432/punimtag_auth`)
- Required for frontend website features
- Can be on same PostgreSQL instance as production
### For Production
1. **Database Type:**
- ✅ Main DB: PostgreSQL (switch from SQLite)
- ✅ Auth DB: PostgreSQL (always required)
2. **Deployment Architecture:**
- **Recommended:** Option 1 (same server, same PostgreSQL instance, different databases)
- **Alternative:** Option 2 (different servers) if you need high availability or security separation
3. **Configuration:**
```bash
# Main database
DATABASE_URL=postgresql+psycopg2://punimtag:password@localhost:5432/punimtag
# Auth database
DATABASE_URL_AUTH=postgresql+psycopg2://punimtag:password@localhost:5432/punimtag_auth
```
4. **Backup Strategy:**
- Both databases should be backed up
- If on same server, can backup together
- If on different servers, backup separately
5. **Security Considerations:**
- Use strong passwords
- Limit database user permissions
- Use SSL/TLS for remote connections
- Consider separate users for read-only access
---
## Next Steps
1. **Review this document** and decide on deployment architecture
2. **Choose database configuration:**
- Development: SQLite for main DB, PostgreSQL for auth DB
- Production: PostgreSQL for both
3. **Decide on deployment:**
- Same server, same PostgreSQL instance (recommended)
- Or separate servers if needed
4. **Set up databases** according to chosen architecture
5. **Configure `.env` file** with appropriate connection strings
6. **Test connections** and verify both databases work correctly
---
## References
- Main database models: `backend/db/models.py`
- Database session management: `backend/db/session.py`
- Auth database schema: `viewer-frontend/prisma/schema-auth.prisma`
- Main database schema: `viewer-frontend/prisma/schema.prisma`
- Deployment questions: `docs/CLIENT_DEPLOYMENT_QUESTIONS.md`
- Architecture documentation: `docs/ARCHITECTURE.md`
---
**Note:** This document should be reviewed and updated as deployment decisions are made and architecture evolves.
+9 -10
View File
@@ -1,12 +1,11 @@
# PunimTag Deployment Guide
# Deployment
**Last Updated:** January 6, 2026
Deploy PunimTag to development or production. Homelab host and Caddy details
live in the private ansible repo.
This guide covers deployment of PunimTag to development and production environments.
Last reviewed: January 6, 2026.
---
## Table of Contents
## Table of contents
1. [Prerequisites](#prerequisites)
2. [Development Server Deployment](#development-server-deployment)
@@ -44,7 +43,7 @@ This guide covers deployment of PunimTag to development and production environme
---
## Development Server Deployment
## Development server deployment
### Step 1: Build Applications
@@ -277,7 +276,7 @@ sudo systemctl status punimtag-worker
---
## Production Deployment
## Production deployment
### Additional Production Considerations
@@ -361,7 +360,7 @@ server {
---
## Environment Configuration
## Environment configuration
### Required Environment Variables
@@ -402,7 +401,7 @@ NEXTAUTH_SECRET=your-nextauth-secret
---
## Service Management
## Service management
### Systemd Commands
+13 -13
View File
@@ -4,20 +4,20 @@ This guide provides detailed, step-by-step instructions for deploying PunimTag t
---
## 📋 Prerequisites Checklist
## Prerequisites Checklist
Before starting, ensure you have:
- ✅ **cPanel access** with Terminal/SSH capabilities
- ✅ **PostgreSQL database** (NOT MySQL - PunimTag requires PostgreSQL)
- ✅ **Python 3.11.2+ or 3.12+** (3.11.2+ matches dev environment; check via CPanel Terminal)
- ✅ **Node.js 18+** (check via CPanel Terminal or contact hosting provider)
- ✅ **Redis** (for background jobs - may need hosting provider to enable)
- ✅ **Domain name** configured in cPanel
- ✅ **File Manager** access in cPanel
- ✅ **Database management** access in cPanel
- **cPanel access** with Terminal/SSH capabilities
- **PostgreSQL database** (NOT MySQL - PunimTag requires PostgreSQL)
- **Python 3.11.2+ or 3.12+** (3.11.2+ matches dev environment; check via CPanel Terminal)
- **Node.js 18+** (check via CPanel Terminal or contact hosting provider)
- **Redis** (for background jobs - may need hosting provider to enable)
- **Domain name** configured in cPanel
- **File Manager** access in cPanel
- **Database management** access in cPanel
**⚠️ CRITICAL:** If PostgreSQL is not available, contact your hosting provider immediately. PunimTag **cannot** work with MySQL.
** CRITICAL:** If PostgreSQL is not available, contact your hosting provider immediately. PunimTag **cannot** work with MySQL.
---
@@ -98,7 +98,7 @@ redis-cli ping
**If any are missing:**
> **📘 Need to install prerequisites?** See [`INSTALL_PREREQUISITES_CPANEL.md`](./INSTALL_PREREQUISITES_CPANEL.md) for detailed instructions on installing Python 3.11.2+ (or 3.12+), Node.js, npm, PostgreSQL client, and Redis client **without sudo access**.
> ** Need to install prerequisites?** See [`INSTALL_PREREQUISITES_CPANEL.md`](./INSTALL_PREREQUISITES_CPANEL.md) for detailed instructions on installing Python 3.11.2+ (or 3.12+), Node.js, npm, PostgreSQL client, and Redis client **without sudo access**.
- Check cPanel → **Software** → **Python Selector** (if available)
- Check cPanel → **Software** → **Node.js Selector** (if available)
@@ -831,7 +831,7 @@ Most cPanel hosts disable `mod_proxy` in user `.htaccess` files for security. Co
### Option C: Configure Apache .htaccess (May Not Work)
**⚠️ Warning:** Most cPanel hosts disable `mod_proxy` in user `.htaccess` files. This option will likely fail, but you can try:
** Warning:** Most cPanel hosts disable `mod_proxy` in user `.htaccess` files. This option will likely fail, but you can try:
Create/edit `.htaccess` in your main domain's `public_html`:
@@ -1262,7 +1262,7 @@ If you encounter issues:
---
**Congratulations!** You've successfully deployed PunimTag to cPanel! 🎉
**Congratulations!** You've successfully deployed PunimTag to cPanel!
---
-47
View File
@@ -1,47 +0,0 @@
# PunimTag fixes — 2026-07-09
Merged: PR #51 → `dev`, PR #52 → `master` (`eeb0033`).
Docs: PR #53. Ansible: #81 (Postgres `.179`), #82 (Kuma/Beszel).
Dev URLs: https://punimtagdev.levkin.ca · https://punimtagadmindev.levkin.ca
## Infra (homelab)
| Item | Detail |
|------|--------|
| Outage | App `.env` pointed at dead Postgres `<old-db-host>` |
| Fix | Retarget to live `postgresQA` **`<db-host>`** (pve201 VM 109) |
| Hosts | LXC **9101** `punimTagFE-dev` @ `<backend-host>` |
| Monitoring | Kuma: PunimTag viewer/admin; Beszel: `punimtag-9101` |
## Ticket fixes (code) — closed
| Issue | Status | Change |
|-------|--------|--------|
| **#45** Recursive scan checkbox | Closed | Re-read local folder when recursive toggles (`Scan.tsx`) |
| **#46** Person search wrong people | Closed | Selected people use exact `person_ids` (`Search.tsx`, `search_service.py`) |
| **#47** Identify Next/Prev blank image | Closed | Clear loading when crop already cached (`Identify.tsx`) |
| **#21** SharePoint/network scan hang | Closed (eng) | Non-blocking import enqueue; 2h job timeout (`photos.py`). Reopen with path + logs if still hangs. |
| **#26** Slow main-page images | Closed (eng) | Grid `?thumbnail=true` + disk cache under `.cache/photo-thumbnails/` |
| **#30/#31/#33** Auto-match false positives | Closed (eng) | Re-enable small-face filters (`face_service.py`). Re-Process after deploy to validate. |
## Still open (blocked on client)
| Issue | Need |
|-------|------|
| **#44** JRCC watermark | ✅ PNG watermark + light/dark header logos (`public/brand/`, `/styles` picker) |
| **#43** Tag without account | Product rules (approval, PII, ToS) |
## Performance notes (#26)
Grid thumbs are ~**0.1%** of full image bytes (~13 KB vs ~11 MB). Warm cache ~15 ms server-side.
**Optional later:** production `next start` (remove Next.js “N” badge); pre-generate thumbs at Process; lazy-load face metadata on home SSR.
## Ops follow-ups (not tickets)
1. Run viewer as production (`next build` + `next start`), not `next dev`
2. Keep `.env` on `.179` after every deploy (`DEPLOYMENT_CHECKLIST.md`)
## Cleanup
Removed unused viewer SVGs and `test-image-detection.ts`.
-140
View File
@@ -1,140 +0,0 @@
# Fix Admin Login WordPress Redirect
## Problem
Login redirects to WordPress (`wp-login.php`) instead of calling FastAPI.
## Root Cause
The admin frontend is calling `/api/v1/auth/login` (relative path) which WordPress intercepts. It should call `/punim-api/api/v1/auth/login`.
This happens because **Vite environment variables are injected at BUILD TIME**, not runtime. If you changed `.env` but didn't rebuild, the old code is still running.
## Solution
### Step 1: Verify Environment Variable
```bash
cd ~/punimtag/admin-frontend
cat .env | grep VITE_API_URL
```
Should show:
```
VITE_API_URL=/punim-api
```
If missing or wrong:
```bash
nano admin-frontend/.env
# Add: VITE_API_URL=/punim-api
# Save: Ctrl+X, Y, Enter
```
### Step 2: REBUILD Admin Frontend (CRITICAL!)
**This is required!** Vite reads `.env` during build:
```bash
cd ~/punimtag/admin-frontend
npm run build
```
Wait for completion. You should see:
```
✓ built in X.XXs
```
### Step 3: Verify Build Contains Correct URL
```bash
cd ~/punimtag/admin-frontend
grep -r "punim-api" dist/ | head -3
```
Should show `/punim-api` in JavaScript files.
### Step 4: Restart Admin Service
```bash
pm2 restart punimtag-admin
```
### Step 5: Clear Browser Cache
- **Hard refresh:** `Ctrl+Shift+R` (Windows/Linux) or `Cmd+Shift+R` (Mac)
- Or open DevTools → Network tab → check "Disable cache"
### Step 6: Verify Request URL
1. Open admin frontend: `https://jrccphotos.org/punim-admin/`
2. Open DevTools → Network tab
3. Try to login
4. Check the POST request URL:
- ✅ **Correct:** `https://jrccphotos.org/punim-api/api/v1/auth/login`
- ❌ **Wrong:** `https://jrccphotos.org/api/v1/auth/login` (means rebuild didn't work)
## Why This Happens
Vite environment variables work like this:
1. **Build time:** Vite reads `VITE_API_URL` from `.env` and replaces `import.meta.env.VITE_API_URL` in code
2. **Runtime:** The built JavaScript has the actual value hardcoded
If you change `.env` but don't rebuild:
- Old build still has old value
- New `.env` value is ignored
- WordPress intercepts relative paths
## Troubleshooting
### Still redirects to WordPress after rebuild?
1. **Check if dist/ folder was updated:**
```bash
ls -lt ~/punimtag/admin-frontend/dist/ | head -5
```
Should show recent timestamps.
2. **Check PM2 is serving from dist/:**
```bash
pm2 logs punimtag-admin --lines 20
```
Should show it's serving from `dist/` directory.
3. **Check serve.sh script:**
```bash
cat ~/punimtag/admin-frontend/serve.sh
```
Should serve from `dist/` directory.
4. **Verify VITE_API_URL in built code:**
```bash
cd ~/punimtag/admin-frontend
grep -o "punim-api" dist/assets/*.js | head -1
```
Should find `/punim-api` in the built files.
### Build fails?
- Check Node.js version: `node --version` (need 18+)
- Check npm install: `cd admin-frontend && npm install`
- Check for errors: `npm run build 2>&1 | tail -20`
## Quick Checklist
- [ ] `VITE_API_URL=/punim-api` in `admin-frontend/.env`
- [ ] Ran `npm run build` in `admin-frontend/` directory
- [ ] Build completed successfully (no errors)
- [ ] Verified `/punim-api` appears in `dist/` files
- [ ] Restarted PM2: `pm2 restart punimtag-admin`
- [ ] Cleared browser cache (hard refresh)
- [ ] Checked browser Network tab - request goes to `/punim-api/api/v1/auth/login`
---
**Remember:** Every time you change `VITE_API_URL` in `.env`, you MUST rebuild!
-124
View File
@@ -1,124 +0,0 @@
# Fix Login Redirect Bug - Client Server
## Problem
When login fails (401 error), the code redirects to `/login` which WordPress intercepts. It should redirect to `/punim-admin/login`.
## Root Cause
In `admin-frontend/src/api/client.ts`, the error handler uses:
```typescript
window.location.href = '/login' // ❌ Wrong - goes to domain root
```
This should be:
```typescript
window.location.href = '/punim-admin/login' // ✅ Correct
```
## Fix on Client Server
### Option 1: Quick Fix (Edit Built File - Temporary)
**⚠️ Warning:** This will be overwritten on next rebuild!
```bash
cd ~/punimtag/admin-frontend/dist/assets
# Find the JavaScript file
JS_FILE=$(ls index-*.js | head -1)
# Backup
cp "$JS_FILE" "$JS_FILE.backup"
# Replace /login with /punim-admin/login (only in redirect contexts)
sed -i 's|window.location.href="/login"|window.location.href="/punim-admin/login"|g' "$JS_FILE"
sed -i "s|window.location.href='/login'|window.location.href='/punim-admin/login'|g" "$JS_FILE"
# Restart
pm2 restart punimtag-admin
```
### Option 2: Fix Source Code (Permanent)
Edit the source file on client server:
```bash
cd ~/punimtag/admin-frontend/src/api
nano client.ts
```
Find these lines (around lines 44 and 65):
```typescript
window.location.href = '/login'
```
Replace with:
```typescript
window.location.href = '/punim-admin/login'
```
Then rebuild:
```bash
cd ~/punimtag/admin-frontend
npm run build
pm2 restart punimtag-admin
```
## Verification
After fix:
1. Try to login with wrong credentials
2. Should stay on admin frontend (show error message)
3. Should NOT redirect to WordPress
4. Check browser URL - should be `https://jrccphotos.org/punim-admin/login`
## What to Change
In `admin-frontend/src/api/client.ts`, change:
**Line ~44:**
```typescript
// OLD:
window.location.href = '/login'
// NEW:
window.location.href = '/punim-admin/login'
```
**Line ~65:**
```typescript
// OLD:
window.location.href = '/login'
// NEW:
window.location.href = '/punim-admin/login'
```
**Line ~42 (check):**
```typescript
// OLD:
const isLoginPage = window.location.pathname === '/login'
// NEW:
const isLoginPage = window.location.pathname === '/punim-admin/login' || window.location.pathname === '/punim-admin/'
```
## Better Solution (Future)
Use a helper function to get the base path dynamically:
```typescript
const getBasePath = () => {
const path = window.location.pathname;
if (path.startsWith('/punim-admin')) return '/punim-admin';
return '';
};
window.location.href = `${getBasePath()}/login`;
```
But for now, the hardcoded `/punim-admin/login` fix will work.
+9 -9
View File
@@ -1,6 +1,6 @@
# Installing Prerequisites in cPanel
> **⚠️ IMPORTANT:** This guide has two paths:
> ** IMPORTANT:** This guide has two paths:
> - **If you have sudo access:** Follow the [Quick Install with Sudo](#quick-install-with-sudo-access) section below
> - **If you don't have sudo access:** Follow the [Manual Installation Without Sudo](#manual-installation-without-sudo-access) section
@@ -183,7 +183,7 @@ redis-cli ping
pm2 --version
```
**✅ You're done!** All prerequisites are installed. Continue with the deployment guide.
** You're done!** All prerequisites are installed. Continue with the deployment guide.
---
@@ -197,9 +197,9 @@ This guide shows how to install Python 3.11.2+ (or 3.12+), Node.js, npm, Postgre
**Important:** Your dev environment uses **Python 3.11.2**, which is perfectly fine!
- ✅ **Python 3.11.2+** - Works great (matches your dev environment)
- ✅ **Python 3.12+** - Recommended for latest features, but not required
- ❌ **Python 3.6.8** - Too old, needs upgrade
- **Python 3.11.2+** - Works great (matches your dev environment)
- **Python 3.12+** - Recommended for latest features, but not required
- **Python 3.6.8** - Too old, needs upgrade
**Recommendation:** Install **Python 3.11.9** (latest 3.11.x) to match your dev environment, or **Python 3.12.7** if you want the latest version.
@@ -284,7 +284,7 @@ openssl version
### 1.2 Install pyenv
**⚠️ IMPORTANT:** If you got "Permission denied" on gcc, pyenv won't be able to compile Python. Skip to the "Alternative: Pre-built Python Binary" section below, or contact your hosting provider first.
** IMPORTANT:** If you got "Permission denied" on gcc, pyenv won't be able to compile Python. Skip to the "Alternative: Pre-built Python Binary" section below, or contact your hosting provider first.
```bash
# Navigate to home directory
@@ -384,7 +384,7 @@ Some cPanel installations have a Python version selector:
### Option 3: Download Pre-compiled Python Binary (Advanced)
**⚠️ Note:** This still requires compilation, so it won't work if gcc has permission issues.
** Note:** This still requires compilation, so it won't work if gcc has permission issues.
If you have gcc access but pyenv failed, you can compile from source:
@@ -450,7 +450,7 @@ source ~/.bashrc
python3 --version
```
**⚠️ This won't work if gcc has permission denied!** Contact your hosting provider instead.
** This won't work if gcc has permission denied!** Contact your hosting provider instead.
**Note:** This requires build tools. If compilation fails, contact hosting provider or use pyenv method above.
@@ -1045,5 +1045,5 @@ If installation fails:
---
**Once all prerequisites are installed, you're ready to deploy PunimTag!** 🚀
**Once all prerequisites are installed, you're ready to deploy PunimTag!**
-780
View File
@@ -1,780 +0,0 @@
# PunimTag Photo Viewer - Architecture Overview
## System Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────────────┐
│ EXISTING PUNIMTAG SYSTEM │
│ ┌────────────────────┐ ┌─────────────────┐ │
│ │ Desktop App │ │ Web Admin │ │
│ │ (Tkinter) │ │ Interface │ │
│ │ │ │ (React+FastAPI)│ │
│ └──────────┬─────────┘ └────────┬────────┘ │
│ │ │ │
│ │ ┌──────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ PostgreSQL Database │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ Tables: │ │ │
│ │ │ • photos │ │ │
│ │ │ • faces │ │ │
│ │ │ • people │ │ │
│ │ │ • person_encodings │ │ │
│ │ │ • tags │ │ │
│ │ │ • phototaglinkage │ │ │
│ │ │ • photo_favorites │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────┬─────────────────┘ │
└─────────────────────┼────────────────────────────────────────────────────┘
│
│ READ-ONLY ACCESS
│
┌─────────────────────▼──────────────────────────────────────────────────┐
│ NEW PHOTO VIEWER WEBSITE │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ User's Web Browser │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Desktop │ │ Tablet │ │ Mobile │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────┬─────────────────────────────────────┘ │
│ │ HTTPS │
│ ┌──────────────────────────▼─────────────────────────────────────┐ │
│ │ Next.js 14 Frontend │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ React Server Components (App Router) │ │ │
│ │ │ • Photo Grid (Infinite Scroll) │ │ │
│ │ │ • Search & Filters │ │ │
│ │ │ • Lightbox Viewer │ │ │
│ │ │ • People & Tags Browsers │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ Client Components (Interactive) │ │ │
│ │ │ • TanStack Query (State Management) │ │ │
│ │ │ • Framer Motion (Animations) │ │ │
│ │ │ • React Photo Album (Grid Layout) │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼─────────────────────────────────────┐ │
│ │ Next.js API Routes (Optional) │ │
│ │ • /api/photos - List and search photos │ │
│ │ • /api/photos/[id] - Get photo details │ │
│ │ • /api/people - List people with photo counts │ │
│ │ • /api/tags - List tags with photo counts │ │
│ │ • /api/search - Combined search │ │
│ └──────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼─────────────────────────────────────┐ │
│ │ Prisma ORM Client │ │
│ │ • Type-safe database queries │ │
│ │ • Read-only operations │ │
│ │ • Connection pooling │ │
│ └──────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼─────────────────────────────────────┐ │
│ │ PostgreSQL (Read-Only User) │ │
│ │ Username: viewer_readonly │ │
│ │ Permissions: SELECT only on all tables │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
```
---
## Data Flow Diagram
### Photo Gallery Loading
```
User Opens Browser
│
▼
┌─────────────────┐
│ Next.js Server │
│ (App Router) │
└────────┬────────┘
│
│ 1. Server Component Renders
│ await prisma.photo.findMany()
│
▼
┌─────────────────┐
│ Prisma Client │
└────────┬────────┘
│
│ 2. SQL Query (Read-Only)
│ SELECT * FROM photos
│ WHERE processed = true
│ ORDER BY date_taken DESC
│ LIMIT 30
│
▼
┌─────────────────┐
│ PostgreSQL │
│ Database │
└────────┬────────┘
│
│ 3. Return Photo Records
│ [{id, path, filename, date_taken, ...}, ...]
│
▼
┌─────────────────┐
│ Next.js Server │
│ (Pre-renders) │
└────────┬────────┘
│
│ 4. Generate HTML with Photo Data
│
▼
┌─────────────────┐
│ User Browser │
│ (Receives HTML)│
└────────┬────────┘
│
│ 5. Hydrate React Components
│
▼
┌─────────────────┐
│ Photo Grid │
│ (Rendered) │
└────────┬────────┘
│
│ 6. Load Images (Lazy)
│ <Image src={photo.path} />
│
▼
┌─────────────────┐
│ Next.js Image │
│ Optimization │
└────────┬────────┘
│
│ 7. Serve Optimized Images
│ (WebP, responsive sizes)
│
▼
┌─────────────────┐
│ User Sees Photos│
└─────────────────┘
```
### Search Flow
```
User Types in Search
│
▼
┌─────────────────┐
│ SearchBar │
│ (Debounced) │
└────────┬────────┘
│ 300ms delay
▼
┌─────────────────┐
│ TanStack Query │
│ (React Query) │
└────────┬────────┘
│
│ Check cache first
│ If not cached:
│
▼
┌─────────────────┐
│ API Route │
│ /api/search │
└────────┬────────┘
│
│ Build Query
│ {people: [1,2], tags: [3], dateFrom: '2024-01-01'}
│
▼
┌─────────────────┐
│ Prisma Query │
│ Builder │
└────────┬────────┘
│
│ WHERE processed = true
│ AND faces.person_id IN (1,2)
│ AND photo_tags.tag_id IN (3)
│ AND date_taken >= '2024-01-01'
│
▼
┌─────────────────┐
│ PostgreSQL │
│ (Indexed) │
└────────┬────────┘
│
│ Return Matching Photos
│
▼
┌─────────────────┐
│ TanStack Query │
│ (Cache Result) │
└────────┬────────┘
│
│ staleTime: 5 min
│ cacheTime: 10 min
│
▼
┌─────────────────┐
│ Photo Grid │
│ (Update UI) │
└─────────────────┘
```
---
## Component Hierarchy
```
App Layout
├── Header
│ ├── Logo
│ ├── Navigation
│ │ ├── Home Link
│ │ ├── People Link
│ │ ├── Tags Link
│ │ └── Timeline Link
│ ├── SearchButton
│ └── UserMenu
│ ├── Login/Logout
│ └── Settings
│
├── Main Content (Page)
│ │
│ ├── HomePage (/)
│ │ ├── Hero Section
│ │ ├── QuickFilters
│ │ └── PhotoGrid
│ │ ├── PhotoCard (x30)
│ │ │ ├── Image (Next.js)
│ │ │ ├── Overlay
│ │ │ └── Actions
│ │ └── InfiniteScroll Trigger
│ │
│ ├── SearchPage (/search)
│ │ ├── SearchBar
│ │ ├── FilterPanel
│ │ │ ├── PeopleFilter
│ │ │ ├── DateRangePicker
│ │ │ └── TagFilter
│ │ └── SearchResults
│ │ └── PhotoGrid
│ │
│ ├── PhotoDetailPage (/photo/[id])
│ │ ├── Lightbox
│ │ │ ├── FullSizeImage
│ │ │ ├── NavigationButtons
│ │ │ └── CloseButton
│ │ └── MetadataPanel
│ │ ├── DateTaken
│ │ ├── PeopleList
│ │ ├── TagList
│ │ └── Actions
│ │
│ ├── PeoplePage (/people)
│ │ ├── PeopleGrid
│ │ │ └── PersonCard (x many)
│ │ │ ├── RepresentativePhoto
│ │ │ ├── PersonName
│ │ │ └── PhotoCount
│ │ └── SearchBar
│ │
│ └── TagsPage (/tags)
│ ├── TagCloud
│ │ └── TagBadge (x many)
│ │ ├── TagName
│ │ └── PhotoCount
│ └── SearchBar
│
└── Footer
├── Copyright
├── Links
└── Version
```
---
## Technology Stack Layers
```
┌──────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌────────────────────────────────────────────────┐ │
│ │ React Server Components + Client Components │ │
│ │ • shadcn/ui (UI Components) │ │
│ │ • Tailwind CSS (Styling) │ │
│ │ • Framer Motion (Animations) │ │
│ │ • Lucide React (Icons) │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌────────────────────────────────────────────────┐ │
│ │ Next.js 14 (App Router) │ │
│ │ • Server-Side Rendering │ │
│ │ • API Routes │ │
│ │ • Image Optimization │ │
│ │ • Route Handlers │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ State Management Layer │
│ ┌────────────────────────────────────────────────┐ │
│ │ TanStack Query (React Query) │ │
│ │ • Server State Caching │ │
│ │ • Automatic Refetching │ │
│ │ • Optimistic Updates │ │
│ │ • Infinite Scroll │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Data Access Layer │
│ ┌────────────────────────────────────────────────┐ │
│ │ Prisma ORM │ │
│ │ • Type-Safe Queries │ │
│ │ • Connection Pooling │ │
│ │ • Query Builder │ │
│ │ • Migration Management │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Database Layer │
│ ┌────────────────────────────────────────────────┐ │
│ │ PostgreSQL │ │
│ │ • Relational Database │ │
│ │ • ACID Transactions │ │
│ │ • Indices for Performance │ │
│ │ • Read-Only User (viewer_readonly) │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
---
## Deployment Architecture
### Option 1: Vercel (Recommended)
```
┌──────────────────────────────────────────────────────────┐
│ Vercel Edge Network │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Global CDN (Automatic) │ │
│ │ • Caches static assets │ │
│ │ │ - HTML, CSS, JS │ │
│ │ │ - Optimized images │ │
│ │ └─ Serves from nearest edge location │ │
│ └────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼───────────────────────────────┐ │
│ │ Next.js Server (Serverless Functions) │ │
│ │ • API Routes │ │
│ │ │ - /api/photos │ │
│ │ │ - /api/search │ │
│ │ │ - /api/people │ │
│ │ └─ Server Components rendering │ │
│ └────────────────────┬───────────────────────────────┘ │
└────────────────────────┼──────────────────────────────────┘
│
│ Database Connection
│ (Secure, encrypted)
│
┌────────────────────────▼──────────────────────────────────┐
│ Your PostgreSQL Server │
│ • On your local network OR │
│ • Hosted database (Railway, Supabase, etc.) │
│ • Connection via DATABASE_URL env variable │
└───────────────────────────────────────────────────────────┘
```
### Option 2: Self-Hosted (Docker)
```
┌──────────────────────────────────────────────────────────┐
│ Your Server / VPS │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Caddy (Reverse Proxy + Auto HTTPS) │ │
│ │ • Automatic SSL certificates │ │
│ │ • HTTPS redirection │ │
│ │ • Static file serving │ │
│ └────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼───────────────────────────────┐ │
│ │ Docker Container: punimtag-viewer │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Next.js Production Server │ │ │
│ │ │ • Node.js 20 │ │ │
│ │ │ • Optimized build │ │ │
│ │ │ • Port 3000 │ │ │
│ │ └────────────────┬─────────────────────────────┘ │ │
│ └───────────────────┼────────────────────────────────┘ │
│ │ │
│ ┌───────────────────▼────────────────────────────────┐ │
│ │ Docker Container: PostgreSQL (Optional) │ │
│ │ • Or connect to existing PunimTag database │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
```
---
## Security Architecture
```
┌──────────────────────────────────────────────────────────┐
│ User Browser │
└───────────────────────┬──────────────────────────────────┘
│ HTTPS Only
│ (TLS 1.3)
▼
┌──────────────────────────────────────────────────────────┐
│ Authentication Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ NextAuth.js (Optional) │ │
│ │ • JWT Sessions │ │
│ │ • OAuth Providers (Google, etc.) │ │
│ │ • OR Simple Password Protection │ │
│ └────────────────────┬───────────────────────────────┘ │
└────────────────────────┼──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Authorization Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Read-Only Access │ │
│ │ • No write operations allowed │ │
│ │ • No photo uploads │ │
│ │ • No deletion │ │
│ │ • Favorites only write operation (per user) │ │
│ └────────────────────┬───────────────────────────────┘ │
└────────────────────────┼──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ API Security Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ • Rate Limiting (10 req/10s per IP) │ │
│ │ • Input Validation (Zod schemas) │ │
│ │ • SQL Injection Prevention (Prisma ORM) │ │
│ │ • XSS Prevention (React auto-escaping) │ │
│ │ • CSRF Protection (SameSite cookies) │ │
│ └────────────────────┬───────────────────────────────┘ │
└────────────────────────┼──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Database Security │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Read-Only PostgreSQL User │ │
│ │ • Username: viewer_readonly │ │
│ │ • SELECT permission only │ │
│ │ • No INSERT, UPDATE, DELETE │ │
│ │ • Connection over encrypted channel │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
---
## Performance Optimization Strategy
```
┌──────────────────────────────────────────────────────────┐
│ Browser Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Browser Caching │ │
│ │ • Static assets: 1 year │ │
│ │ • Images: 1 month │ │
│ │ • API responses: 5 minutes │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ React Query Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ In-Memory Cache │ │
│ │ • Query results cached │ │
│ │ • staleTime: 5 minutes │ │
│ │ • cacheTime: 10 minutes │ │
│ │ • Automatic background refetching │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Next.js Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Image Optimization │ │
│ │ • Automatic WebP/AVIF conversion │ │
│ │ • Responsive images (srcset) │ │
│ │ • Lazy loading │ │
│ │ • Blur placeholders │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ Code Splitting │ │
│ │ • Route-based splitting │ │
│ │ • Dynamic imports │ │
│ │ • Component lazy loading │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ Server-Side Rendering │ │
│ │ • Initial render on server │ │
│ │ • Faster first contentful paint │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Database Layer │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Prisma Connection Pooling │ │
│ │ • Reuse database connections │ │
│ │ • Max connections: 10 │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ PostgreSQL Indices │ │
│ │ • idx_photos_date_taken │ │
│ │ • idx_photos_processed │ │
│ │ • idx_faces_person_id │ │
│ │ • idx_photo_tags_tag │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ Query Optimization │ │
│ │ • Cursor-based pagination │ │
│ │ • SELECT only needed columns │ │
│ │ • Minimize JOINs │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
---
## Comparison: Admin Interface vs Photo Viewer
| Aspect | Admin Interface | Photo Viewer |
|--------|----------------|--------------|
| **Purpose** | Manage & Process | View & Browse |
| **Tech Stack** | React + FastAPI | Next.js 14 |
| **Rendering** | Client-Side (SPA) | Server + Client (SSR) |
| **State** | React Context | TanStack Query |
| **Database Access** | Full (R/W) via API | Read-Only via Prisma |
| **Photo Upload** | ✅ Yes | ❌ No |
| **Face Processing** | ✅ Yes | ❌ No |
| **Search** | ✅ Advanced | ✅ User-friendly |
| **Performance** | Good | Excellent |
| **Mobile UX** | Functional | Optimized |
| **Auth** | Required | Optional |
| **Deployment** | Backend + Frontend | Single Next.js App |
| **Target Users** | Admin (1-2) | All Users (5-50+) |
| **Complexity** | High | Low |
---
## Database Read Patterns
### Admin Interface (Write-Heavy)
```
┌─────────────────────────────────────┐
│ Photo Upload │
│ INSERT INTO photos │
│ (Heavy write operations) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Face Processing │
│ INSERT INTO faces │
│ UPDATE photos SET processed │
│ (Batch operations) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Person Identification │
│ UPDATE faces SET person_id │
│ INSERT INTO person_encodings │
└─────────────────────────────────────┘
```
### Photo Viewer (Read-Heavy)
```
┌─────────────────────────────────────┐
│ Browse Photos │
│ SELECT * FROM photos │
│ WHERE processed = true │
│ ORDER BY date_taken DESC │
│ LIMIT 30 OFFSET X │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Search by Person │
│ SELECT p.* FROM photos p │
│ JOIN faces f ON p.id = f.photo_id │
│ WHERE f.person_id = X │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Get Photo Details │
│ SELECT * FROM photos WHERE id = X │
│ + JOIN faces + people + tags │
└─────────────────────────────────────┘
```
---
## Image Serving Strategy
```
┌──────────────────────────────────────────────────────────┐
│ Original Photo Files │
│ Location: /path/to/photos/2024/01/IMG_1234.jpg │
│ Size: 4000x3000, 8MB │
└───────────────────────┬──────────────────────────────────┘
│
│ Referenced by path in DB
│
┌───────────────────────▼──────────────────────────────────┐
│ Next.js Image Component │
│ <Image src={photo.path} width={800} height={600} /> │
└───────────────────────┬──────────────────────────────────┘
│
│ Optimization Request
│
┌───────────────────────▼──────────────────────────────────┐
│ Next.js Image Optimization │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 1. Load original image │ │
│ │ 2. Resize to requested dimensions │ │
│ │ 3. Convert to WebP/AVIF (browser support) │ │
│ │ 4. Apply quality compression (85%) │ │
│ │ 5. Generate blur placeholder (base64) │ │
│ │ 6. Cache optimized image │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────┬──────────────────────────────────┘
│
│ Serve Optimized
│
┌───────────────────────▼──────────────────────────────────┐
│ Browser Display │
│ Sizes: │
│ • Thumbnail: 300x200 (~30KB WebP) │
│ • Medium: 800x600 (~150KB WebP) │
│ • Large: 1920x1440 (~500KB WebP) │
│ • Full: 4000x3000 (~2MB original, if download enabled) │
└──────────────────────────────────────────────────────────┘
```
---
## Responsive Design Strategy
### Mobile First Approach
```
┌─────────────────────────────────────────────────────────┐
│ Mobile (<640px) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ [☰] PunimTag [🔍] │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Photo │ │ Photo │ 2 columns │ │
│ │ └──────────┘ └──────────┘ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Photo │ │ Photo │ Compact │ │
│ │ └──────────┘ └──────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Tablet (640px - 1024px) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ [Logo] Home | People | Tags [Search] [👤] │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │Photo│ │Photo│ │Photo│ 3-4 columns │ │
│ │ └─────┘ └─────┘ └─────┘ │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │Photo│ │Photo│ │Photo│ Medium size │ │
│ │ └─────┘ └─────┘ └─────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Desktop (>1024px) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ [Logo] Home | People | Tags [Search Bar] [👤] │ │
│ ├──────┬────────────────────────────────────────────┤ │
│ │Filter│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │
│ │ │ │Ph│ │Ph│ │Ph│ │Ph│ │Ph│ 5-6 columns │ │
│ │People│ └──┘ └──┘ └──┘ └──┘ └──┘ │ │
│ │☐ John│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │
│ │☐ Jane│ │Ph│ │Ph│ │Ph│ │Ph│ │Ph│ Large grid │ │
│ │ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │
│ │Tags │ │ │
│ │☐ Fam │ │ │
│ └──────┴────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
---
## Error Handling Flow
```
User Request
│
▼
┌──────────────────┐
│ Try Operation │
└────────┬─────────┘
│
├─ Success ────────────────┐
│ ▼
│ ┌─────────────────┐
│ │ Return Data │
│ │ Update UI │
│ └─────────────────┘
│
└─ Error ──────────────────┐
▼
┌──────────────────┐
│ Catch Error │
└────────┬─────────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────┐ ┌────────────┐
│ Network Error │ │ 404 Not │ │ 500 Server │
│ (Offline) │ │ Found │ │ Error │
└───────┬───────┘ └──────┬──────┘ └─────┬──────┘
│ │ │
│ │ │
└────────────────┼──────────────┘
▼
┌──────────────────┐
│ Log Error │
│ (Sentry) │
└────────┬─────────┘
▼
┌──────────────────┐
│ Show Error UI │
│ • Toast │
│ • Error Page │
│ • Retry Button │
└──────────────────┘
```
---
This architecture ensures a clean separation between the admin interface (for management) and the photo viewer (for browsing), while sharing the same database and maintaining data consistency.
-425
View File
@@ -1,425 +0,0 @@
# PunimTag Photo Viewer - Executive Summary
## 📋 Overview
This document provides a high-level summary of the plan to create a new photo viewing website for PunimTag. This summary is designed for decision-makers and provides key recommendations.
---
## 🎯 What Are We Building?
A **modern, fast, beautiful photo gallery website** that allows family members to browse and search photos from your PunimTag database without needing admin access.
### Key Features
- ✅ Browse all photos in a beautiful grid layout
- ✅ Search by people, dates, and tags
- ✅ View full-size photos with metadata
- ✅ Mobile-optimized responsive design
- ✅ Fast loading with modern optimizations
- ✅ Optional authentication for privacy
### What It's NOT
- ❌ Not a replacement for the admin interface
- ❌ No photo uploads (read-only)
- ❌ No face processing or management
- ❌ No bulk operations
---
## 💡 Why Do We Need This?
### Current Situation
You have a powerful admin interface for managing photos, but it's:
- Too complex for casual family browsing
- Requires admin credentials
- Focused on management, not viewing
- Not optimized for mobile devices
### The Solution
A separate, simplified photo viewer that:
- Focuses solely on viewing and searching
- Easy for anyone in the family to use
- Optimized for browsing experience
- Beautiful, modern design
- Works great on all devices
---
## 🏗️ Technical Approach
### Recommended Technology Stack
| Component | Technology | Why? |
|-----------|-----------|------|
| **Framework** | Next.js 14 | Best-in-class for performance, SEO, and developer experience |
| **UI Library** | shadcn/ui + Tailwind | Modern, beautiful, customizable components |
| **Database** | Existing PostgreSQL | Reuse your database with read-only access |
| **ORM** | Prisma | Type-safe database queries, excellent PostgreSQL support |
| **State** | TanStack Query | Perfect for server data caching and infinite scroll |
| **Images** | Next.js Image | Automatic optimization, WebP conversion, lazy loading |
| **Hosting** | Vercel (recommended) | Zero-config deployment, global CDN, free tier available |
### Architecture Overview
```
User Browser → Next.js Frontend → Prisma ORM → PostgreSQL Database
(Read-Only User)
```
---
## 📊 Decision Matrix
### 🌟 Recommended Approach: Next.js with Vercel
**Pros:**
- ⭐ Fastest time to market (6-8 weeks)
- ⭐ Best performance (Lighthouse score >90)
- ⭐ Easiest deployment (zero config)
- ⭐ Free hosting tier available
- ⭐ Automatic image optimization
- ⭐ Built-in SEO and accessibility
**Cons:**
- 💰 Can be expensive for high traffic (but free tier is generous)
- 🌐 Requires database to be network-accessible (or use hosted DB)
**Best For:** Most users, especially if you want the best UX and fastest development
---
### Alternative 1: Self-Hosted Docker
**Pros:**
- 💰 Lower ongoing costs (after initial setup)
- 🔒 Full control over hosting
- 🏠 Can run on local network
- 🔐 More privacy (no third-party hosting)
**Cons:**
- 🛠️ Requires DevOps knowledge
- ⏰ Longer setup time
- 📈 Manual scaling
- 🔧 More maintenance
**Best For:** Technical users who want full control and have server management experience
---
### Alternative 2: Astro + React Islands
**Pros:**
- ⚡ Even faster than Next.js
- 💾 Lower bandwidth usage
- 🎨 Great for content-heavy sites
**Cons:**
- 📚 Smaller ecosystem than Next.js
- 🆕 Less mature (but stable)
- 🔧 More manual configuration
**Best For:** Users who prioritize absolute maximum performance
---
## 💰 Cost Analysis
### Development Costs
| Approach | Timeline | Estimated Cost |
|----------|----------|---------------|
| **DIY (Self-Development)** | 12 weeks | Your time (200-300 hours) |
| **Freelancer** | 8-12 weeks | $18,000 - $28,000 |
| **Agency** | 6-10 weeks | $30,000 - $50,000 |
### Monthly Hosting Costs
| Option | Cost/Month | Best For |
|--------|------------|----------|
| **Vercel (Free Tier)** | $0 | Small families (<100 users) |
| **Vercel (Pro)** | $20 | Medium usage (100-1000 users) |
| **Railway** | $5-20 | Hobby projects |
| **VPS (DigitalOcean)** | $12-24 | Self-hosted, full control |
| **Home Server** | $0* | After hardware cost, local network |
**Recommendation:** Start with Vercel Free Tier, upgrade if needed
---
## ⏱️ Timeline
### Fast Track (6 weeks)
| Week | Phase | Deliverables |
|------|-------|--------------|
| 1-2 | Foundation | Project setup, database connection, basic layout |
| 3-4 | Core Features | Photo grid, lightbox, search by people/tags |
| 5 | Polish | Responsive design, animations, performance |
| 6 | Launch | Deploy, test, documentation |
### Standard Track (12 weeks)
| Week | Phase | Deliverables |
|------|-------|--------------|
| 1-2 | Foundation | Setup + authentication |
| 3-4 | Core Features | Photo browsing |
| 5-6 | Search | Advanced search and filters |
| 7-8 | Polish | Optimization and UX |
| 9-10 | Advanced | Favorites, sharing, timeline |
| 11-12 | Launch | Testing, deployment, docs |
**Recommendation:** Standard track for best quality and features
---
## 🎨 Design Approach
### Modern, Clean, Fast
- **Inspiration:** Google Photos, iCloud Photos
- **Style:** Minimalist, photo-first design
- **Colors:** Modern blues and clean whites/darks
- **Typography:** Inter font family (clean, readable)
- **Layout:** Responsive grid (masonry or justified)
- **Animations:** Subtle, smooth, meaningful
### Mobile First
- Works beautifully on phones and tablets
- Touch-optimized interactions
- Fast loading on cellular networks
- Responsive images for all screen sizes
---
## 🔒 Security Considerations
### Database Security
- ✅ Read-only PostgreSQL user (no write access)
- ✅ Separate credentials from admin interface
- ✅ Encrypted database connections
- ✅ Network isolation (if self-hosted)
### Application Security
- ✅ Optional authentication (NextAuth.js)
- ✅ Rate limiting to prevent abuse
- ✅ Input validation on all queries
- ✅ XSS and CSRF protection (built-in)
- ✅ HTTPS only (automatic with Vercel/Caddy)
### Privacy Options
- ✅ Strip GPS data from EXIF
- ✅ Configurable download permissions
- ✅ Per-user favorites (privacy-preserving)
- ✅ Optional authentication requirement
---
## 📈 Success Metrics
### Technical Goals
- 🎯 Lighthouse Performance Score: >90
- 🎯 Page Load Time: <2 seconds
- 🎯 Image Load Time: <1 second
- 🎯 API Response Time: <200ms
- 🎯 Mobile Experience: Excellent
### User Experience Goals
- 🎯 User Adoption: 90% of family members
- 🎯 Session Duration: >5 minutes
- 🎯 Return Visits: >60% weekly
- 🎯 User Satisfaction: >4.5/5 stars
- 🎯 Mobile Usage: >50% of traffic
---
## 🚦 Go/No-Go Criteria
### ✅ Proceed If:
- You have a PostgreSQL database with photos
- You want to share photos with family/friends
- You have budget for development OR time to DIY
- You value user experience and modern design
- You want mobile-optimized photo browsing
### ❌ Don't Proceed If:
- Your database has <100 photos (not worth it yet)
- You're happy with current admin interface for viewing
- Budget constraints (<$500 for hosting/development)
- You don't care about mobile experience
- You need write operations (upload, edit, delete)
---
## 🎬 Next Steps
### Option A: Hire Developer/Agency
1. ✅ Approve this plan
2. ✅ Choose technology stack (recommend: Next.js + Vercel)
3. ✅ Set budget and timeline
4. ✅ Find and hire developer/agency
5. ✅ Provide database access (read-only user)
6. ✅ Review progress weekly
7. ✅ Test and provide feedback
8. ✅ Launch and train users
### Option B: DIY Development
1. ✅ Review technical requirements
2. ✅ Follow Quick Start Guide (`docs/PHOTO_VIEWER_QUICKSTART.md`)
3. ✅ Set up development environment
4. ✅ Follow phase-by-phase plan (`docs/PHOTO_VIEWER_PLAN.md`)
5. ✅ Build incrementally (one feature at a time)
6. ✅ Test with real users frequently
7. ✅ Deploy when stable
8. ✅ Iterate based on feedback
### Option C: Hybrid Approach
1. ✅ Hire freelancer for initial setup (Weeks 1-4)
2. ✅ Learn and customize yourself (Weeks 5+)
3. ✅ Freelancer available for support
4. ✅ Best of both worlds
**Recommendation:** Option A or C for fastest, highest-quality results
---
## ❓ Frequently Asked Questions
### Q: Can I keep using the admin interface?
**A:** Yes! The photo viewer is a separate application. Your admin interface continues working exactly as before.
### Q: Will this modify my database?
**A:** No. The photo viewer uses a read-only database user. It can't modify your photos, faces, or people data. The only exception is the optional favorites feature, which adds per-user favorites to a separate table.
### Q: Do I need to migrate data?
**A:** No. The photo viewer reads directly from your existing PunimTag PostgreSQL database. No data migration needed.
### Q: Can I customize the design?
**A:** Absolutely! The design is fully customizable. You can change colors, fonts, layouts, and components to match your preferences.
### Q: What if I have 100,000 photos?
**A:** The architecture is designed to scale. With proper indexing and pagination, it can handle hundreds of thousands of photos. Performance may require some optimization for very large databases.
### Q: Can users upload photos?
**A:** Not in the initial design (read-only). However, this can be added as a future enhancement if needed. The admin interface remains the primary way to add photos.
### Q: Is it mobile-friendly?
**A:** Yes! Mobile-first design with responsive layouts, touch-optimized interactions, and fast loading on cellular networks.
### Q: What about authentication?
**A:** Optional. You can:
- Make it completely public (no login)
- Add simple password protection
- Use OAuth (Google, Facebook)
- Use email/password authentication
Choose based on your privacy needs.
### Q: Can I host it on my home server?
**A:** Yes! You can self-host using Docker. The plan includes instructions for both cloud (Vercel) and self-hosted (Docker) deployment.
### Q: What if my database is not network-accessible?
**A:** For Vercel hosting, you'll need network access. For self-hosted, you can run it on the same network as your database.
### Q: How do I update it when new photos are added?
**A:** It's automatic! The viewer reads from the live database, so new photos appear immediately after they're processed in the admin interface.
---
## 🎯 Recommendation Summary
### For Most Users: Next.js + Vercel
- ⭐ Best performance and user experience
- ⭐ Fastest development (6-8 weeks)
- ⭐ Easiest deployment and maintenance
- ⭐ Free hosting tier available
- ⭐ Proven, mature ecosystem
### Cost: $0-20/month hosting + $18K-28K development (or DIY)
### Timeline: 6-12 weeks depending on approach
### Next Action: Review full plan, make go/no-go decision, allocate budget
---
## 📚 Documentation Index
| Document | Purpose | Audience |
|----------|---------|----------|
| **This Document** | Executive summary and decision guide | Decision makers |
| `PHOTO_VIEWER_PLAN.md` | Complete detailed plan (20+ pages) | Developers, project managers |
| `PHOTO_VIEWER_QUICKSTART.md` | Quick setup guide (5 minutes to start) | Developers |
| `PHOTO_VIEWER_ARCHITECTURE.md` | Technical architecture and diagrams | Developers, architects |
---
## ✅ Action Items
### For You (Decision Maker)
- [ ] Review this executive summary
- [ ] Read the full detailed plan if needed
- [ ] Make go/no-go decision
- [ ] Allocate budget (development + hosting)
- [ ] Choose deployment approach (Vercel vs self-hosted)
- [ ] Decide on authentication requirement
- [ ] Approve timeline and milestones
### For Developer/Agent
- [ ] Read all documentation
- [ ] Set up development environment
- [ ] Create read-only database user
- [ ] Initialize Next.js project
- [ ] Follow Phase 1 tasks
- [ ] Provide weekly progress updates
---
## 🎊 Expected Outcome
After completion, you'll have:
✅ **A beautiful, modern photo gallery** that family members love to use
✅ **Fast, responsive browsing** on all devices (desktop, tablet, mobile)
✅ **Powerful search capabilities** (people, dates, tags)
✅ **Read-only access** that keeps your database safe
✅ **Separate from admin interface** so you can manage photos independently
✅ **Scalable architecture** that grows with your photo collection
✅ **Low maintenance** with automatic updates and optimizations
---
## 📧 Questions?
If you have questions or need clarification on any part of this plan:
1. **Review the detailed plan** (`PHOTO_VIEWER_PLAN.md`)
2. **Check the architecture** (`PHOTO_VIEWER_ARCHITECTURE.md`)
3. **Try the quick start** (`PHOTO_VIEWER_QUICKSTART.md`)
4. **Consult Next.js docs** (https://nextjs.org/docs)
---
**Ready to proceed?** 🚀
Choose your path:
- **Option A:** Hire a developer and provide them with this plan
- **Option B:** Build it yourself using the Quick Start Guide
- **Option C:** Hybrid approach (developer + your customizations)
**This is a well-planned project with clear deliverables, proven technologies, and realistic timelines.**
Let's build something amazing! 🎉
---
**Document Version:** 1.0
**Last Updated:** November 14, 2025
**Status:** Ready for Decision
**Recommended Action:** Proceed with Next.js + Vercel approach
File diff suppressed because it is too large Load Diff
-470
View File
@@ -1,470 +0,0 @@
# PunimTag Photo Viewer - Quick Start Guide
## 🎯 What Is This?
A **modern, fast, beautiful photo viewing website** that connects to your existing PunimTag PostgreSQL database. Perfect for sharing photos with family members without giving them admin access.
---
## 🚀 Quick Setup (5 Minutes)
### 1. Create the Project
```bash
# Navigate to your projects folder
cd /path/to
# Create Next.js project
npx create-next-app@latest punimtag-viewer --typescript --tailwind --app --no-src-dir
# Navigate into project
cd punimtag-viewer
```
When prompted, choose:
- TypeScript: **Yes**
- ESLint: **Yes**
- Tailwind CSS: **Yes**
- `src/` directory: **No** (we'll use app directory)
- App Router: **Yes**
- Import alias: **Yes** (@/*)
### 2. Install Core Dependencies
```bash
# UI Components & Styling
npx shadcn-ui@latest init
# Database
npm install @prisma/client
npm install -D prisma
# State Management & Data Fetching
npm install @tanstack/react-query
# Image Gallery
npm install react-photo-album yet-another-react-lightbox
# Icons & Animations
npm install lucide-react framer-motion
# Date Handling
npm install date-fns
# Authentication (Optional)
npm install next-auth
```
### 3. Set Up Database Connection
```bash
# Initialize Prisma
npx prisma init
```
Edit `.env`:
```bash
DATABASE_URL="postgresql://viewer_readonly:password@localhost:5432/punimtag"
```
### 4. Create Prisma Schema
Create `prisma/schema.prisma`:
```prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Photo {
id Int @id @default(autoincrement())
path String @unique
filename String
dateAdded DateTime @default(now()) @map("date_added")
dateTaken DateTime? @map("date_taken")
processed Boolean @default(false)
faces Face[]
photoTags PhotoTagLinkage[]
favorites PhotoFavorite[]
@@map("photos")
}
model Person {
id Int @id @default(autoincrement())
firstName String @map("first_name")
lastName String @map("last_name")
middleName String? @map("middle_name")
maidenName String? @map("maiden_name")
dateOfBirth DateTime? @map("date_of_birth") @db.Date
createdDate DateTime @default(now()) @map("created_date")
faces Face[]
personEncodings PersonEncoding[]
@@unique([firstName, lastName, middleName, maidenName, dateOfBirth], name: "uq_people_names_dob")
@@map("people")
}
model Face {
id Int @id @default(autoincrement())
photoId Int @map("photo_id")
personId Int? @map("person_id")
encoding Bytes
location String
confidence Decimal @default(0) @db.Decimal
qualityScore Decimal @default(0) @map("quality_score") @db.Decimal
detectorBackend String @default("retinaface") @map("detector_backend")
modelName String @default("ArcFace") @map("model_name")
faceConfidence Decimal @default(0) @map("face_confidence") @db.Decimal
poseMode String @default("frontal") @map("pose_mode")
photo Photo @relation(fields: [photoId], references: [id])
person Person? @relation(fields: [personId], references: [id])
personEncodings PersonEncoding[]
@@index([photoId])
@@index([personId])
@@map("faces")
}
model PersonEncoding {
id Int @id @default(autoincrement())
personId Int @map("person_id")
faceId Int @map("face_id")
encoding Bytes
qualityScore Decimal @default(0) @map("quality_score") @db.Decimal
detectorBackend String @default("retinaface") @map("detector_backend")
modelName String @default("ArcFace") @map("model_name")
createdDate DateTime @default(now()) @map("created_date")
person Person @relation(fields: [personId], references: [id])
face Face @relation(fields: [faceId], references: [id])
@@index([personId])
@@map("person_encodings")
}
model Tag {
id Int @id @default(autoincrement())
tagName String @unique @map("tag_name")
createdDate DateTime @default(now()) @map("created_date")
photoTags PhotoTagLinkage[]
@@map("tags")
}
model PhotoTagLinkage {
linkageId Int @id @default(autoincrement()) @map("linkage_id")
photoId Int @map("photo_id")
tagId Int @map("tag_id")
linkageType Int @default(0) @map("linkage_type")
createdDate DateTime @default(now()) @map("created_date")
photo Photo @relation(fields: [photoId], references: [id])
tag Tag @relation(fields: [tagId], references: [id])
@@unique([photoId, tagId], name: "uq_photo_tag")
@@index([photoId])
@@index([tagId])
@@map("phototaglinkage")
}
model PhotoFavorite {
id Int @id @default(autoincrement())
username String
photoId Int @map("photo_id")
createdDate DateTime @default(now()) @map("created_date")
photo Photo @relation(fields: [photoId], references: [id])
@@unique([username, photoId], name: "uq_user_photo_favorite")
@@index([username])
@@index([photoId])
@@map("photo_favorites")
}
```
### 5. Generate Prisma Client
```bash
npx prisma generate
```
### 6. Test Database Connection
```bash
npx prisma db pull # Verify schema matches database
npx prisma studio # Open database browser (optional)
```
### 7. Create First Component
Create `app/page.tsx`:
```typescript
import { prisma } from '@/lib/db';
import { PhotoGrid } from '@/components/PhotoGrid';
export default async function HomePage() {
const photos = await prisma.photo.findMany({
where: { processed: true },
orderBy: { dateTaken: 'desc' },
take: 30,
});
return (
<main className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-8">Family Photos</h1>
<PhotoGrid photos={photos} />
</main>
);
}
```
Create `lib/db.ts`:
```typescript
import { PrismaClient } from '@prisma/client';
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
```
### 8. Run Development Server
```bash
npm run dev
```
Open http://localhost:3000
---
## 📁 Project Structure
```
punimtag-viewer/
├── app/ # Next.js 14 App Router
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page (photo grid)
│ ├── photo/[id]/ # Photo detail page
│ ├── people/ # People browser
│ ├── tags/ # Tags browser
│ └── search/ # Search page
├── components/ # React components
│ ├── ui/ # shadcn components
│ ├── PhotoGrid.tsx # Photo grid
│ ├── PhotoCard.tsx # Photo card
│ └── Lightbox.tsx # Photo viewer
├── lib/ # Utilities
│ ├── db.ts # Prisma client
│ ├── queries.ts # Database queries
│ └── utils.ts # Helpers
├── prisma/
│ └── schema.prisma # Database schema
├── .env # Environment variables
└── package.json
```
---
## 🎨 Install UI Components
```bash
# Install shadcn/ui components as needed
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add input
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add popover
npx shadcn-ui@latest add select
npx shadcn-ui@latest add calendar
npx shadcn-ui@latest add badge
npx shadcn-ui@latest add skeleton
```
---
## 🔐 Set Up Read-Only Database User
On your PostgreSQL server:
```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;
```
---
## 🚢 Deploy to Vercel (5 Minutes)
### 1. Push to GitHub
```bash
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourusername/punimtag-viewer.git
git push -u origin main
```
### 2. Deploy to Vercel
```bash
# Install Vercel CLI
npm i -g vercel
# Login
vercel login
# Deploy
vercel
# Set environment variables
vercel env add DATABASE_URL
```
Or use the Vercel dashboard:
1. Go to https://vercel.com
2. Import your GitHub repository
3. Add environment variable `DATABASE_URL`
4. Deploy!
---
## 🎯 Next Steps
### Essential Features (Week 1)
- [ ] Create photo grid with infinite scroll
- [ ] Add photo lightbox/viewer
- [ ] Implement search by people
- [ ] Add date range filter
- [ ] Create tag browser
### Nice-to-Have Features (Week 2-3)
- [ ] Add favorites system
- [ ] Implement timeline view
- [ ] Add photo sharing
- [ ] Create mobile navigation
- [ ] Add authentication (if needed)
### Polish (Week 4)
- [ ] Optimize performance (Lighthouse > 90)
- [ ] Add animations
- [ ] Test responsive design
- [ ] Add error handling
- [ ] Write documentation
---
## 📚 Key Documentation
- **Full Plan:** `docs/PHOTO_VIEWER_PLAN.md`
- **Next.js Docs:** https://nextjs.org/docs
- **Prisma Docs:** https://www.prisma.io/docs
- **shadcn/ui:** https://ui.shadcn.com/
---
## 🆘 Troubleshooting
### "Can't connect to database"
```bash
# Test connection manually
psql -U viewer_readonly -d punimtag -h localhost
# Check .env file has correct credentials
cat .env | grep DATABASE_URL
```
### "Module not found: @/..."
```bash
# Check tsconfig.json has paths configured
# Should have: "@/*": ["./*"]
```
### "Prisma Client not generated"
```bash
npx prisma generate
```
### "Images not loading"
```bash
# Check file paths in database
# Configure Next.js image domains in next.config.js
```
---
## 💡 Pro Tips
1. **Use React Server Components** - They're faster and simpler
2. **Enable Image Optimization** - Configure Next.js Image component
3. **Add Loading States** - Use Suspense and loading.tsx files
4. **Implement Infinite Scroll** - Use TanStack Query's useInfiniteQuery
5. **Cache Aggressively** - Photos don't change often
6. **Test Mobile First** - Most users will view on phones
7. **Add Dark Mode** - Use next-themes package
8. **Monitor Performance** - Use Vercel Analytics
---
## 🎨 Design Resources
**Color Schemes:**
- Modern Blue: `#3b82f6`
- Clean White: `#ffffff`
- Dark Mode: `#0f172a`
**Fonts:**
- Inter (Google Fonts)
- SF Pro (System font)
**Inspiration:**
- Google Photos
- iCloud Photos
- Unsplash
---
## 🤝 Need Help?
- Read the full plan: `docs/PHOTO_VIEWER_PLAN.md`
- Check Next.js docs: https://nextjs.org/docs
- Join Next.js Discord: https://nextjs.org/discord
- Check shadcn/ui examples: https://ui.shadcn.com/examples
---
**Ready to build something awesome!** 🚀
Start with Phase 1 in the full plan and iterate based on user feedback.
-384
View File
@@ -1,384 +0,0 @@
# PunimTag Photo Viewer - Documentation Index
## 📖 Welcome
This directory contains comprehensive documentation for the **PunimTag Photo Viewer** project - a modern, fast, and beautiful photo gallery website that connects to your existing PunimTag PostgreSQL database.
---
## 📑 Documentation Structure
### 🎯 Start Here
| Document | Description | Who Should Read |
|----------|-------------|-----------------|
| **[Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md)** | High-level overview, recommendations, and decision guide | 👔 Decision makers, Project sponsors |
| **[Quick Start Guide](PHOTO_VIEWER_QUICKSTART.md)** | Get up and running in 5 minutes | 👨‍💻 Developers (DIY approach) |
### 📚 Detailed Documentation
| Document | Description | Who Should Read |
|----------|-------------|-----------------|
| **[Complete Plan](PHOTO_VIEWER_PLAN.md)** | 20+ page detailed plan with all features, phases, and specifications | 👨‍💻 Developers, 👷 Project managers, 🏗️ Architects |
| **[Architecture](PHOTO_VIEWER_ARCHITECTURE.md)** | System architecture, diagrams, data flows, and technical design | 👨‍💻 Developers, 🏗️ Architects |
---
## 🚀 Quick Navigation
### I want to...
| Goal | Document | Section |
|------|----------|---------|
| **Understand what this is** | [Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md) | Overview |
| **Make a decision on whether to proceed** | [Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md) | Decision Matrix |
| **Estimate costs** | [Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md) | Cost Analysis |
| **See the timeline** | [Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md) | Timeline |
| **Start building right now** | [Quick Start Guide](PHOTO_VIEWER_QUICKSTART.md) | All |
| **Understand the technology choices** | [Complete Plan](PHOTO_VIEWER_PLAN.md) | Technology Stack |
| **See the architecture** | [Architecture](PHOTO_VIEWER_ARCHITECTURE.md) | All |
| **Learn about security** | [Complete Plan](PHOTO_VIEWER_PLAN.md) | Security & Access Control |
| **Understand deployment options** | [Complete Plan](PHOTO_VIEWER_PLAN.md) | Deployment Options |
| **See the roadmap** | [Complete Plan](PHOTO_VIEWER_PLAN.md) | Development Roadmap |
| **Compare with admin interface** | [Architecture](PHOTO_VIEWER_ARCHITECTURE.md) | Comparison Table |
---
## 🎯 Project Overview
### What Is This?
A **modern, lightweight photo viewing website** that allows family members to browse and search photos from your PunimTag database without admin access.
### Key Features
- ✅ **Beautiful Photo Grid** - Responsive, infinite scroll gallery
- ✅ **Powerful Search** - Search by people, dates, tags
- ✅ **Photo Lightbox** - Full-size viewing with metadata
- ✅ **Mobile Optimized** - Works great on all devices
- ✅ **Fast Loading** - Modern optimizations (Lighthouse >90)
- ✅ **Read-Only** - Safe access to your database
### Technology Stack
- **Frontend:** Next.js 14 + React + TypeScript
- **UI:** shadcn/ui + Tailwind CSS
- **Database:** PostgreSQL (existing PunimTag DB)
- **ORM:** Prisma (type-safe queries)
- **State:** TanStack Query (React Query)
- **Hosting:** Vercel (recommended) or Self-hosted
---
## 📊 Documentation at a Glance
### Executive Summary (5 min read)
**File:** `PHOTO_VIEWER_EXECUTIVE_SUMMARY.md`
**Contains:**
- ✅ What we're building and why
- ✅ Technical approach and recommendations
- ✅ Cost analysis ($0-20/month hosting)
- ✅ Timeline (6-12 weeks)
- ✅ Decision criteria (go/no-go)
- ✅ FAQ and next steps
**Best For:** Decision makers who need high-level overview
---
### Quick Start Guide (10 min read)
**File:** `PHOTO_VIEWER_QUICKSTART.md`
**Contains:**
- ✅ 5-minute project setup
- ✅ Step-by-step installation
- ✅ Database configuration
- ✅ First component creation
- ✅ Deployment instructions
- ✅ Troubleshooting tips
**Best For:** Developers ready to start coding
---
### Complete Plan (60 min read)
**File:** `PHOTO_VIEWER_PLAN.md`
**Contains:**
- ✅ Comprehensive feature specifications
- ✅ Detailed technology stack justifications
- ✅ Complete architecture design
- ✅ UI/UX design system
- ✅ Performance optimization strategies
- ✅ Security implementation
- ✅ 6-phase development roadmap
- ✅ Testing strategy
- ✅ Deployment options
- ✅ Cost estimates
- ✅ Risk analysis
**Best For:** Developers, project managers, technical leads
---
### Architecture (30 min read)
**File:** `PHOTO_VIEWER_ARCHITECTURE.md`
**Contains:**
- ✅ System architecture diagrams
- ✅ Data flow diagrams
- ✅ Component hierarchy
- ✅ Technology stack layers
- ✅ Deployment architectures
- ✅ Security architecture
- ✅ Performance optimization
- ✅ Image serving strategy
- ✅ Responsive design strategy
- ✅ Error handling flow
**Best For:** Developers, architects, technical reviewers
---
## 🎨 Project Highlights
### Design Philosophy
**Modern. Clean. Fast.**
- Photo-first design (content is king)
- Minimalist interface (photos speak for themselves)
- Smooth animations (delightful interactions)
- Mobile-first approach (works everywhere)
- Accessible (WCAG AA compliant)
### Performance Targets
| Metric | Target |
|--------|--------|
| Lighthouse Score | >90 |
| Page Load Time | <2s |
| Image Load Time | <1s |
| API Response Time | <200ms |
### Browser Support
- ✅ Chrome/Edge (last 2 versions)
- ✅ Firefox (last 2 versions)
- ✅ Safari (last 2 versions)
- ✅ Mobile Safari (iOS 14+)
- ✅ Chrome Mobile (Android 10+)
---
## 💰 Cost Summary
### Development
| Approach | Cost | Timeline |
|----------|------|----------|
| DIY | Your time (200-300 hrs) | 12 weeks |
| Freelancer | $18K-28K | 8-12 weeks |
| Agency | $30K-50K | 6-10 weeks |
### Hosting (Monthly)
| Option | Cost | Best For |
|--------|------|----------|
| Vercel Free | $0 | Small families |
| Vercel Pro | $20 | Medium usage |
| VPS | $12-24 | Self-hosted |
| Home Server | $0* | Local network |
**Recommended:** Vercel Free Tier → upgrade if needed
---
## ⏱️ Timeline
### Fast Track: 6 Weeks
1. **Weeks 1-2:** Foundation (setup, DB, layout)
2. **Weeks 3-4:** Core Features (gallery, search)
3. **Week 5:** Polish (responsive, animations)
4. **Week 6:** Launch (deploy, test)
### Standard Track: 12 Weeks
1. **Weeks 1-2:** Foundation + Auth
2. **Weeks 3-4:** Core Features
3. **Weeks 5-6:** Search & Filters
4. **Weeks 7-8:** Polish & Optimization
5. **Weeks 9-10:** Advanced Features
6. **Weeks 11-12:** Testing & Launch
---
## 🚦 Decision Guide
### ✅ Proceed If:
- You have PostgreSQL database with photos
- You want to share photos with family
- You have budget/time for development
- You value UX and modern design
- You need mobile-optimized browsing
### ❌ Wait If:
- Database has <100 photos
- Happy with admin interface for viewing
- Budget constraints (<$500)
- Don't need mobile experience
- Need write operations (upload/edit)
---
## 🎬 Getting Started
### Option A: Hire Developer
1. Share this documentation with developer
2. Approve budget and timeline
3. Provide read-only database access
4. Review progress weekly
5. Test and provide feedback
6. Launch!
### Option B: DIY
1. Read [Quick Start Guide](PHOTO_VIEWER_QUICKSTART.md)
2. Follow setup instructions
3. Build phase by phase
4. Test with real users
5. Deploy and iterate
### Option C: Hybrid
1. Hire developer for initial setup
2. Learn and customize yourself
3. Developer available for support
---
## 📚 Additional Resources
### External Documentation
- **Next.js:** https://nextjs.org/docs
- **Prisma:** https://www.prisma.io/docs
- **shadcn/ui:** https://ui.shadcn.com/
- **TanStack Query:** https://tanstack.com/query/latest
- **Tailwind CSS:** https://tailwindcss.com/docs
### Inspiration
- **Google Photos:** https://photos.google.com
- **iCloud Photos:** https://www.icloud.com/photos
- **Unsplash:** https://unsplash.com
### Example Projects
- Next.js + Prisma: https://github.com/vercel/next.js/tree/canary/examples/with-prisma
- Photo Gallery: https://github.com/topics/photo-gallery
---
## 🆘 Support & Help
### Common Questions
**Q: Can I use this with SQLite?**
A: Yes, but PostgreSQL is recommended for better performance with large photo collections.
**Q: Do I need to migrate my database?**
A: No! The viewer reads from your existing PunimTag database.
**Q: Can I customize the design?**
A: Absolutely! All colors, fonts, and layouts are customizable.
**Q: What if I have 100K photos?**
A: The architecture scales well with proper indexing and pagination.
**Q: Is authentication required?**
A: No, it's optional. Configure based on your privacy needs.
### Getting Help
1. Check the documentation (you're reading it!)
2. Review the FAQ in Executive Summary
3. Check Next.js documentation
4. Search GitHub issues/discussions
5. Ask in Next.js Discord community
---
## 📝 Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0 | Nov 14, 2025 | Initial comprehensive plan |
---
## 🙏 Credits
**Planned and documented by:** AI Assistant (Claude Sonnet 4.5)
**For:** PunimTag Project
**Purpose:** Family photo sharing and browsing
---
## ✅ Checklist: Before Starting Development
### Planning Phase
- [ ] Read Executive Summary
- [ ] Review Complete Plan
- [ ] Understand Architecture
- [ ] Make go/no-go decision
- [ ] Approve budget
- [ ] Approve timeline
### Technical Preparation
- [ ] Verify PostgreSQL database access
- [ ] Create read-only database user
- [ ] Test database connection
- [ ] Identify photo storage location
- [ ] Choose hosting platform (Vercel vs self-hosted)
- [ ] Set up development environment
### Team Preparation
- [ ] Assign developer(s)
- [ ] Set up project management (GitHub, etc.)
- [ ] Schedule regular check-ins
- [ ] Identify test users
- [ ] Plan feedback process
### Ready to Start? 🚀
- [ ] Follow [Quick Start Guide](PHOTO_VIEWER_QUICKSTART.md)
- [ ] Begin Phase 1 of [Complete Plan](PHOTO_VIEWER_PLAN.md)
---
## 🎯 Success Criteria
This project will be successful when:
✅ **Users love it** - Family members prefer it over admin interface for browsing
✅ **It's fast** - Lighthouse score >90, loads in <2 seconds
✅ **It's beautiful** - Modern design, smooth animations
✅ **It works everywhere** - Great experience on mobile, tablet, desktop
✅ **It's reliable** - 99.9% uptime, handles errors gracefully
✅ **It's maintainable** - Clean code, good documentation
---
## 🎊 Let's Build Something Amazing!
You now have everything you need to create a world-class photo viewing experience for your family. The plan is comprehensive, the technology is proven, and the path is clear.
**Choose your starting point:**
- 👔 **Decision maker?** → Read [Executive Summary](PHOTO_VIEWER_EXECUTIVE_SUMMARY.md)
- 👨‍💻 **Developer?** → Follow [Quick Start Guide](PHOTO_VIEWER_QUICKSTART.md)
- 🏗️ **Architect?** → Study [Architecture](PHOTO_VIEWER_ARCHITECTURE.md)
- 👷 **Project manager?** → Review [Complete Plan](PHOTO_VIEWER_PLAN.md)
**Ready when you are!** 🚀
---
**Questions?** Start with the Executive Summary and work your way through the documentation. Everything you need is here.
**Good luck!** 🎉
-368
View File
@@ -1,368 +0,0 @@
# Project Restructure Summary
**Date**: October 15, 2025
**Status**: ✅ Structure Complete - ⏳ Imports Need Updating
---
## What Was Done
### ✅ Completed Tasks
1. **Created New Directory Structure**
- `src/` - All source code
- `src/core/` - Business logic modules
- `src/gui/` - GUI components
- `src/utils/` - Utility functions
- `tests/` - Test suite
- `docs/` - Documentation
- `.notes/` - Project planning and notes
- `archive/` - Legacy files
2. **Moved Files**
- 6 core business logic files → `src/core/`
- 6 GUI panel files → `src/gui/`
- 1 utility file → `src/utils/`
- 8 test files → `tests/`
- 4 documentation files → `docs/`
- 7 legacy files → `archive/`
3. **Created Package Structure**
- Added `__init__.py` to all package directories
- Defined public APIs in `__init__.py` files
- Proper Python package hierarchy
4. **Created Project Documentation**
- `README.md` - Main project documentation
- `CONTRIBUTING.md` - Contribution guidelines
- `docs/ARCHITECTURE.md` - System architecture
- `.notes/project_overview.md` - Project overview
- `.notes/task_list.md` - Task tracking
- `.notes/directory_structure.md` - Directory layout
- `.notes/meeting_notes.md` - Meeting records
- `.notes/restructure_migration.md` - Migration guide
5. **Created Configuration Files**
- `.cursorrules` - Cursor AI coding guidelines
- `.cursorignore` - Files for Cursor to ignore
6. **Archived Legacy Files**
- `*_backup.py` files
- Old standalone GUI files
- `*_original_backup.py` files
---
## New Directory Structure
```
punimtag/
├── .notes/ # Project planning & notes
│ ├── project_overview.md # Project description
│ ├── task_list.md # Task tracking
│ ├── directory_structure.md # Structure documentation
│ ├── meeting_notes.md # Meeting records
│ └── restructure_migration.md # Migration guide
│
├── src/ # Source code
│ ├── __init__.py
│ ├── photo_tagger.py # CLI entry point
│ ├── setup.py # Package setup
│ │
│ ├── core/ # Business logic
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── database.py
│ │ ├── face_processing.py
│ │ ├── photo_management.py
│ │ ├── tag_management.py
│ │ └── search_stats.py
│ │
│ ├── gui/ # GUI components
│ │ ├── __init__.py
│ │ ├── dashboard_gui.py
│ │ ├── gui_core.py
│ │ ├── identify_panel.py
│ │ ├── auto_match_panel.py
│ │ ├── modify_panel.py
│ │ └── tag_manager_panel.py
│ │
│ └── utils/ # Utilities
│ ├── __init__.py
│ └── path_utils.py
│
├── tests/ # Test suite
│ ├── __init__.py
│ ├── test_deepface_gui.py
│ ├── test_face_recognition.py
│ ├── test_simple_gui.py
│ ├── test_thumbnail_sizes.py
│ ├── debug_face_detection.py
│ └── show_large_thumbnails.py
│
├── docs/ # Documentation
│ ├── README.md
│ ├── ARCHITECTURE.md
│ ├── DEMO.md
│ └── README_UNIFIED_DASHBOARD.md
│
├── archive/ # Legacy files
│ ├── *_backup.py
│ └── *_gui.py
│
├── data/ # Application data
│ └── photos.db
│
├── demo_photos/ # Sample images
├── scripts/ # Utility scripts
├── logs/ # Log files
├── venv/ # Virtual environment
│
├── README.md # Main README
├── CONTRIBUTING.md # Contribution guide
├── RESTRUCTURE_SUMMARY.md # This file
├── requirements.txt # Dependencies
├── .cursorrules # AI guidelines
├── .cursorignore # AI ignore list
├── .gitignore # Git ignore
└── gui_config.json # GUI config
```
---
## File Movements
### Core Business Logic (6 files)
```
config.py → src/core/config.py
database.py → src/core/database.py
face_processing.py → src/core/face_processing.py
photo_management.py → src/core/photo_management.py
tag_management.py → src/core/tag_management.py
search_stats.py → src/core/search_stats.py
```
### GUI Components (6 files)
```
dashboard_gui.py → src/gui/dashboard_gui.py
gui_core.py → src/gui/gui_core.py
identify_panel.py → src/gui/identify_panel.py
auto_match_panel.py → src/gui/auto_match_panel.py
modify_panel.py → src/gui/modify_panel.py
tag_manager_panel.py → src/gui/tag_manager_panel.py
```
### Utilities (1 file)
```
path_utils.py → src/utils/path_utils.py
```
### Entry Points (2 files)
```
photo_tagger.py → src/photo_tagger.py
setup.py → src/setup.py
```
### Tests (8 files)
```
test_deepface_gui.py → tests/test_deepface_gui.py
test_face_recognition.py → tests/test_face_recognition.py
test_simple_gui.py → tests/test_simple_gui.py
test_thumbnail_sizes.py → tests/test_thumbnail_sizes.py
test_deepface_only.py → tests/test_deepface_only.py
debug_face_detection.py → tests/debug_face_detection.py
show_large_thumbnails.py → tests/show_large_thumbnails.py
```
### Documentation (4 files)
```
README.md → docs/README.md
ARCHITECTURE.md → docs/ARCHITECTURE.md
DEMO.md → docs/DEMO.md
README_UNIFIED_DASHBOARD.md → docs/README_UNIFIED_DASHBOARD.md
```
---
## What Needs to Be Done
### ⏳ Immediate Next Steps
1. **Update Import Statements** (HIGH PRIORITY)
- Update all files to use new import paths
- Change `from config import` to `from src.core.config import`
- See `.notes/restructure_migration.md` for details
2. **Test Functionality**
- Test dashboard GUI launches
- Verify all features work
- Run test suite
- Fix any issues
3. **Update Scripts**
- Update `demo.sh` paths
- Update `run_deepface_gui.sh` paths
- Create new launcher scripts if needed
4. **Commit Changes**
- Stage all changes
- Commit with descriptive message
- Push to repository
---
## Benefits of Restructure
### 🎯 Organization
- **Before**: 30+ files in root directory
- **After**: Clean hierarchy with 6 main directories
- **Result**: Much easier to navigate and find files
### 📦 Package Structure
- **Before**: No package structure
- **After**: Proper Python packages with `__init__.py`
- **Result**: Can import as proper Python modules
### 🧪 Testing
- **Before**: Tests mixed with source code
- **After**: Separate `tests/` directory
- **Result**: Clear separation, easier to run tests
### 📚 Documentation
- **Before**: Documentation scattered
- **After**: Centralized in `docs/` and `.notes/`
- **Result**: Easy to find information
### 🔧 Maintenance
- **Before**: Hard to understand dependencies
- **After**: Clear module hierarchy
- **Result**: Easier to maintain and extend
---
## Quick Reference
### Run Dashboard
```bash
python src/gui/dashboard_gui.py
```
### Run CLI
```bash
python src/photo_tagger.py --help
```
### Run Tests
```bash
python -m pytest tests/
```
### Import Examples
**Core:**
```python
from src.core.database import DatabaseManager
from src.core.face_processing import FaceProcessor
```
**GUI:**
```python
from src.gui.gui_core import GUICore
from src.gui.dashboard_gui import DashboardGUI
```
**Utils:**
```python
from src.utils.path_utils import normalize_path
```
---
## Documentation Files
### User-Facing
- `README.md` - Main project documentation
- `docs/DEMO.md` - Demo guide
- `docs/README_UNIFIED_DASHBOARD.md` - Dashboard guide
### Developer-Facing
- `CONTRIBUTING.md` - How to contribute
- `docs/ARCHITECTURE.md` - System architecture
- `.notes/directory_structure.md` - Structure details
- `.notes/restructure_migration.md` - Migration guide
### Planning
- `.notes/project_overview.md` - Project goals
- `.notes/task_list.md` - Task tracking
- `.notes/meeting_notes.md` - Meeting records
---
## Key Files Created
1. **`README.md`** - Comprehensive user documentation
2. **`CONTRIBUTING.md`** - Detailed contribution guidelines
3. **`.cursorrules`** - Cursor AI coding standards
4. **`.cursorignore`** - Files for AI to ignore
5. **`src/__init__.py`** - Main package init
6. **`src/core/__init__.py`** - Core module exports
7. **`src/gui/__init__.py`** - GUI module exports
8. **`src/utils/__init__.py`** - Utils module exports
9. **`.notes/*`** - Four project management documents
---
## Migration Checklist
- [x] Create directory structure
- [x] Move files to new locations
- [x] Create `__init__.py` files
- [x] Archive legacy files
- [x] Create documentation
- [x] Create project notes
- [x] Create migration guide
- [ ] Update import statements
- [ ] Test dashboard GUI
- [ ] Test CLI tool
- [ ] Run test suite
- [ ] Update launch scripts
- [ ] Commit changes
- [ ] Update README with new paths
---
## Rollback Information
If issues arise, files can be moved back:
```bash
# See .notes/restructure_migration.md for rollback steps
```
All files are tracked in Git, so changes can be reverted if needed.
---
## Support
- **Migration Guide**: `.notes/restructure_migration.md`
- **Directory Structure**: `.notes/directory_structure.md`
- **Task List**: `.notes/task_list.md`
---
## Statistics
- **Files Moved**: 27
- **Directories Created**: 8
- **Documentation Files Created**: 9
- **Package Init Files**: 5
- **Configuration Files**: 2
- **Total Files in Project**: 96
---
**Next Action**: Update import statements in all source files
**Status**: Structure complete ✅ | Imports pending ⏳ | Testing pending ⏳
+64 -92
View File
@@ -1,34 +1,21 @@
# PunimTag Web - User Guide
# Admin user guide
**Complete guide to using the PunimTag web application**
How to use the PunimTag admin UI (http://localhost:3000) day to day.
Viewer browsing is separate (port 3001).
---
## Table of contents
## Table of Contents
1. [Getting Started](#getting-started)
2. [Navigation Overview](#navigation-overview)
3. [Page-by-Page Guide](#page-by-page-guide)
- [Login Page](#login-page)
- [Dashboard](#dashboard)
- [Scan Page](#scan-page)
- [Process Page](#process-page)
- [Identify Page](#identify-page)
- [Auto-Match Page](#auto-match-page)
- [Search Page](#search-page)
- [Modify Page](#modify-page)
- [Tags Page](#tags-page)
- [Faces Maintenance Page](#faces-maintenance-page)
- [Settings Page](#settings-page)
4. [Workflow Examples](#workflow-examples)
5. [Tips & Best Practices](#tips--best-practices)
1. [Getting started](#getting-started)
2. [Navigation](#navigation)
3. [Pages](#pages)
4. [Workflow examples](#workflow-examples)
5. [Tips](#tips)
6. [Troubleshooting](#troubleshooting)
---
## Getting Started
## Getting started
### First Time Setup
### First-time setup
1. **Start the Application**
- Ensure Redis is running
@@ -39,7 +26,7 @@
2. **Login**
- Default credentials:
- Use credentials from `.env` (`ADMIN_USERNAME` / `ADMIN_PASSWORD`)
- ⚠️ **Important**: Change these credentials in production!
- Change these credentials before any shared deploy.
3. **Initial Workflow**
- **Scan** → Import your photos
@@ -49,78 +36,74 @@
- **Search** → Find photos by people, dates, or tags
- **Tags** → Tag photos and manage tags
---
## Navigation Overview
## Navigation
The application uses a **left sidebar navigation** with the following pages:
- 🏠 **Dashboard** - Overview and statistics
- 📁 **Scan** - Import photos from folders or upload files
- ⚙️ **Process** - Detect and process faces in photos
- 👤 **Identify** - Manually identify people in faces
- 🤖 **Auto-Match** - Automatically match similar faces to previously identified faces
- 🔍 **Search** - Search and filter photos
- ✏️ **Modify** - Edit person information
- 🏷️ **Tags** - Tag photos and manage photo tags
- 🔧 **Faces Maintenance** - Manage face data
- ⚙️ **Settings** - Application settings
- **Dashboard** - Overview and statistics
- **Scan** - Import photos from folders or upload files
- **Process** - Detect and process faces in photos
- **Identify** - Manually identify people in faces
- **Auto-Match** - Automatically match similar faces to previously identified faces
- **Search** - Search and filter photos
- **Modify** - Edit person information
- **Tags** - Tag photos and manage photo tags
- **Faces Maintenance** - Manage face data
- **Settings** - Application settings
---
## Page-by-Page Guide
## Pages
### Login Page
**Purpose**: Authenticate and access the application
Purpose: Authenticate and access the application
**Features**:
Features:
- Username and password login
- JWT-based authentication
- Automatic redirect to dashboard after login
**How to Use**:
How to use:
1. Enter your username (`ADMIN_USERNAME` from `.env`)
2. Enter your password (`ADMIN_PASSWORD` from `.env`)
3. Click "Login" button
4. You'll be redirected to the Dashboard
**Notes**:
Notes:
- Session persists until logout
- Default credentials are for development only
- Change credentials in production for security
---
### Dashboard
**Purpose**: Overview of your photo collection and statistics
Purpose: Overview of your photo collection and statistics
**Features**:
Features:
- Collection statistics
- Quick access to main features
- Recent activity summary
**How to Use**:
How to use:
- View statistics about your photo collection
- Navigate to other pages using the sidebar
- Monitor overall system status
**Current Status**: Basic implementation - more features coming in future updates
Status: Basic implementation - more stats may be added later
---
### Scan Page
**Purpose**: Import photos into your collection from folders or upload files
Purpose: Import photos into your collection from folders or upload files
**Features**:
Features:
- **Folder Selection**: Browse and select folders containing photos
- **Recursive Scanning**: Option to scan subfolders recursively
- **Duplicate Detection**: Automatically detects and skips duplicate photos
- **Real-time Progress**: Live progress tracking during import
**How to Use**:
How to use:
**Folder Scan**
1. Click "Browse Folder" button
@@ -140,13 +123,12 @@ The application uses a **left sidebar navigation** with the following pages:
**Tips**:
- Large folders may take time - be patient!
---
### Process Page
**Purpose**: Detect faces in imported photos and generate face encodings
Purpose: Detect faces in imported photos and generate face encodings
**Features**:
Features:
- **face detection method used - `retinaface` - Best accuracy, medium speed
- **Face recognition model used - `ArcFace` - Best accuracy, medium speed
@@ -154,7 +136,7 @@ The application uses a **left sidebar navigation** with the following pages:
- **Real-time Progress**: Live progress tracking
- **Job Cancellation**: Stop processing if needed
**How to Use**:
How to use:
1. Optionally set **Batch Size** (leave empty for default)
2. Click "Start Processing" button
3. Monitor progress:
@@ -173,13 +155,12 @@ The application uses a **left sidebar navigation** with the following pages:
**Tips**:
- You can cancel and resume later
---
### Identify Page
**Purpose**: Manually identify people in detected faces
Purpose: Manually identify people in detected faces
**Features**:
Features:
- **Face Navigation**: Browse through unidentified faces
- **Person Creation**: Create new person records
- **Similar Faces Panel**: View similar faces for comparison
@@ -188,7 +169,7 @@ The application uses a **left sidebar navigation** with the following pages:
- **Unique Faces Filter**: Hide duplicate faces of same person
- **Face Information**: View face metadata (confidence, quality, detector/model)
**How to Use**:
How to use:
**Basic Identification**:
1. Navigate to Identify page
@@ -222,11 +203,11 @@ The application uses a **left sidebar navigation** with the following pages:
- Similar faces panel remains unfiltered
**Confidence Colors**:
- 🟢 **80%+** = Very High (Almost Certain)
- 🟡 **70%+** = High (Likely Match)
- 🟠 **60%+** = Medium (Possible Match)
- 🔴 **50%+** = Low (Questionable)
- ⚫ **<50%** = Very Low (Unlikely)
- **80%+** = Very High (Almost Certain)
- **70%+** = High (Likely Match)
- **60%+** = Medium (Possible Match)
- **50%+** = Low (Questionable)
- **<50%** = Very Low (Unlikely)
**Tips**:
- Use similar faces to identify groups of photos
@@ -234,20 +215,19 @@ The application uses a **left sidebar navigation** with the following pages:
- Unique faces filter reduces clutter
- Confidence scores help prioritize identification
---
### Auto-Match Page
**Purpose**: Automatically match unidentified faces to identified people
Purpose: Automatically match unidentified faces to identified people
**Features**:
Features:
- **Person-Centric View**: Shows identified person on left, matches on right
- **Checkbox Selection**: Select which faces to identify
- **Confidence Display**: Color-coded match confidence
- **Batch Identification**: Identify multiple faces at once
- **Navigation**: Move between different people
**How to Use**:
How to use:
**Manual Match Workflow**:
1. Navigate to Auto-Match page
@@ -295,13 +275,12 @@ profile faces are excluded for better accuracy
- You can correct mistakes by going back and unchecking
- High confidence matches (>70%) are usually accurate
---
### Search Page
**Purpose**: Search and filter photos by various criteria
Purpose: Search and filter photos by various criteria
**Features**:
Features:
- **People Filter**: Filter by identified people
- **Date Filter**: Filter by date taken or date added
- **Tag Filter**: Filter by photo tags
@@ -309,7 +288,7 @@ profile faces are excluded for better accuracy
- **Photo Grid**: Virtualized grid of matching photos
- **Pagination**: Navigate through search results
**How to Use**:
How to use:
**Basic Search**:
1. Navigate to Search page
@@ -339,19 +318,18 @@ profile faces are excluded for better accuracy
- Tag filtering helps find themed photos
- People filter is most useful after identification
---
### Modify Page
**Purpose**: Edit person information and manage person records
Purpose: Edit person information and manage person records
**Features**:
Features:
- **Person Selection**: Choose person to edit
- **Information Editing**: Update names and date of birth
- **Face Management**: View and manage person's faces
- **Person Deletion**: Remove person records (with confirmation)
**How to Use**:
How to use:
**Editing Person Information**:
1. Navigate to Modify page
@@ -374,20 +352,19 @@ profile faces are excluded for better accuracy
- Update names if you learn more information
- Be careful with deletion - it's permanent
---
### Tags Page
**Purpose**: Manage photo tags and tag-photo relationships
Purpose: Manage photo tags and tag-photo relationships
**Features**:
Features:
- **Tag List**: View all existing tags
- **Tag Creation**: Create new tags
- **Tag Editing**: Edit tag names
- **Tag Deletion**: Remove tags
- **Photo-Tag Linkage**: Assign tags to photos
**How to Use**:
How to use:
**Creating Tags**:
1. Navigate to Tags page
@@ -400,7 +377,7 @@ profile faces are excluded for better accuracy
- View all tags in a list
- Edit tag names by clicking edit button
- Delete tags by selecting it's check box and clicking "Delete selected tags" button
- ⚠️ **Warning**: Deleting a tag removes it from all photos
- **Warning**: Deleting a tag removes it from all photos
**Assigning Tags to Photos**:
- Select photos from Tags page(or from Search page)
@@ -414,19 +391,18 @@ profile faces are excluded for better accuracy
- Create tags for events, locations, themes
- Tags help organize and find photos later
Note: Tags are case insensitive!*********
---
### Faces Maintenance Page
**Purpose**: Remove unwanted faces - mainly due to low quality face detections
Purpose: Remove unwanted faces - mainly due to low quality face detections
**Features**:
Features:
- **Face List**: View all faces in database
- **Face Filtering**: Filter quality
- **Face Deletion**: Remove unwanted faces
- **Bulk Operations**: Perform actions on multiple faces
**How to Use**:
How to use:
**Viewing Faces**:
1. Navigate to Faces Maintenance page
@@ -438,7 +414,7 @@ Note: Tags are case insensitive!*********
- Select faces to delete
- Click "Delete Selected" button
- Confirm deletion
- ⚠️ **Warning**: Deletion is permanent
- **Warning**: Deletion is permanent
**Bulk Operations**:
- Select multiple faces
@@ -449,12 +425,10 @@ Note: Tags are case insensitive!*********
- Remove low-quality face detections
- Regular maintenance keeps database clean
---
### Settings Page
**Purpose**: Configure application settings and preferences
---
Purpose: Configure application settings and preferences
@@ -489,13 +463,11 @@ Note: Tags are case insensitive!*********
- Find specific photos
- Assign tags for organization
---
**Last Updated**: October 2025
**Version**: 1.0
**Application**: PunimTag Web
---
*For technical details and development information, see the main README.md*