# QDR Temporary Shared Storage - REST API Documentation ## Introduction The QDR Temporary Shared Storage plugin provides a comprehensive REST API for uploading, managing, and downloading temporary shared files. This document outlines the available endpoints, authentication requirements, and provides examples in both PHP and JavaScript. ## Authentication All API requests require an API key sent in the `X-Api-Key` HTTP header. You can generate or view your API key in the plugin settings page at "Settings > Shared Storage". ## API Base URL The base URL for all API endpoints is: ``` https://your-wordpress-site.com/wp-json/qdr-tss/v1/ ``` ## Endpoints ### 1. Initialize Chunked Upload Prepares the server for a chunked upload. - **Endpoint**: `/upload/init` - **Method**: `POST` - **Parameters**: - `original_file_name`: The original name of the file being uploaded - `file_size`: The total size of the file in bytes - **Response**: ```json { "upload_id": "5f8d7a6b9c3e", "status": "initialized" } ``` ### 2. Upload Chunk Uploads a single chunk of the file. - **Endpoint**: `/upload/chunk` - **Method**: `POST` - **Parameters**: - `upload_id`: The upload ID received from the initialization - `chunk_index`: The index of the current chunk (starting from 0) - `total_chunks`: The total number of chunks - `chunk`: The chunk file data (multipart/form-data) - **Response**: ```json { "upload_id": "5f8d7a6b9c3e", "chunk_index": 0, "status": "chunk_uploaded" } ``` ### 3. Finalize Upload Combines all chunks and creates the file record. - **Endpoint**: `/upload/finalize` - **Method**: `POST` - **Parameters**: - `upload_id`: The upload ID received from the initialization - `total_chunks`: The total number of chunks - `original_file_name`: The original name of the file - `description`: Description of the file - `message`: Custom message (optional) - `active_from`: Date from which permalink will be active (optional, defaults to current time) - `active_to`: Date until which permalink will be active (optional, defaults to 30 days from now) - `password`: Password for download protection - `reference`: Custom reference value (optional) - **Response**: ```json { "media_id": "1621234567", "permalink": "https://your-wordpress-site.com/shared-file-download/1621234567", "shortcode": "[qdr_tss_download id=\"1621234567\"]" } ``` ### 4. Get File Details Retrieves details about a specific file. - **Endpoint**: `/media/{media_id}` - **Method**: `GET` - **Response**: ```json { "id": 1, "media_id": "1621234567", "original_file_name": "example.pdf", "description": "Example document", "message": "Please review this document", "active_from": "2025-05-19 15:30:00", "active_to": "2025-06-18 15:30:00", "reference": "REF123", "upload_date": "2025-05-19 15:30:00", "download_count": 5, "last_download": "2025-05-20 10:45:30", "file_size": 1048576 } ``` ### 5. Update File Updates properties of a specific file. - **Endpoint**: `/media/{media_id}` - **Method**: `PUT` - **Parameters**: - `message`: Custom message (optional) - `active_from`: Date from which permalink will be active (optional) - `active_to`: Date until which permalink will be active (optional) - `reference`: Custom reference value (optional) - `description`: File description (optional) - **Response**: Updated file details (same format as Get File Details) ### 6. Delete File Deletes a specific file. - **Endpoint**: `/media/{media_id}` - **Method**: `DELETE` - **Response**: ```json { "deleted": true, "media_id": "1621234567" } ``` ### 7. List Files Retrieves a list of files with pagination and filtering. - **Endpoint**: `/media` - **Method**: `GET` - **Parameters**: - `page`: Page number (optional, default: 1) - `per_page`: Items per page (optional, default: 10) - `orderby`: Field to sort by (optional, default: "upload_date") - `order`: Sort order (optional, default: "DESC") - `original_file_name`: Filter by file name (optional) - `reference`: Filter by reference (optional) - **Response**: ```json [ { "id": 1, "media_id": "1621234567", "original_file_name": "example.pdf", "description": "Example document", "message": "Please review this document", "active_from": "2025-05-19 15:30:00", "active_to": "2025-06-18 15:30:00", "reference": "REF123", "upload_date": "2025-05-19 15:30:00", "download_count": 5, "last_download": "2025-05-20 10:45:30", "file_size": 1048576 }, ... ] ``` ## Code Examples ### PHP Example: Complete File Upload ```php $file_name, 'file_size' => $file_size ])); $response = curl_exec($ch); curl_close($ch); $init_result = json_decode($response, true); if (!isset($init_result['upload_id'])) { throw new Exception('Failed to initialize upload: ' . $response); } $upload_id = $init_result['upload_id']; // Step 2: Upload chunks $file = fopen($file_path, 'rb'); for ($i = 0; $i < $total_chunks; $i++) { // Create a temporary file for the chunk $chunk_file = tempnam(sys_get_temp_dir(), 'chunk'); $chunk_handle = fopen($chunk_file, 'wb'); // Read chunk from original file $chunk_data = fread($file, $chunk_size); fwrite($chunk_handle, $chunk_data); fclose($chunk_handle); // Upload the chunk $ch = curl_init($api_url . '/upload/chunk'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-Api-Key: ' . $api_key ]); $post_data = [ 'upload_id' => $upload_id, 'chunk_index' => $i, 'total_chunks' => $total_chunks, 'chunk' => new CURLFile($chunk_file, 'application/octet-stream', 'chunk') ]; curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); $response = curl_exec($ch); curl_close($ch); // Delete temporary chunk file unlink($chunk_file); $chunk_result = json_decode($response, true); if (!isset($chunk_result['status']) || $chunk_result['status'] !== 'chunk_uploaded') { throw new Exception('Failed to upload chunk ' . $i . ': ' . $response); } // Display progress echo "Uploaded chunk " . ($i + 1) . " of " . $total_chunks . "\n"; } fclose($file); // Step 3: Finalize upload $ch = curl_init($api_url . '/upload/finalize'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-Api-Key: ' . $api_key, 'Content-Type: application/json' ]); // Set expiration to 30 days from now $active_to = date('Y-m-d H:i:s', strtotime('+30 days')); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'upload_id' => $upload_id, 'total_chunks' => $total_chunks, 'original_file_name' => $file_name, 'description' => $description, 'message' => 'Uploaded via API example', 'password' => $password, 'reference' => $reference, 'active_to' => $active_to ])); $response = curl_exec($ch); curl_close($ch); $final_result = json_decode($response, true); if (!isset($final_result['media_id'])) { throw new Exception('Failed to finalize upload: ' . $response); } return $final_result; } // Example usage try { $result = uploadSharedFile( '/path/to/your/file.pdf', 'Example document uploaded via API', 'secure_password', 'REF123' ); echo "File uploaded successfully!\n"; echo "Media ID: " . $result['media_id'] . "\n"; echo "Permalink: " . $result['permalink'] . "\n"; echo "Shortcode: " . $result['shortcode'] . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; } ``` ### JavaScript Example: Chunked File Upload ```javascript /** * Uploads a file using chunked upload * @param {File} file The file to upload * @param {string} description File description * @param {string} password Password for download protection * @param {string} reference Optional reference * @param {function} progressCallback Callback for upload progress * @returns {Promise} Promise resolving to the upload result */ async function uploadSharedFile(file, description, password, reference = '', progressCallback = null) { const apiKey = 'your-api-key-here'; const apiUrl = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1'; const chunkSize = 1024 * 1024; // 1MB chunks const totalChunks = Math.ceil(file.size / chunkSize); // Step 1: Initialize upload const initResponse = await fetch(`${apiUrl}/upload/init`, { method: 'POST', headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ original_file_name: file.name, file_size: file.size }) }); const initResult = await initResponse.json(); if (!initResult.upload_id) { throw new Error('Failed to initialize upload'); } const uploadId = initResult.upload_id; // Step 2: Upload chunks for (let i = 0; i < totalChunks; i++) { const start = i * chunkSize; const end = Math.min(file.size, start + chunkSize); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('upload_id', uploadId); formData.append('chunk_index', i); formData.append('total_chunks', totalChunks); formData.append('chunk', chunk); const chunkResponse = await fetch(`${apiUrl}/upload/chunk`, { method: 'POST', headers: { 'X-Api-Key': apiKey }, body: formData }); const chunkResult = await chunkResponse.json(); if (!chunkResult.status || chunkResult.status !== 'chunk_uploaded') { throw new Error(`Failed to upload chunk ${i}`); } // Update progress if (progressCallback) { progressCallback((i + 1) / totalChunks * 100); } } // Step 3: Finalize upload const activeToDate = new Date(); activeToDate.setDate(activeToDate.getDate() + 30); // 30 days from now const activeTo = activeToDate.toISOString().slice(0, 19).replace('T', ' '); const finalizeResponse = await fetch(`${apiUrl}/upload/finalize`, { method: 'POST', headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ upload_id: uploadId, total_chunks: totalChunks, original_file_name: file.name, description: description, message: 'Uploaded via JavaScript API example', password: password, reference: reference, active_to: activeTo }) }); const finalResult = await finalizeResponse.json(); if (!finalResult.media_id) { throw new Error('Failed to finalize upload'); } return finalResult; } // Example usage with HTML file input document.getElementById('file-input').addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file) return; const progressBar = document.getElementById('progress-bar'); try { const result = await uploadSharedFile( file, 'Example document uploaded via JavaScript', 'secure_password', 'REF123', (progress) => { progressBar.style.width = `${progress}%`; } ); console.log('File uploaded successfully!'); console.log(`Media ID: ${result.media_id}`); console.log(`Permalink: ${result.permalink}`); console.log(`Shortcode: ${result.shortcode}`); } catch (error) { console.error('Error:', error.message); } }); ``` ### PHP Example: Get File List ```php $page, 'per_page' => $perPage, 'orderby' => $orderBy, 'order' => $order, 'original_file_name' => $fileName, 'reference' => $reference ]); $ch = curl_init($api_url . '/media?' . $query_params); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-Api-Key: ' . $api_key ]); $response = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $header_size); $body = substr($response, $header_size); // Get pagination headers $total = 0; $total_pages = 0; foreach (explode("\r\n", $header) as $line) { if (strpos($line, 'X-WP-Total:') === 0) { $total = intval(trim(str_replace('X-WP-Total:', '', $line))); } else if (strpos($line, 'X-WP-TotalPages:') === 0) { $total_pages = intval(trim(str_replace('X-WP-TotalPages:', '', $line))); } } curl_close($ch); $files = json_decode($body, true); return [ 'files' => $files, 'total' => $total, 'total_pages' => $total_pages ]; } // Example usage $result = getSharedFiles(1, 10, 'upload_date', 'DESC', '', 'REF123'); echo "Total files: " . $result['total'] . "\n"; echo "Total pages: " . $result['total_pages'] . "\n"; foreach ($result['files'] as $file) { echo "ID: " . $file['media_id'] . ", Name: " . $file['original_file_name'] . "\n"; } ``` ### JavaScript Example: Update File Properties ```javascript /** * Updates file properties * @param {string} mediaId The media ID * @param {object} properties Properties to update * @returns {Promise} Promise resolving to the updated file */ async function updateSharedFile(mediaId, properties) { const apiKey = 'your-api-key-here'; const apiUrl = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1'; const response = await fetch(`${apiUrl}/media/${mediaId}`, { method: 'PUT', headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify(properties) }); const result = await response.json(); if (!result.media_id) { throw new Error('Failed to update file'); } return result; } // Example usage async function exampleUpdateFile() { try { const updatedFile = await updateSharedFile('1621234567', { message: 'Updated message', active_to: '2025-07-19 15:30:00', reference: 'UPDATED-REF' }); console.log('File updated successfully!'); console.log(updatedFile); } catch (error) { console.error('Error:', error.message); } } exampleUpdateFile(); ``` ## Error Handling All API endpoints return standard HTTP status codes: - `200 OK`: Request succeeded - `201 Created`: Resource created successfully - `400 Bad Request`: Invalid request parameters - `401 Unauthorized`: Missing API key - `403 Forbidden`: Invalid API key - `404 Not Found`: Resource not found - `500 Internal Server Error`: Server error Error responses include a message explaining the error: ```json { "code": "qdr_tss_not_found", "message": "Media not found.", "data": { "status": 404 } } ``` ## Rate Limiting There are currently no rate limits on the API, but excessive usage may be throttled in the future. ## Best Practices 1. Always use chunked uploads for large files 2. Set reasonable expiration dates for files to avoid cluttering storage 3. Use unique reference codes to help identify files 4. Regularly check and purge expired files using the admin interface 5. Include descriptive file names and messages for better user experience