[feat] Add async model upload with WebSocket progress tracking (#7746)

## Summary
- Adds asynchronous model upload support with HTTP 202 responses
- Implements WebSocket-based real-time download progress tracking via
`asset_download` events
- Creates `assetDownloadStore` for centralized download state management
and toast notifications
- Updates upload wizard UI to show "processing" state when downloads
continue in background

## Changes
- **Core**: New `assetDownloadStore` for managing async downloads with
WebSocket events
- **API**: Support for HTTP 202 async upload responses with task
tracking
- **UI**: Upload wizard now shows "processing" state and allows closing
dialog during download
- **Progress**: Periodic toast notifications (every 5s) during active
downloads with completion/error toasts
- **Schema**: Updated task statuses (`created`, `running`, `completed`,
`failed`) and WebSocket message types

## Review Focus
- WebSocket event handling and download state management in
`assetDownloadStore`
- Upload flow UX - users can now close the dialog and download continues
in background
- Toast notification frequency and timing
- Schema alignment with backend async upload API

Fixes #7748

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7746-feat-Add-async-model-upload-with-WebSocket-progress-tracking-2d36d73d3650811cb79ae06f470dcded)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Luke Mino-Altherr
2026-01-06 11:43:11 -08:00
committed by GitHub
parent fbdaf5d7f3
commit 14d0ec73f6
18 changed files with 490 additions and 88 deletions

View File

@@ -81,12 +81,19 @@
<span>{{ $t('assetBrowser.upload') }}</span>
</Button>
<Button
v-else-if="currentStep === 3 && uploadStatus === 'success'"
v-else-if="
currentStep === 3 &&
(uploadStatus === 'success' || uploadStatus === 'processing')
"
variant="secondary"
data-attr="upload-model-step3-finish-button"
@click="emit('close')"
>
{{ $t('assetBrowser.finish') }}
{{
uploadStatus === 'processing'
? $t('g.close')
: $t('assetBrowser.finish')
}}
</Button>
<VideoHelpDialog
v-model="showCivitaiHelp"
@@ -119,7 +126,7 @@ defineProps<{
isUploading: boolean
canFetchMetadata: boolean
canUploadModel: boolean
uploadStatus: 'idle' | 'uploading' | 'success' | 'error'
uploadStatus?: 'processing' | 'success' | 'error'
}>()
const emit = defineEmits<{