Skip to content

Refactor: Migrate project preview to polymorphic file table #769

@coderabbitai

Description

@coderabbitai

Context

This issue tracks the refactoring work to migrate from a string-based preview column in the project table to using a polymorphic file table pattern.

Related PR: #766
Requested by: @fingertips18

Current State

  • ✅ File table created with polymorphic pattern (parent_table, parent_id, role)
  • ❌ Project table still has preview string column
  • ❌ Backend and frontend code still expect preview as a string field
  • Migration to drop preview column exists but should be deferred until this refactoring is complete

Goal

Implement a flexible polymorphic file management system where:

  • Projects can have multiple files with different roles (preview, attachments, etc.)
  • File metadata is centralized in the file table
  • Easy to extend to other entities (users, etc.)

Implementation Steps

1. Backend Changes (Go)

1.1 Create File Domain Model

// apps/backend/internal/domain/file.go
type File struct {
    ID          uuid.UUID
    ParentTable string
    ParentID    uuid.UUID
    Role        string
    Key         string
    URL         string
    Type        string
    Size        int64
    CreatedAt   time.Time
    UpdatedAt   time.Time
}

1.2 Create File Repository

  • FindByParent(parentTable, parentID, role) - Get files for a specific parent and role
  • Create(file) - Create new file record
  • Delete(id) - Delete file record
  • FindByID(id) - Get file by ID

1.3 Update Project Model

Add computed field or association:

type Project struct {
    // ... existing fields
    PreviewFile *File `json:"preview_file,omitempty"`
}

1.4 Update Project Repository

Modify GetByID and List to JOIN with file table:

SELECT p.*, f.* FROM project p
LEFT JOIN file f ON f.parent_table = 'project' 
  AND f.parent_id = p.id 
  AND f.role = 'preview'

1.5 Update API Handlers

  • Modify GET /projects responses to include preview_file
  • Add file upload endpoints if not already present
  • Handle file creation when project is created/updated

2. Frontend Changes (React/TypeScript)

2.1 Update TypeScript Types

// apps/admin/src/types/file.ts
export interface File {
  id: string;
  parentTable: string;
  parentId: string;
  role: string;
  key: string;
  url: string;
  type: string;
  size: number;
  createdAt: string;
  updatedAt: string;
}

// apps/admin/src/types/project.ts
export interface Project {
  // ... existing fields
  preview?: string; // Keep for backward compatibility initially
  previewFile?: File; // New field
}

2.2 Update Project Forms

  • apps/admin/src/pages/project/add/_components/form.tsx
  • apps/admin/src/pages/project/edit/_components/form.tsx
  • Update file upload handlers to create file records with parent_table='project' and role='preview'

2.3 Update Display Components

  • apps/admin/src/pages/project/view/index.tsx
  • apps/frontend/src/pages/project/card.tsx
  • Use previewFile?.url instead of preview where applicable

2.4 Add Fallback Logic

Handle transition period where some projects have string preview, others have previewFile:

const previewUrl = project.previewFile?.url ?? project.preview;

3. Data Migration

3.1 Create Migration Script

-- Migrate existing preview URLs to file table
INSERT INTO file (id, parent_table, parent_id, role, key, url, type, size, created_at, updated_at)
SELECT 
  gen_random_uuid(),
  'project',
  id,
  'preview',
  SUBSTRING(preview FROM '[^/]+$'), -- Extract filename from URL
  preview,
  'image/jpeg', -- Default type, adjust as needed
  0, -- Size unknown, can be updated later
  NOW(),
  NOW()
FROM project
WHERE preview IS NOT NULL AND preview != '';

3.2 Drop Preview Column

Once migration is complete and tested:

-- apps/backend/internal/database/migrations/YYYYMMDDHHMMSS_drop_project_preview.up.sql
ALTER TABLE project DROP COLUMN preview;

4. Testing Checklist

  • Backend: Test file repository methods
  • Backend: Test project API returns preview_file
  • Frontend: Test creating new project with file upload
  • Frontend: Test editing project with file replacement
  • Frontend: Test viewing project with preview
  • Data Migration: Run migration script on staging
  • Data Migration: Verify all previews migrated correctly
  • E2E: Test full flow from upload to display
  • Rollback: Test down migration works

5. Deployment Steps

  1. Merge backend changes (API backward compatible)
  2. Run data migration script
  3. Deploy frontend changes
  4. Monitor for issues
  5. After stable, run migration to drop preview column
  6. Clean up fallback code in frontend

Notes

  • Keep the migration that drops the preview column but don't apply it until this refactoring is complete
  • Consider adding file deletion cleanup (delete from storage when file record is deleted)
  • Future: Extend to support multiple files per project (galleries, attachments)
  • Future: Add file validation (size limits, type restrictions)

Definition of Done

  • File table is being used for project previews
  • Old preview column is dropped
  • All existing projects have their previews migrated
  • Frontend displays previews correctly
  • Tests are passing
  • Documentation updated

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions