project-standalo-sonic-cloud/.workflow/versions/v004/design/design_document.yml

616 lines
19 KiB
YAML

workflow_version: "v004"
feature: "add share music system"
created_at: "2025-12-18T18:07:00"
status: draft
revision: 1
data_models:
- id: model_share
name: Share
table_name: shares
description: Tracks shared content links with analytics
primary_key: id
fields:
- name: id
type: String
constraints: [primary_key, cuid]
description: Unique identifier for the share
- name: type
type: ShareType
constraints: [required]
description: Type of content being shared (SONG, PLAYLIST, ALBUM)
- name: targetId
type: String
constraints: [required]
description: ID of the shared content (references songs/playlists/albums)
- name: token
type: String
constraints: [required, unique]
description: Unique URL-safe token for share links
- name: userId
type: String
constraints: [optional]
description: User who created the share (null for anonymous shares)
- name: platform
type: String
constraints: [optional]
description: Platform where content was shared to (twitter, facebook, etc)
- name: clickCount
type: Int
default: 0
description: Number of times the share link was clicked
- name: createdAt
type: DateTime
default: now()
description: Timestamp when the share was created
relations: []
indexes:
- fields: [token]
unique: true
description: Fast lookup by share token
- fields: [targetId, type]
description: Fast lookup of shares for specific content
timestamps: false
validations:
- field: token
rule: minLength(8)
message: Token must be at least 8 characters
- field: type
rule: enum(SONG, PLAYLIST, ALBUM)
message: Type must be SONG, PLAYLIST, or ALBUM
api_endpoints:
- id: api_create_song_share
method: POST
path: /api/share/song/[id]
description: Generate share link for a song
auth:
required: false
description: No authentication required to share content
request_params:
- name: id
type: string
location: path
required: true
description: Song ID to generate share link for
request_body:
- name: platform
type: string
required: false
description: Platform being shared to (twitter, facebook, etc)
responses:
- status: 200
description: Share link created successfully
schema:
shareUrl: string
token: string
type: string
- status: 404
description: Song not found
schema:
error: string
business_logic:
- Verify song exists and is public
- Generate unique 10-character alphanumeric token
- Create Share record with type=SONG
- Return full share URL (https://domain.com/s/[token])
depends_on_models: [model_share]
- id: api_create_playlist_share
method: POST
path: /api/share/playlist/[id]
description: Generate share link for a playlist
auth:
required: false
request_params:
- name: id
type: string
location: path
required: true
description: Playlist ID to generate share link for
request_body:
- name: platform
type: string
required: false
description: Platform being shared to
responses:
- status: 200
description: Share link created successfully
schema:
shareUrl: string
token: string
type: string
- status: 404
description: Playlist not found
schema:
error: string
business_logic:
- Verify playlist exists and is public
- Generate unique token
- Create Share record with type=PLAYLIST
- Return full share URL
depends_on_models: [model_share]
- id: api_create_album_share
method: POST
path: /api/share/album/[id]
description: Generate share link for an album
auth:
required: false
request_params:
- name: id
type: string
location: path
required: true
description: Album ID to generate share link for
request_body:
- name: platform
type: string
required: false
description: Platform being shared to
responses:
- status: 200
description: Share link created successfully
schema:
shareUrl: string
token: string
type: string
- status: 404
description: Album not found
schema:
error: string
business_logic:
- Verify album exists
- Generate unique token
- Create Share record with type=ALBUM
- Return full share URL
depends_on_models: [model_share]
- id: api_resolve_share
method: GET
path: /api/share/[token]
description: Resolve share token and get content details
auth:
required: false
request_params:
- name: token
type: string
location: path
required: true
description: Share token to resolve
responses:
- status: 200
description: Share resolved successfully
schema:
type: string
targetId: string
content: object
shareUrl: string
- status: 404
description: Share not found or content no longer available
schema:
error: string
business_logic:
- Lookup Share record by token
- Fetch associated content based on type (Song/Playlist/Album)
- Include artist information for songs/albums
- Include song list for playlists
- Verify content is still public and available
- Return content details for display
depends_on_models: [model_share]
- id: api_track_share_click
method: POST
path: /api/share/[token]/click
description: Increment share click count for analytics
auth:
required: false
request_params:
- name: token
type: string
location: path
required: true
description: Share token to track click for
request_body: []
responses:
- status: 200
description: Click tracked successfully
schema:
success: boolean
clickCount: integer
- status: 404
description: Share not found
schema:
error: string
business_logic:
- Find Share record by token
- Increment clickCount by 1
- Return new click count
depends_on_models: [model_share]
pages:
- id: page_share
path: /s/[token]
name: SharePage
description: Public landing page for shared music content
layout: minimal
auth:
required: false
description: Publicly accessible share page
data_needs:
- api_id: api_resolve_share
purpose: Load shared content details
on_load: true
description: Fetch content details when page loads
- api_id: api_track_share_click
purpose: Track analytics
on_load: true
description: Track that user clicked on share link
components:
- component_share_content_display
seo:
dynamic_title: true
description: Dynamic based on shared content
og_image: true
twitter_card: true
ui_states:
- state: loading
description: Fetching shared content
- state: content_loaded
description: Display shared content with CTA
- state: not_found
description: Share token invalid or content no longer available
- state: private_content
description: Content is now private
components:
- id: component_share_button
name: ShareButton
description: Button component to trigger share modal
file_path: components/ShareButton.tsx
props:
- name: type
type: "'song' | 'playlist' | 'album'"
required: true
description: Type of content to share
- name: targetId
type: string
required: true
description: ID of the content to share
- name: title
type: string
required: true
description: Title to display in share modal
- name: className
type: string
required: false
description: Additional CSS classes
state:
- name: isModalOpen
type: boolean
description: Controls share modal visibility
- name: shareUrl
type: string | null
description: Generated share URL after creation
- name: isLoading
type: boolean
description: Share link generation in progress
events:
- name: onShare
payload: "{ shareUrl: string, token: string }"
description: Fired when share link is successfully generated
uses_components: [component_share_modal]
uses_apis: [api_create_song_share, api_create_playlist_share, api_create_album_share]
styling:
- Accessible button with share icon
- Loading state during share generation
- Error state if share creation fails
- id: component_share_modal
name: ShareModal
description: Modal displaying share options and social buttons
file_path: components/ShareModal.tsx
props:
- name: isOpen
type: boolean
required: true
description: Controls modal visibility
- name: onClose
type: "() => void"
required: true
description: Callback to close modal
- name: shareUrl
type: string
required: true
description: The share URL to display and copy
- name: title
type: string
required: true
description: Title of content being shared
- name: type
type: "'song' | 'playlist' | 'album'"
required: true
description: Type of content for display context
state:
- name: copied
type: boolean
description: Shows copied confirmation after clipboard copy
events:
- name: onCopy
payload: null
description: Fired when user copies link to clipboard
uses_components: [component_social_share_buttons]
uses_apis: []
styling:
- Centered modal with backdrop
- Copy to clipboard button with icon
- Success message after copy
- Close button
- Responsive design for mobile
- id: component_social_share_buttons
name: SocialShareButtons
description: Social media share buttons for Twitter and Facebook
file_path: components/SocialShareButtons.tsx
props:
- name: url
type: string
required: true
description: URL to share
- name: title
type: string
required: true
description: Title/text to include in share
- name: type
type: "'song' | 'playlist' | 'album'"
required: false
description: Type of content for customized share text
events:
- name: onPlatformShare
payload: "{ platform: string }"
description: Fired when user clicks a social share button
uses_components: []
uses_apis: []
business_logic:
- Twitter button opens Twitter intent URL with pre-filled text
- Facebook button opens Facebook sharer dialog
- Generate share text based on content type
- URL encode parameters properly
styling:
- Buttons with platform brand colors
- Platform icons (Twitter bird, Facebook f)
- Hover and focus states
- Responsive layout (stack on mobile)
- id: component_share_content_display
name: SharedContentDisplay
description: Displays shared song/playlist/album with call-to-action
file_path: components/SharedContentDisplay.tsx
props:
- name: type
type: "'song' | 'playlist' | 'album'"
required: true
description: Type of shared content
- name: content
type: object
required: true
description: Content data (song/playlist/album with artist info)
state:
- name: isPlaying
type: boolean
description: Audio playback state (for songs)
uses_components: []
uses_apis: [api_track_share_click]
business_logic:
- Display cover art, title, artist name
- For songs: show waveform, duration, play button
- For playlists: show track count, curator
- For albums: show release date, track count
- 'Call-to-action button: "Listen on Sonic Cloud" or "Sign up to create playlists"'
styling:
- Large cover art
- Prominent title and artist
- Clean, minimal design focused on content
- Branded CTA button
- Preview player for songs (if implemented)
utils:
- id: util_generate_share_token
name: generateShareToken
description: Generate unique URL-safe token for share links
file_path: lib/share.ts
function_signature: "() => string"
implementation:
- Generate random 10-character alphanumeric string
- Use crypto-safe random generation
- Verify uniqueness against existing tokens
- Return URL-safe token (no special characters)
- id: util_build_share_url
name: buildShareUrl
description: Build full share URL from token
file_path: lib/share.ts
function_signature: "(token: string) => string"
implementation:
- Get base URL from environment or request
- Construct URL: https://domain.com/s/[token]
- Return full shareable URL
- id: util_build_social_share_urls
name: buildSocialShareUrls
description: Generate platform-specific share URLs
file_path: lib/share.ts
function_signature: "(url: string, title: string, type: string) => { twitter: string, facebook: string }"
implementation:
- Twitter: https://twitter.com/intent/tweet?text=[title]&url=[url]
- Facebook: https://www.facebook.com/sharer/sharer.php?u=[url]
- URL encode all parameters
- Customize share text based on content type
types:
- id: type_share_type
name: ShareType
description: Enum for share content types
file_path: types/share.ts
definition: "enum ShareType { SONG = 'SONG', PLAYLIST = 'PLAYLIST', ALBUM = 'ALBUM' }"
- id: type_share
name: Share
description: TypeScript type for Share model
file_path: types/share.ts
definition: |
interface Share {
id: string;
type: ShareType;
targetId: string;
token: string;
userId: string | null;
platform: string | null;
clickCount: number;
createdAt: Date;
}
- id: type_share_response
name: ShareResponse
description: API response for share creation
file_path: types/share.ts
definition: |
interface ShareResponse {
shareUrl: string;
token: string;
type: ShareType;
}
- id: type_resolved_share
name: ResolvedShare
description: API response for share resolution
file_path: types/share.ts
definition: |
interface ResolvedShare {
type: ShareType;
targetId: string;
content: Song | Playlist | Album;
shareUrl: string;
}
architecture:
layer_1_data:
- Add Share model to Prisma schema
- Create ShareType enum in Prisma
- Add optional userId relation to User model
- Migration: create shares table with indexes
layer_2_api:
- POST /api/share/song/[id] - Create song share
- POST /api/share/playlist/[id] - Create playlist share
- POST /api/share/album/[id] - Create album share
- GET /api/share/[token] - Resolve share and fetch content
- POST /api/share/[token]/click - Track analytics
layer_3_ui:
- ShareButton component (trigger)
- ShareModal component (display options)
- SocialShareButtons component (Twitter/Facebook)
- SharedContentDisplay component (public page)
- /s/[token] page (public landing page)
layer_4_util:
- lib/share.ts - Token generation and URL building utilities
dependencies:
data_to_api:
model_share:
- api_create_song_share
- api_create_playlist_share
- api_create_album_share
- api_resolve_share
- api_track_share_click
api_to_component:
api_create_song_share: [component_share_button]
api_create_playlist_share: [component_share_button]
api_create_album_share: [component_share_button]
api_resolve_share: [component_share_content_display]
api_track_share_click: [component_share_content_display]
component_to_page:
component_share_button: [page_home, page_artist_profile, page_album_detail, page_playlist_detail, page_search]
component_share_content_display: [page_share]
component_to_component:
component_share_button:
- component_share_modal
component_share_modal:
- component_social_share_buttons
implementation_order:
phase_1_data:
- Add ShareType enum to Prisma schema
- Add Share model to Prisma schema
- Create and run migration
- Generate Prisma client
phase_2_utilities:
- Create lib/share.ts with token generation
- Create types/share.ts with TypeScript types
- Add share URL building utilities
phase_3_api:
- Implement POST /api/share/song/[id]
- Implement POST /api/share/playlist/[id]
- Implement POST /api/share/album/[id]
- Implement GET /api/share/[token]
- Implement POST /api/share/[token]/click
phase_4_components:
- Create SocialShareButtons component
- Create ShareModal component
- Create ShareButton component
- Create SharedContentDisplay component
phase_5_pages:
- Create /s/[token] page
- Integrate ShareButton into existing pages
testing_requirements:
unit_tests:
- Token generation uniqueness
- URL building correctness
- Share creation for each content type
- Share resolution with valid/invalid tokens
- Click count increment
integration_tests:
- Full share flow: create → resolve → display
- Social share URL generation
- Public page rendering for all content types
- Analytics tracking
edge_cases:
- Share private content (should fail or make public)
- Share deleted content (404 on resolution)
- Token collision (should regenerate)
- Anonymous vs authenticated shares
security_considerations:
- Share tokens must be cryptographically random (10+ characters)
- Rate limit share creation to prevent abuse
- Validate content is public before sharing
- Sanitize all user input in share URLs
- Handle deleted/private content gracefully
- No PII in share URLs
seo_requirements:
- Dynamic Open Graph tags on /s/[token] pages
- og:title from shared content
- og:image from cover art
- og:description from content description
- Twitter Card meta tags
- Canonical URL handling
analytics_requirements:
- Track share creation by content type
- Track share clicks (clickCount field)
- Track platform (Twitter, Facebook, copy link)
- Track conversion from share page to signup/login