Spaces:
Running
Running
|
Download docs/database-schema.md from Benaah/MicProtocol: direct link, hf CLI and curl.
- Browser
- Download file 5.74 kB
-
https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/docs/database-schema.md
- Command line
-
hf download hf://spaces/Benaah/MicProtocol/docs/database-schema.md
-
curl -L -o database-schema.md https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/docs/database-schema.md
5.74 kB
| # MicProtocol β Database Schema | |
| ## Entity Relationship Diagram | |
| ```mermaid | |
| erDiagram | |
| USER ||--o{ WORKSPACE_MEMBER : member_of | |
| WORKSPACE ||--o{ WORKSPACE_MEMBER : has_members | |
| WORKSPACE ||--o{ PODCAST : owns | |
| PODCAST ||--o{ GUEST : prospects | |
| PODCAST ||--o{ EPISODE_TEMPLATE : defines | |
| PODCAST ||--o{ TAG : categorizes | |
| GUEST ||--o{ GUEST_TAG : tagged | |
| TAG ||--o{ GUEST_TAG : categorized | |
| GUEST ||--o{ OUTREACH_SEQUENCE : enrolled_in | |
| OUTREACH_SEQUENCE ||--o{ OUTREACH_MESSAGE : contains | |
| GUEST ||--o{ EPISODE : featured_in | |
| PODCAST ||--o{ EPISODE : produces | |
| EPISODE ||--o{ BOOKING : scheduled_via | |
| EPISODE ||--o{ ASSET_REQUEST : requires | |
| ASSET_REQUEST ||--o{ ASSET_SUBMISSION : fulfilled_by | |
| EPISODE ||--o{ PROMO_TASK : generates | |
| USER { | |
| uuid id PK | |
| string email UK | |
| string clerk_id UK | |
| json profile | |
| string default_timezone | |
| timestamp created_at | |
| timestamp updated_at | |
| } | |
| WORKSPACE { | |
| uuid id PK | |
| string name | |
| string slug UK | |
| json billing_settings | |
| timestamp created_at | |
| timestamp updated_at | |
| } | |
| WORKSPACE_MEMBER { | |
| uuid id PK | |
| uuid user_id FK | |
| uuid workspace_id FK | |
| enum role "owner|admin|editor|viewer" | |
| timestamp joined_at | |
| } | |
| PODCAST { | |
| uuid id PK | |
| uuid workspace_id FK | |
| string title | |
| string rss_feed_url | |
| json recording_settings | |
| json outreach_defaults | |
| string calendar_integration_id | |
| timestamp created_at | |
| timestamp updated_at | |
| } | |
| GUEST { | |
| uuid id PK | |
| uuid podcast_id FK | |
| string email "unique per podcast" | |
| string full_name | |
| string company | |
| string title | |
| json social_links | |
| text bio | |
| enum status "prospect|contacted|scheduled|recorded|published" | |
| int priority_score "0-100" | |
| timestamp last_contact_at | |
| json custom_fields | |
| timestamp created_at | |
| timestamp updated_at | |
| } | |
| TAG { | |
| uuid id PK | |
| uuid podcast_id FK | |
| string name "unique per podcast" | |
| string color | |
| } | |
| GUEST_TAG { | |
| uuid id PK | |
| uuid guest_id FK | |
| uuid tag_id FK | |
| timestamp added_at | |
| } | |
| OUTREACH_SEQUENCE { | |
| uuid id PK | |
| uuid guest_id FK | |
| enum status "active|paused|completed" | |
| uuid template_id FK | |
| int current_step | |
| timestamp started_at | |
| timestamp updated_at | |
| } | |
| OUTREACH_MESSAGE { | |
| uuid id PK | |
| uuid sequence_id FK | |
| enum type "initial|followup|booking|reminder" | |
| string subject | |
| text body_html | |
| text body_text | |
| timestamp sent_at | |
| timestamp opened_at | |
| timestamp clicked_at | |
| json metadata | |
| } | |
| EPISODE { | |
| uuid id PK | |
| uuid podcast_id FK | |
| uuid guest_id FK | |
| string title | |
| enum status "draft|scheduled|recorded|editing|published" | |
| timestamp scheduled_at | |
| int duration_minutes | |
| text host_notes | |
| text guest_instructions | |
| enum recording_platform "riverside|zoom|studio" | |
| timestamp created_at | |
| timestamp updated_at | |
| } | |
| EPISODE_TEMPLATE { | |
| uuid id PK | |
| uuid podcast_id FK | |
| string name | |
| int duration_minutes | |
| json required_assets | |
| text guest_instructions | |
| } | |
| BOOKING { | |
| uuid id PK | |
| uuid episode_id FK | |
| string calendar_event_id | |
| timestamp start_time | |
| timestamp end_time | |
| string timezone | |
| string meet_link | |
| json calendar_provider_data | |
| boolean is_confirmed | |
| } | |
| ASSET_REQUEST { | |
| uuid id PK | |
| uuid episode_id FK | |
| enum type "headshot|bio|social_links|intro_audio|talking_points" | |
| boolean is_required | |
| text instructions | |
| timestamp due_date | |
| } | |
| ASSET_SUBMISSION { | |
| uuid id PK | |
| uuid request_id FK | |
| string file_url | |
| string file_type | |
| int file_size | |
| enum status "pending|approved|rejected" | |
| timestamp submitted_at | |
| } | |
| PROMO_TASK { | |
| uuid id PK | |
| uuid episode_id FK | |
| string title | |
| text description | |
| string platform | |
| timestamp scheduled_at | |
| timestamp completed_at | |
| json metadata | |
| } | |
| ``` | |
| ## Key Design Decisions | |
| ### Guest Uniqueness is Per-Podcast | |
| A guest can appear on multiple shows with different statuses. The composite unique constraint `@@unique([podcastId, email])` allows this while preventing duplicates within a single podcast. | |
| ### Outreach Sequences are State Machines | |
| Sequences track `currentStep` and have `active|paused|completed` status, enabling A/B testing of cadence (e.g., Day 0, Day 3, Day 7 follow-ups). Messages track `sentAt`, `openedAt`, and `clickedAt` independently. | |
| ### Asset Requests are Template-Driven | |
| Podcasts define required assets per episode template via JSON in `EpisodeTemplate.requiredAssets`. When an episode is created from a template, asset requests are automatically generated. | |
| ### Row-Level Security | |
| The workspace β podcast β guest hierarchy ensures data isolation. All queries scope through `podcastId`, which chains to `workspaceId`, preventing cross-workspace data leakage. | |
| ### Index Strategy | |
| Performance-critical queries are covered by composite indexes: | |
| - `guests(podcast_id, status)` β Kanban board column queries | |
| - `guests(podcast_id, priority_score)` β priority sorting | |
| - `outreach_sequences(guest_id, status)` β active sequence lookups | |
| - `bookings(start_time, end_time)` β calendar overlap detection | |
| - `asset_requests(episode_id, type)` β completion status checks | |