max / makenotwork
10 files changed,
+40 insertions,
-24 deletions
| @@ -0,0 +1,3 @@ | |||
| 1 | + | -- Add optional label to versions (e.g. "macOS (arm)", "Linux (x86_64)") | |
| 2 | + | -- for distinguishing platform variants within a single item. | |
| 3 | + | ALTER TABLE versions ADD COLUMN label TEXT; |
| @@ -211,6 +211,8 @@ pub struct DbVersion { | |||
| 211 | 211 | pub s3_key: Option<String>, | |
| 212 | 212 | /// Malware scan status for uploaded files. | |
| 213 | 213 | pub scan_status: super::super::FileScanStatus, | |
| 214 | + | /// Optional short label (e.g. "macOS (arm)", "Linux (x86_64)"). | |
| 215 | + | pub label: Option<String>, | |
| 214 | 216 | } | |
| 215 | 217 | ||
| 216 | 218 | /// A chapter marker within an audio item. |
| @@ -20,6 +20,7 @@ pub async fn create_version( | |||
| 20 | 20 | file_url: Option<&str>, | |
| 21 | 21 | file_size_bytes: Option<i64>, | |
| 22 | 22 | file_name: Option<&str>, | |
| 23 | + | label: Option<&str>, | |
| 23 | 24 | ) -> Result<DbVersion> { | |
| 24 | 25 | let mut tx = pool.begin().await?; | |
| 25 | 26 | ||
| @@ -32,8 +33,8 @@ pub async fn create_version( | |||
| 32 | 33 | // Create new version as current | |
| 33 | 34 | let version = sqlx::query_as::<_, DbVersion>( | |
| 34 | 35 | r#" | |
| 35 | - | INSERT INTO versions (item_id, version_number, changelog, file_url, file_size_bytes, file_name, is_current) | |
| 36 | - | VALUES ($1, $2, $3, $4, $5, $6, true) | |
| 36 | + | INSERT INTO versions (item_id, version_number, changelog, file_url, file_size_bytes, file_name, is_current, label) | |
| 37 | + | VALUES ($1, $2, $3, $4, $5, $6, true, $7) | |
| 37 | 38 | RETURNING * | |
| 38 | 39 | "#, | |
| 39 | 40 | ) | |
| @@ -43,6 +44,7 @@ pub async fn create_version( | |||
| 43 | 44 | .bind(file_url) | |
| 44 | 45 | .bind(file_size_bytes) | |
| 45 | 46 | .bind(file_name) | |
| 47 | + | .bind(label) | |
| 46 | 48 | .fetch_one(&mut *tx) | |
| 47 | 49 | .await?; | |
| 48 | 50 |
| @@ -202,6 +202,7 @@ pub(super) async fn confirm_upload( | |||
| 202 | 202 | Some(&req.s3_key), | |
| 203 | 203 | Some(file_size_bytes), | |
| 204 | 204 | file_name.as_deref(), | |
| 205 | + | None, | |
| 205 | 206 | ) | |
| 206 | 207 | .await?; | |
| 207 | 208 | db::scanning::update_version_scan_status(&state.db, version.id, status).await?; |
| @@ -26,6 +26,7 @@ pub struct CreateVersionRequest { | |||
| 26 | 26 | pub file_url: Option<String>, | |
| 27 | 27 | pub file_size_bytes: Option<i64>, | |
| 28 | 28 | pub file_name: Option<String>, | |
| 29 | + | pub label: Option<String>, | |
| 29 | 30 | } | |
| 30 | 31 | ||
| 31 | 32 | /// JSON response representing an item version. | |
| @@ -65,6 +66,7 @@ pub(in crate::routes::api) async fn create_version( | |||
| 65 | 66 | req.file_url.as_deref(), | |
| 66 | 67 | req.file_size_bytes, | |
| 67 | 68 | req.file_name.as_deref(), | |
| 69 | + | req.label.as_deref(), | |
| 68 | 70 | ) | |
| 69 | 71 | .await?; | |
| 70 | 72 |
| @@ -447,6 +447,7 @@ impl Version { | |||
| 447 | 447 | is_current: v.is_current, | |
| 448 | 448 | has_file, | |
| 449 | 449 | file_name: v.file_name.clone(), | |
| 450 | + | label: v.label.clone(), | |
| 450 | 451 | } | |
| 451 | 452 | } | |
| 452 | 453 | } |
| @@ -732,6 +732,7 @@ pub struct Version { | |||
| 732 | 732 | pub is_current: bool, | |
| 733 | 733 | pub has_file: bool, | |
| 734 | 734 | pub file_name: Option<String>, | |
| 735 | + | pub label: Option<String>, | |
| 735 | 736 | } | |
| 736 | 737 | ||
| 737 | 738 | /// Row data for displaying a license key in the creator dashboard. |
| @@ -169,12 +169,14 @@ | |||
| 169 | 169 | if (!versionNumber) { showToast('Please enter a version number.'); return; } | |
| 170 | 170 | if (!selectedFile) { showToast('Please select a file to upload.'); return; } | |
| 171 | 171 | ||
| 172 | + | var label = (document.getElementById('new-version-label') || {}).value || ''; | |
| 172 | 173 | fetch('/api/items/' + itemId + '/versions', { | |
| 173 | 174 | method: 'POST', | |
| 174 | 175 | headers: { 'Content-Type': 'application/json', ...csrfHeaders() }, | |
| 175 | 176 | body: JSON.stringify({ | |
| 176 | 177 | version_number: versionNumber, | |
| 177 | - | changelog: changelog || null | |
| 178 | + | changelog: changelog || null, | |
| 179 | + | label: label.trim() || null | |
| 178 | 180 | }) | |
| 179 | 181 | }) | |
| 180 | 182 | .then(function(res) { |
| @@ -122,8 +122,8 @@ | |||
| 122 | 122 | {% endblock %} | |
| 123 | 123 | ||
| 124 | 124 | {% block scripts %} | |
| 125 | - | <script src="/static/upload.js?v=0514"></script> | |
| 126 | - | <script src="/static/media-picker.js?v=0514"></script> | |
| 127 | - | <script src="/static/item-details.js?v=0514"></script> | |
| 128 | - | <script src="/static/item-upload.js?v=0514"></script> | |
| 125 | + | <script src="/static/upload.js?v=0515"></script> | |
| 126 | + | <script src="/static/media-picker.js?v=0515"></script> | |
| 127 | + | <script src="/static/item-details.js?v=0515"></script> | |
| 128 | + | <script src="/static/item-upload.js?v=0515"></script> | |
| 129 | 129 | {% endblock %} |
| @@ -1,22 +1,18 @@ | |||
| 1 | 1 | <div class="version-upload" id="version-upload" data-item-id="{{ item.id }}"> | |
| 2 | - | <div class="warning-box"> | |
| 3 | - | Version changes are mandatory: Any published change requires a new version number. | |
| 4 | - | </div> | |
| 5 | 2 | ||
| 6 | 3 | {% if versions.is_empty() %} | |
| 7 | 4 | <div style="text-align: center; padding: 2rem 1rem; opacity: 0.6;"> | |
| 8 | - | <p>No versions uploaded yet. Create your first version below to publish this item.</p> | |
| 5 | + | <p>No versions uploaded yet. Create your first version below.</p> | |
| 9 | 6 | </div> | |
| 10 | 7 | {% else %} | |
| 11 | 8 | <table class="data-table"> | |
| 12 | 9 | <thead> | |
| 13 | 10 | <tr> | |
| 14 | 11 | <th>Version</th> | |
| 15 | - | <th>Uploaded</th> | |
| 16 | - | <th>Files</th> | |
| 12 | + | <th>Label</th> | |
| 13 | + | <th>File</th> | |
| 17 | 14 | <th>Size</th> | |
| 18 | 15 | <th>Downloads</th> | |
| 19 | - | <th>Status</th> | |
| 20 | 16 | <th>Actions</th> | |
| 21 | 17 | </tr> | |
| 22 | 18 | </thead> | |
| @@ -24,11 +20,10 @@ | |||
| 24 | 20 | {% for version in versions %} | |
| 25 | 21 | <tr> | |
| 26 | 22 | <td><span class="badge{% if version.is_current %} current{% endif %}">v{{ version.number }}</span></td> | |
| 27 | - | <td>{{ version.uploaded_date }}</td> | |
| 28 | - | <td>{% if version.has_file %}{% match version.file_name %}{% when Some with (name) %}{{ name }}{% when None %}1 file{% endmatch %}{% else %}No file{% endif %}</td> | |
| 23 | + | <td style="font-size: 0.85rem;">{% if let Some(label) = version.label %}{{ label }}{% endif %}</td> | |
| 24 | + | <td style="font-size: 0.85rem;">{% if version.has_file %}{% match version.file_name %}{% when Some with (name) %}{{ name }}{% when None %}1 file{% endmatch %}{% else %}<span style="opacity: 0.5;">No file</span>{% endif %}</td> | |
| 29 | 25 | <td>{{ version.size }}</td> | |
| 30 | 26 | <td>{{ version.downloads }}</td> | |
| 31 | - | <td>{{ version.status }}</td> | |
| 32 | 27 | <td> | |
| 33 | 28 | {% if version.has_file %} | |
| 34 | 29 | <button class="secondary download-version-btn" style="padding: 0.4rem 0.8rem; font-size: 0.85rem;" | |
| @@ -48,19 +43,26 @@ | |||
| 48 | 43 | ||
| 49 | 44 | <!-- New Version Form --> | |
| 50 | 45 | <div id="new-version-form"> | |
| 51 | - | <div class="form-group"> | |
| 52 | - | <label for="new-version-number">New Version Number</label> | |
| 53 | - | <input type="text" id="new-version-number" placeholder="e.g., 1.3.0"> | |
| 46 | + | <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;"> | |
| 47 | + | <div class="form-group"> | |
| 48 | + | <label for="new-version-number">Version Number</label> | |
| 49 | + | <input type="text" id="new-version-number" placeholder="e.g., 1.0"> | |
| 50 | + | </div> | |
| 51 | + | ||
| 52 | + | <div class="form-group"> | |
| 53 | + | <label for="new-version-label">Label (optional)</label> | |
| 54 | + | <input type="text" id="new-version-label" placeholder="e.g., macOS (arm), Linux (x86_64)"> | |
| 55 | + | </div> | |
| 54 | 56 | </div> | |
| 55 | 57 | ||
| 56 | 58 | <div class="form-group"> | |
| 57 | - | <label for="version-changelog">Version Notes / Changelog</label> | |
| 58 | - | <textarea id="version-changelog" placeholder="Describe what changed in this version..."></textarea> | |
| 59 | + | <label for="version-changelog">Notes (optional)</label> | |
| 60 | + | <textarea id="version-changelog" rows="2" placeholder="What changed in this version..."></textarea> | |
| 59 | 61 | </div> | |
| 60 | 62 | ||
| 61 | 63 | <div class="file-upload-area" id="version-dropzone"> | |
| 62 | 64 | <div class="upload-text">Drop file here or click to upload</div> | |
| 63 | - | <div class="upload-hint">Supported: ZIP, DMG, EXE, AppImage, DEB, tar.gz, CLAP, VST3 up to 500 MB</div> | |
| 65 | + | <div class="upload-hint">ZIP, DMG, EXE, AppImage, DEB, tar.gz, CLAP, VST3 up to 500 MB</div> | |
| 64 | 66 | <input type="file" id="version-file-input" style="display: none;" | |
| 65 | 67 | accept=".zip,.dmg,.exe,.appimage,.deb,.tar.gz,.clap,.vst3"> | |
| 66 | 68 | </div> | |
| @@ -72,7 +74,7 @@ | |||
| 72 | 74 | <div class="hidden" id="existing-version-upload"> | |
| 73 | 75 | <div class="file-upload-area" id="existing-version-dropzone"> | |
| 74 | 76 | <div class="upload-text">Drop file to upload for this version</div> | |
| 75 | - | <div class="upload-hint">Supported: ZIP, DMG, EXE, AppImage, DEB, tar.gz, CLAP, VST3</div> | |
| 77 | + | <div class="upload-hint">ZIP, DMG, EXE, AppImage, DEB, tar.gz, CLAP, VST3</div> | |
| 76 | 78 | <input type="file" id="existing-version-file-input" style="display: none;" | |
| 77 | 79 | accept=".zip,.dmg,.exe,.appimage,.deb,.tar.gz,.clap,.vst3"> | |
| 78 | 80 | </div> |