mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-07 06:15:36 +00:00
Resolve "The "Try to reconnect" modal shortly appears when trying to download a user file"
This commit is contained in:
parent
7b47b5a470
commit
921e3dfc08
12 changed files with 178 additions and 34 deletions
backend
changelog.mddeploy/heroku
docs/getting-started
web-frontend/modules
|
@ -28,3 +28,5 @@ click==7.1.2
|
|||
cryptography==3.4.8
|
||||
antlr4-python3-runtime==4.8.0
|
||||
tqdm==4.62.3
|
||||
boto3==1.20.38
|
||||
django-storages==1.12.3
|
||||
|
|
|
@ -266,6 +266,27 @@ SPECTACULAR_SETTINGS = {
|
|||
# The storage must always overwrite existing files.
|
||||
DEFAULT_FILE_STORAGE = "baserow.core.storage.OverwriteFileSystemStorage"
|
||||
|
||||
# Optional S3 storage configuration
|
||||
if os.getenv("AWS_ACCESS_KEY_ID", "") != "":
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
|
||||
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
|
||||
AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")
|
||||
AWS_S3_OBJECT_PARAMETERS = {
|
||||
"CacheControl": "max-age=86400",
|
||||
}
|
||||
AWS_S3_FILE_OVERWRITE = True
|
||||
AWS_DEFAULT_ACL = "public-read"
|
||||
|
||||
if os.getenv("AWS_S3_REGION_NAME", "") != "":
|
||||
AWS_S3_REGION_NAME = os.getenv("AWS_S3_REGION_NAME")
|
||||
|
||||
if os.getenv("AWS_S3_ENDPOINT_URL", "") != "":
|
||||
AWS_S3_ENDPOINT_URL = os.getenv("AWS_S3_ENDPOINT_URL")
|
||||
|
||||
if os.getenv("AWS_S3_CUSTOM_DOMAIN", "") != "":
|
||||
AWS_S3_CUSTOM_DOMAIN = os.getenv("AWS_S3_CUSTOM_DOMAIN")
|
||||
|
||||
MJML_BACKEND_MODE = "tcpserver"
|
||||
MJML_TCPSERVERS = [
|
||||
(os.getenv("MJML_SERVER_HOST", "mjml"), int(os.getenv("MJML_SERVER_PORT", 28101))),
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
* Fixed migration failing when upgrading a version of Baserow installed using Postgres
|
||||
10 or lower.
|
||||
* Fixed download/preview files from another origin
|
||||
|
||||
## Released (2022-01-13)
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ import os
|
|||
import ssl
|
||||
import dj_database_url
|
||||
|
||||
INSTALLED_APPS = INSTALLED_APPS + ["storages"]
|
||||
|
||||
MEDIA_ROOT = "/baserow/media"
|
||||
|
||||
MJML_BACKEND_MODE = "cmd"
|
||||
|
@ -39,24 +37,3 @@ CELERY_REDIS_MAX_CONNECTIONS = min(
|
|||
DATABASES = {
|
||||
"default": dj_database_url.parse(os.environ["DATABASE_URL"], conn_max_age=600)
|
||||
}
|
||||
|
||||
if os.getenv("AWS_ACCESS_KEY_ID", "") != "":
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
|
||||
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
|
||||
AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")
|
||||
AWS_S3_OBJECT_PARAMETERS = {
|
||||
"CacheControl": "max-age=86400",
|
||||
"ContentDisposition": "attachment",
|
||||
}
|
||||
AWS_S3_FILE_OVERWRITE = True
|
||||
AWS_DEFAULT_ACL = "public-read"
|
||||
|
||||
if os.getenv("AWS_S3_REGION_NAME", "") != "":
|
||||
AWS_S3_REGION_NAME = os.getenv("AWS_S3_REGION_NAME")
|
||||
|
||||
if os.getenv("AWS_S3_ENDPOINT_URL", "") != "":
|
||||
AWS_S3_ENDPOINT_URL = os.getenv("AWS_S3_ENDPOINT_URL")
|
||||
|
||||
if os.getenv("AWS_S3_CUSTOM_DOMAIN", "") != "":
|
||||
AWS_S3_CUSTOM_DOMAIN = os.getenv("AWS_S3_CUSTOM_DOMAIN")
|
||||
|
|
|
@ -90,6 +90,10 @@ are accepted.
|
|||
* `DATABASE_PASSWORD` (default `baserow`): The password for the PostgreSQL database.
|
||||
* `DATABASE_HOST` (default `db`): The hostname of the PostgreSQL server.
|
||||
* `DATABASE_PORT` (default `5432`): The port of the PostgreSQL server.
|
||||
* `DOWNLOAD_FILE_VIA_XHR` (default `0`): Set to `1` to force download links to
|
||||
download files via XHR query to bypass `Content-Disposition: inline` that
|
||||
can't be overridden in another way. If your files are stored under another
|
||||
origin, you also must add CORS headers to your server.
|
||||
* `MJML_SERVER_HOST` (default `mjml`): The hostname of the MJML TCP server. In the
|
||||
development environment we use the `liminspace/mjml-tcpserver:0.10` image.
|
||||
* `MJML_SERVER_PORT` (default `28101`): The port of the MJML TCP server.
|
||||
|
|
|
@ -42,7 +42,12 @@
|
|||
}
|
||||
|
||||
.file-field-modal__body {
|
||||
@include absolute($file-field-modal-head-height, 0, $file-field-modal-foot-height, 0);
|
||||
@include absolute(
|
||||
$file-field-modal-head-height,
|
||||
0,
|
||||
$file-field-modal-foot-height,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
.file-field-modal__body-nav {
|
||||
|
@ -75,7 +80,12 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include absolute(0, $file-field-modal-body-nav-width, 0, $file-field-modal-body-nav-width);
|
||||
@include absolute(
|
||||
0,
|
||||
$file-field-modal-body-nav-width,
|
||||
0,
|
||||
$file-field-modal-body-nav-width
|
||||
);
|
||||
}
|
||||
|
||||
.file-field-modal__preview-icon {
|
||||
|
@ -166,4 +176,20 @@
|
|||
&:hover {
|
||||
background-color: $color-neutral-700;
|
||||
}
|
||||
|
||||
&.file-field-modal__action--loading {
|
||||
// Prevent interactions while loading file
|
||||
pointer-events: none;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
|
||||
@include loading(16px);
|
||||
@include absolute(8px, 8px, auto, auto);
|
||||
}
|
||||
|
||||
& .fas {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
99
web-frontend/modules/core/components/DownloadLink.vue
Normal file
99
web-frontend/modules/core/components/DownloadLink.vue
Normal file
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<a
|
||||
v-if="!downloadXHR"
|
||||
:href="`${url}?dl=${filename}`"
|
||||
target="_blank"
|
||||
:download="filename"
|
||||
>
|
||||
<slot></slot>
|
||||
</a>
|
||||
<a
|
||||
v-else
|
||||
:href="`${url}`"
|
||||
target="_blank"
|
||||
:download="filename"
|
||||
:class="{ [loadingClass]: loading }"
|
||||
@click="onClick($event)"
|
||||
>
|
||||
<slot></slot>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DownloadLink',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
onError: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
loadingClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { loading: false }
|
||||
},
|
||||
computed: {
|
||||
downloadXHR() {
|
||||
return this.$env.DOWNLOAD_FILE_VIA_XHR === '1'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async download() {
|
||||
this.loading = true
|
||||
// We are using fetch here to avoid extra header
|
||||
// as we need to add them to CORS later
|
||||
const response = await fetch(this.url)
|
||||
const blob = await response.blob()
|
||||
const data = window.URL.createObjectURL(blob)
|
||||
|
||||
this.loading = false
|
||||
|
||||
// Create temporary anchor element to trigger the download
|
||||
const a = document.createElement('a')
|
||||
a.style = 'display: none'
|
||||
a.href = data
|
||||
a.target = '_blank'
|
||||
a.download = this.filename
|
||||
document.body.appendChild(a)
|
||||
a.onclick = (e) => {
|
||||
// prevent modal/whatever closing
|
||||
e.stopPropagation()
|
||||
}
|
||||
a.click()
|
||||
|
||||
setTimeout(function () {
|
||||
// Remove the element
|
||||
document.body.removeChild(a)
|
||||
// Release resource on after triggering the download
|
||||
window.URL.revokeObjectURL(data)
|
||||
}, 500)
|
||||
},
|
||||
onClick(event) {
|
||||
if (this.downloadXHR) {
|
||||
event.preventDefault()
|
||||
this.download().catch((error) => {
|
||||
// In any case, to be sure the loading animation will not last
|
||||
this.loading = false
|
||||
if (this.onError) {
|
||||
this.onError(error)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -141,7 +141,7 @@ export default {
|
|||
},
|
||||
trashDuration() {
|
||||
const hours = this.$env.HOURS_UNTIL_TRASH_PERMANENTLY_DELETED
|
||||
return moment().subtract(hours, 'hours').fromNow().replace('ago', '')
|
||||
return moment().subtract(hours, 'hours').fromNow(true)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -51,6 +51,15 @@ export default function CoreModule(options) {
|
|||
key: 'INITIAL_TABLE_DATA_LIMIT',
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
// Set to `1` to force download links to download files via XHR query
|
||||
// to bypass `Content-Disposition: inline` that can't be overridden
|
||||
// in another way.
|
||||
// If your files are stored under another origin, you also
|
||||
// must add CORS headers to your server.
|
||||
key: 'DOWNLOAD_FILE_VIA_XHR',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
// If you change this default please also update the default for the
|
||||
// backend found in src/baserow/config/settings/base.py:321
|
||||
|
|
|
@ -12,6 +12,7 @@ import Error from '@baserow/modules/core/components/Error'
|
|||
import SwitchInput from '@baserow/modules/core/components/SwitchInput'
|
||||
import Copied from '@baserow/modules/core/components/Copied'
|
||||
import MarkdownIt from '@baserow/modules/core/components/MarkdownIt'
|
||||
import DownloadLink from '@baserow/modules/core/components/DownloadLink'
|
||||
|
||||
import lowercase from '@baserow/modules/core/filters/lowercase'
|
||||
import uppercase from '@baserow/modules/core/filters/uppercase'
|
||||
|
@ -37,6 +38,7 @@ Vue.component('Error', Error)
|
|||
Vue.component('SwitchInput', SwitchInput)
|
||||
Vue.component('Copied', Copied)
|
||||
Vue.component('MarkdownIt', MarkdownIt)
|
||||
Vue.component('DownloadLink', DownloadLink)
|
||||
|
||||
Vue.filter('lowercase', lowercase)
|
||||
Vue.filter('uppercase', uppercase)
|
||||
|
|
|
@ -25,16 +25,18 @@
|
|||
>
|
||||
{{ $t('exportTableLoadingBar.export') }}
|
||||
</button>
|
||||
<a
|
||||
<DownloadLink
|
||||
v-else
|
||||
class="
|
||||
button button--large button--success
|
||||
export-table-modal__export-button
|
||||
"
|
||||
:href="`${job.url}?dl=${filename}`"
|
||||
:url="job.url"
|
||||
:filename="filename"
|
||||
:loading-class="'button--loading'"
|
||||
>
|
||||
{{ $t('exportTableLoadingBar.download') }}
|
||||
</a>
|
||||
</DownloadLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -86,12 +86,13 @@
|
|||
</li>
|
||||
</ul>
|
||||
<ul v-if="preview" class="file-field-modal__actions">
|
||||
<a
|
||||
:href="preview.url + `?dl=${preview.visible_name}`"
|
||||
<DownloadLink
|
||||
class="file-field-modal__action"
|
||||
>
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
:url="preview.url"
|
||||
:filename="preview.visible_name"
|
||||
:loading-class="'file-field-modal__action--loading'"
|
||||
><i class="fas fa-download"></i
|
||||
></DownloadLink>
|
||||
<a
|
||||
v-if="!readOnly"
|
||||
class="file-field-modal__action"
|
||||
|
|
Loading…
Add table
Reference in a new issue