1
0
Fork 0
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:
Jrmi 2022-01-20 15:50:31 +00:00
parent 7b47b5a470
commit 921e3dfc08
12 changed files with 178 additions and 34 deletions
backend
requirements
src/baserow/config/settings
changelog.md
deploy/heroku
docs/getting-started
web-frontend/modules
core
database/components

View file

@ -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

View file

@ -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))),

View file

@ -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)

View file

@ -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")

View file

@ -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.

View file

@ -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;
}
}
}

View 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>

View file

@ -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: {

View file

@ -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

View file

@ -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)

View file

@ -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>

View file

@ -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"