workflow_version: "v005" feature: "examine what is missing in current app and implement it" created_at: "2025-12-20T22:05:00Z" status: "draft" revision: 1 data_models: # New RefreshToken model for token rotation - id: model_refresh_token name: RefreshToken description: "JWT refresh tokens for secure authentication" table_name: refresh_tokens fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique identifier" - name: token type: string constraints: ["unique", "not_null"] description: "Refresh token value" - name: userId type: string constraints: ["not_null", "indexed"] description: "User who owns this token" - name: expiresAt type: datetime constraints: ["not_null", "indexed"] description: "Token expiration time" - name: createdAt type: datetime constraints: ["not_null", "default(now())"] description: "When token was created" - name: isRevoked type: boolean constraints: ["default(false)", "indexed"] description: "Whether token has been revoked" relations: - type: belongs_to target: model_user foreign_key: userId on_delete: cascade timestamps: false # New Session model for active session tracking - id: model_session name: Session description: "Active user sessions with device tracking" table_name: sessions fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique session identifier" - name: userId type: string constraints: ["not_null", "indexed"] description: "User who owns this session" - name: token type: string constraints: ["unique", "not_null"] description: "Session token" - name: deviceInfo type: json constraints: [] description: "Device information (browser, OS)" - name: ipAddress type: string constraints: [] description: "Client IP address" - name: userAgent type: string constraints: [] description: "Browser user agent" - name: lastActivity type: datetime constraints: ["default(now())"] description: "Last activity timestamp" - name: createdAt type: datetime constraints: ["not_null", "default(now())"] description: "When session was created" relations: - type: belongs_to target: model_user foreign_key: userId relation_name: UserSessions on_delete: cascade timestamps: false # New PlayHistory model - id: model_play_history name: PlayHistory description: "Track user play history for analytics" table_name: play_history fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique identifier" - name: userId type: string constraints: ["not_null", "indexed"] description: "User who played the song" - name: songId type: string constraints: ["not_null", "indexed"] description: "Song that was played" - name: playedAt type: datetime constraints: ["not_null", "default(now())", "indexed"] description: "When playback started" - name: playedDuration type: integer constraints: [] description: "Seconds actually played" - name: completed type: boolean constraints: ["default(false)"] description: "Did user listen to completion" - name: source type: string constraints: [] description: "Where playback was initiated" relations: - type: belongs_to target: model_user foreign_key: userId relation_name: UserPlayHistory on_delete: cascade - type: belongs_to target: model_song foreign_key: songId relation_name: SongPlayHistory on_delete: cascade timestamps: false # New Queue model for persistent queues - id: model_queue name: Queue description: "User playback queue with state" table_name: queues fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique queue identifier" - name: userId type: string constraints: ["not_null", "unique"] description: "User who owns this queue" - name: songIds type: json constraints: ["not_null"] description: "Array of song IDs in order" - name: currentIndex type: integer constraints: ["default(0)"] description: "Current playing index" - name: isShuffled type: boolean constraints: ["default(false)"] description: "Whether queue is shuffled" - name: repeatMode type: string constraints: ["default('none')"] description: "Repeat mode: none, one, all" - name: createdAt type: datetime constraints: ["not_null", "default(now())"] description: "When queue was created" - name: updatedAt type: datetime constraints: ["not_null", "default(now())"] description: "Last update time" relations: - type: belongs_to target: model_user foreign_key: userId relation_name: UserQueue on_delete: cascade timestamps: false # New UploadSession model for chunked uploads - id: model_upload_session name: UploadSession description: "Chunked upload session tracking" table_name: upload_sessions fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique upload session ID" - name: userId type: string constraints: ["not_null", "indexed"] description: "User uploading the file" - name: fileName type: string constraints: ["not_null"] description: "Original file name" - name: fileSize type: integer constraints: ["not_null"] description: "Total file size in bytes" - name: mimeType type: string constraints: ["not_null"] description: "File MIME type" - name: chunkSize type: integer constraints: ["not_null"] description: "Size of each chunk" - name: totalChunks type: integer constraints: ["not_null"] description: "Total number of chunks" - name: uploadedChunks type: json constraints: [] description: "Array of uploaded chunk numbers" - name: status type: string constraints: ["default('pending')"] description: "Upload status" - name: fileId type: string constraints: [] description: "Associated file ID when complete" - name: metadata type: json constraints: [] description: "Additional file metadata" - name: createdAt type: datetime constraints: ["not_null", "default(now())"] description: "When upload started" - name: expiresAt type: datetime constraints: ["not_null"] description: "When upload session expires" relations: - type: belongs_to target: model_user foreign_key: userId on_delete: cascade timestamps: false # New SearchIndex model - id: model_search_index name: SearchIndex description: "Full-text search index for entities" table_name: search_index fields: - name: id type: string constraints: ["primary_key", "default(cuid())"] description: "Unique index entry ID" - name: entityType type: string constraints: ["not_null", "indexed"] description: "Type of entity (song, album, artist)" - name: entityId type: string constraints: ["not_null", "indexed"] description: "ID of the indexed entity" - name: title type: string constraints: ["not_null"] description: "Entity title for search" - name: content type: text constraints: [] description: "Full text content" - name: metadata type: json constraints: [] description: "Additional searchable metadata" - name: createdAt type: datetime constraints: ["not_null", "default(now())"] description: "When indexed" - name: updatedAt type: datetime constraints: ["not_null", "default(now())"] description: "Last update" indexes: - fields: ["entityType", "entityId"] unique: true name: idx_search_entity timestamps: false api_endpoints: # Session Management - id: api_auth_refresh method: POST path: /api/auth/refresh description: "Refresh access token using refresh token" auth_required: false request_body: refreshToken: string responses: - status: 200 description: "Token refreshed successfully" - status: 401 description: "Invalid or expired refresh token" - id: api_auth_logout method: POST path: /api/auth/logout description: "Logout user and invalidate tokens" auth_required: true - id: api_auth_sessions method: GET path: /api/auth/sessions description: "List all active sessions" auth_required: true - id: api_auth_revoke_session method: DELETE path: /api/auth/sessions/:id description: "Revoke specific session" auth_required: true # Email Verification - id: api_auth_verify_email method: POST path: /api/auth/verify-email description: "Send email verification" auth_required: true - id: api_auth_confirm_email method: POST path: /api/auth/confirm-email description: "Confirm email with token" auth_required: false # Audio Player API - id: api_player_play method: POST path: /api/player/play description: "Start or resume playback" auth_required: true - id: api_player_pause method: POST path: /api/player/pause description: "Pause playback" auth_required: true - id: api_player_next method: POST path: /api/player/next description: "Skip to next track" auth_required: true - id: api_player_previous method: POST path: /api/player/previous description: "Go to previous track" auth_required: true - id: api_player_queue method: GET path: /api/player/queue description: "Get current queue" auth_required: true - id: api_player_queue_add method: POST path: /api/player/queue description: "Add songs to queue" auth_required: true - id: api_player_queue_clear method: DELETE path: /api/player/queue description: "Clear queue" auth_required: true - id: api_player_history method: GET path: /api/player/history description: "Get play history" auth_required: true # File Upload API - id: api_upload_init method: POST path: /api/upload/init description: "Initialize chunked upload" auth_required: true - id: api_upload_chunk method: POST path: /api/upload/chunk/:uploadId/:chunkIndex description: "Upload chunk" auth_required: true - id: api_upload_complete method: POST path: /api/upload/complete/:uploadId description: "Complete upload" auth_required: true - id: api_upload_presigned method: GET path: /api/upload/presigned-url description: "Get presigned URL for direct upload" auth_required: true # Search API - id: api_search method: GET path: /api/search description: "Search across all content" auth_required: false - id: api_search_suggestions method: GET path: /api/search/suggestions description: "Get search suggestions" auth_required: false - id: api_search_index method: POST path: /api/search/index description: "Index entity for search" auth_required: true pages: - id: page_auth_verify_email name: EmailVerification path: /auth/verify-email description: "Email verification page" data_needs: [] components: [] auth_required: false - id: page_settings_security name: SecuritySettings path: /settings/security description: "Security settings page" data_needs: - api_id: api_auth_sessions purpose: "Load active sessions" on_load: true components: - component_session_list - component_change_password auth_required: true components: - id: component_audio_player name: AudioPlayer description: "Full-featured audio player with controls" props: - name: song type: object required: false description: "Current playing song" - name: isPlaying type: boolean required: true description: "Playback state" - name: progress type: number required: true description: "Current playback progress (0-1)" - name: volume type: number required: true description: "Volume level (0-1)" - name: queue type: object required: false description: "Current queue" events: - name: play description: "Play or resume playback" - name: pause description: "Pause playback" - name: seek description: "Seek to position" - name: volumeChange description: "Change volume" - name: next description: "Play next song" - name: previous description: "Play previous song" - name: shuffle description: "Toggle shuffle" - name: repeat description: "Set repeat mode" uses_apis: - api_player_play - api_player_pause - api_player_next - api_player_previous - api_player_queue - id: component_upload_manager name: UploadManager description: "Handles chunked file uploads" props: - name: onUploadComplete type: function required: true description: "Callback when upload completes" - name: acceptedTypes type: array required: false description: "Accepted file types" - name: maxSize type: number required: false description: "Max file size in bytes" events: - name: progress description: "Upload progress update" - name: error description: "Upload error" uses_apis: - api_upload_init - api_upload_chunk - api_upload_complete - id: component_search_bar name: SearchBar description: "Search input with suggestions" props: - name: placeholder type: string required: false description: "Input placeholder" - name: autoFocus type: boolean required: false description: "Auto focus input" events: - name: search description: "Search query submitted" - name: suggestionSelect description: "Suggestion selected" uses_apis: - api_search_suggestions - id: component_search_results name: SearchResults description: "Display search results" props: - name: query type: string required: true description: "Search query" - name: filters type: object required: false description: "Search filters" events: - name: playSong description: "Play selected song" - name: viewAlbum description: "View album details" - name: viewArtist description: "View artist profile" uses_apis: - api_search