Upload an application's image and installer instead of typing paths
Adding an application meant typing an image FILENAME and trusting someone had dropped the file into the frontend's own directory by hand, and typing an install path from memory. Both are uploads now, following the model-image trio that models and part photos already use. The two differ deliberately. The image is public, because application tiles render before anything is authenticated. The installer is not: it is licensed vendor software, an open URL would publish it to anything that can reach the site, and it is always sent as an attachment rather than rendered. Installers are capped at 500MB and the size is measured by seeking the stream rather than trusting Content-Length, which a chunked upload does not send and a client can understate. Anything larger belongs on the share, and the error says so rather than just refusing. Files are chosen before a new application exists, so they are held and uploaded once there is an id to attach them to. A failed upload leaves the saved record alone and reports, rather than losing what saved fine. Removing an installer only clears installpath when it pointed at the upload - a share path was typed by a person and is not ours to wipe. The detail page reads both shapes, since entries from the classic site hold a bare filename that is still served from /images/applications/.
This commit is contained in:
@@ -306,6 +306,9 @@ export const printersApi = {
|
||||
getDrivers(id) {
|
||||
return api.get(`/printers/${id}/drivers`)
|
||||
},
|
||||
supplyForecast(days = 90) {
|
||||
return api.get('/printers/supplies/forecast', { params: { days } })
|
||||
},
|
||||
lowSupplies() {
|
||||
return api.get('/printers/lowsupplies')
|
||||
},
|
||||
@@ -477,6 +480,33 @@ export const applicationsApi = {
|
||||
delete(id) {
|
||||
return api.delete(`/applications/${id}`)
|
||||
},
|
||||
// multipart image upload; backend sets image to the served URL
|
||||
uploadImage(id, file) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return api.post(`/applications/${id}/image`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
|
||||
},
|
||||
removeImage(id) {
|
||||
return api.delete(`/applications/${id}/image`)
|
||||
},
|
||||
// installer upload; backend sets installpath to the download URL.
|
||||
// onProgress gets 0-100 - an installer is big enough that a silent wait
|
||||
// reads as a hang.
|
||||
uploadPackage(id, file, onProgress) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return api.post(`/applications/${id}/package`, form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
onUploadProgress: event => {
|
||||
if (onProgress && event.total) {
|
||||
onProgress(Math.round((event.loaded * 100) / event.total))
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
removePackage(id) {
|
||||
return api.delete(`/applications/${id}/package`)
|
||||
},
|
||||
// Versions
|
||||
getVersions(appId) {
|
||||
return api.get(`/applications/${appId}/versions`)
|
||||
@@ -1088,6 +1118,15 @@ export const warrantyApi = {
|
||||
},
|
||||
report() {
|
||||
return api.get('/warranty/report')
|
||||
},
|
||||
// proof of cover: invoice, certificate, whatever the vendor sent
|
||||
uploadProof(id, file) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return api.post(`/warranty/${id}/proof`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
|
||||
},
|
||||
removeProof(id) {
|
||||
return api.delete(`/warranty/${id}/proof`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user