Merge branch 'master' of github.com:Budibase/budibase into fix/cypress-fixes

This commit is contained in:
mike12345567 2021-06-07 13:22:27 +01:00
commit 1e9ce62261
69 changed files with 1059 additions and 273 deletions

View File

@ -30,9 +30,6 @@
<a href="https://github.com/Budibase/budibase/releases"> <a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase"> <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase">
</a> </a>
<a href="https://discord.gg/rCYayfe">
<img alt="Discord" src="https://img.shields.io/discord/733030666647765003">
</a>
<a href="https://twitter.com/intent/follow?screen_name=budibase"> <a href="https://twitter.com/intent/follow?screen_name=budibase">
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" /> <img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" />
</a> </a>
@ -52,8 +49,6 @@
<a href="https://github.com/Budibase/budibase/issues">Report a bug</a> <a href="https://github.com/Budibase/budibase/issues">Report a bug</a>
<span> · </span> <span> · </span>
Support: <a href="https://github.com/Budibase/budibase/discussions">Discussions</a> Support: <a href="https://github.com/Budibase/budibase/discussions">Discussions</a>
<span> & </span>
<a href="https://discord.gg/rCYayfe">Discord</a>
</h3> </h3>
@ -61,7 +56,7 @@
- **Build and ship real software.** Unlike other platforms, with Budibase you build and ship single page applications. Budibase applications have performance baked in and can be designed responsively, providing your users with a great experience. - **Build and ship real software.** Unlike other platforms, with Budibase you build and ship single page applications. Budibase applications have performance baked in and can be designed responsively, providing your users with a great experience.
- **Open source and extensable.** Budibase is open-source - licensed as GPL v3. This should fill you with confidence that Budibase will always be around. You can also code against Budibase or fork it and make changes as you please, providing a developer-friendly experience. - **Open source and extensible.** Budibase is open-source - licensed as GPL v3. This should fill you with confidence that Budibase will always be around. You can also code against Budibase or fork it and make changes as you please, providing a developer-friendly experience.
- **Load data or start from scratch.** Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, mySQL, Airtable, S3, DyanmoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). - **Load data or start from scratch.** Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, mySQL, Airtable, S3, DyanmoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
@ -79,28 +74,6 @@
--- ---
## ⌛ Status
- [x] Alpha: We are demoing Budibase to users and receiving feedback
- [x] Private Beta: We are testing Budibase with a closed set of customers
- [x] Public Beta: Anyone can [get started](https://docs.budibase.com/getting-started).
- [ ] Official Launch
Watch "releases" of this repo to get notified of major updates, and give the star button a click whilst you're there.
<p align="center">
<img src="https://i.imgur.com/cJpgqm8.png">
</p>
### Stargazers over time
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
---
## 🏁 Getting Started with Budibase in 5 minutes ## 🏁 Getting Started with Budibase in 5 minutes
To get started, you must have docker and docker compose installed on your machine. To get started, you must have docker and docker compose installed on your machine.
@ -141,21 +114,40 @@ Done! You are now ready to build powerful internal tools in minutes. For additio
The Budibase [documentation lives here](https://docs.budibase.com). The Budibase [documentation lives here](https://docs.budibase.com).
You can also follow a quick tutorial on [how to build a CRM with Budibase](https://docs.budibase.com/tutorial/tutorial-introduction)
---
## ⌛ Status
- [x] Alpha: We are demoing Budibase to users and receiving feedback
- [x] Private Beta: We are testing Budibase with a closed set of customers
- [x] Public Beta: Anyone can [get started](https://docs.budibase.com/getting-started).
- [ ] Official Launch
Watch "releases" of this repo to get notified of major updates, and give the star button a click whilst you're there.
<p align="center">
<img src="https://i.imgur.com/cJpgqm8.png">
</p>
---
## Stargazers over time
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
--- ---
## Roadmap
Checkout our [Public Roadmap](https://github.com/Budibase/budibase/projects/10). If you would like to discuss some of the items on the roadmap, please feel to reach out on [Discord](https://discord.gg/rCYayfe), or via [Github discussions](https://github.com/Budibase/budibase/discussions)
## ❗ Code of Conduct ## ❗ Code of Conduct
Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it. Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it.
---
## 🙌 Contributing to Budibase ## 🙌 Contributing to Budibase
From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain. From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain.
@ -188,11 +180,7 @@ Budibase is open-source. The builder is licensed [AGPL v3](https://www.gnu.org/l
## 💬 Get in touch ## 💬 Get in touch
If you have a question or would like to talk with other Budibase users, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions) or join our Discord server: If you have a question or would like to talk with other Budibase users and join our community, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions)
[Discord chatroom](https://discord.gg/rCYayfe)
![Discord Shield](https://discordapp.com/api/guilds/733030666647765003/widget.png?style=shield)
--- ---

View File

@ -24,6 +24,8 @@ services:
ENABLE_ANALYTICS: "true" ENABLE_ANALYTICS: "true"
REDIS_URL: redis-service:6379 REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD} REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- ./logs:/logs
depends_on: depends_on:
- worker-service - worker-service
@ -46,6 +48,8 @@ services:
INTERNAL_API_KEY: ${INTERNAL_API_KEY} INTERNAL_API_KEY: ${INTERNAL_API_KEY}
REDIS_URL: redis-service:6379 REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD} REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- ./logs:/logs
depends_on: depends_on:
- redis-service - redis-service
- minio-service - minio-service
@ -109,6 +113,21 @@ services:
- "${REDIS_PORT}:6379" - "${REDIS_PORT}:6379"
volumes: volumes:
- redis_data:/data - redis_data:/data
watchtower-service:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --debug --http-api-update budibase/apps budibase/worker
environment:
- WATCHTOWER_HTTP_API=true
- WATCHTOWER_HTTP_API_TOKEN=budibase
- WATCHTOWER_CLEANUP=true
labels:
- "com.centurylinklabs.watchtower.enable=false"
ports:
- 6161:8080
volumes: volumes:
couchdb3_data: couchdb3_data:

View File

@ -133,4 +133,3 @@ static_resources:
socket_address: socket_address:
address: {{ address }} address: {{ address }}
port_value: 4002 port_value: 4002

View File

@ -21,6 +21,10 @@ static_resources:
cluster: app-service cluster: app-service
prefix_rewrite: "/" prefix_rewrite: "/"
- match: { path: "/v1/update" }
route:
cluster: watchtower-service
- match: { prefix: "/builder/" } - match: { prefix: "/builder/" }
route: route:
cluster: app-service cluster: app-service
@ -123,3 +127,17 @@ static_resources:
address: couchdb-service address: couchdb-service
port_value: 5984 port_value: 5984
- name: watchtower-service
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
load_assignment:
cluster_name: watchtower-service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: watchtower-service
port_value: 6161

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.21", "version": "0.9.27",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.21", "version": "0.9.27",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -2,7 +2,7 @@ const passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy const JwtStrategy = require("passport-jwt").Strategy
const { StaticDatabases } = require("./db/utils") const { StaticDatabases } = require("./db/utils")
const { jwt, local, authenticated, google } = require("./middleware") const { jwt, local, authenticated, google, auditLog } = require("./middleware")
const { setDB, getDB } = require("./db") const { setDB, getDB } = require("./db")
// Strategies // Strategies
@ -45,6 +45,7 @@ module.exports = {
passport, passport,
google, google,
jwt: require("jsonwebtoken"), jwt: require("jsonwebtoken"),
auditLog,
}, },
StaticDatabases, StaticDatabases,
constants: require("./constants"), constants: require("./constants"),

View File

@ -0,0 +1,4 @@
module.exports = async (ctx, next) => {
// Placeholder for audit log middleware
return next()
}

View File

@ -2,10 +2,12 @@ const jwt = require("./passport/jwt")
const local = require("./passport/local") const local = require("./passport/local")
const google = require("./passport/google") const google = require("./passport/google")
const authenticated = require("./authenticated") const authenticated = require("./authenticated")
const auditLog = require("./auditLog")
module.exports = { module.exports = {
google, google,
jwt, jwt,
local, local,
authenticated, authenticated,
auditLog,
} }

View File

@ -159,7 +159,7 @@ exports.upload = async ({
* Similar to the upload function but can be used to send a file stream * Similar to the upload function but can be used to send a file stream
* through to the object store. * through to the object store.
*/ */
exports.streamUpload = async (bucketName, filename, stream) => { exports.streamUpload = async (bucketName, filename, stream, extra = {}) => {
const objectStore = exports.ObjectStore(bucketName) const objectStore = exports.ObjectStore(bucketName)
await exports.makeSureBucketExists(objectStore, bucketName) await exports.makeSureBucketExists(objectStore, bucketName)
@ -167,6 +167,7 @@ exports.streamUpload = async (bucketName, filename, stream) => {
Bucket: sanitizeBucket(bucketName), Bucket: sanitizeBucket(bucketName),
Key: sanitizeKey(filename), Key: sanitizeKey(filename),
Body: stream, Body: stream,
...extra,
} }
return objectStore.upload(params).promise() return objectStore.upload(params).promise()
} }

View File

@ -13,7 +13,6 @@ const BUILTIN_IDS = {
POWER: "POWER", POWER: "POWER",
BASIC: "BASIC", BASIC: "BASIC",
PUBLIC: "PUBLIC", PUBLIC: "PUBLIC",
BUILDER: "BUILDER",
} }
// exclude internal roles like builder // exclude internal roles like builder

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "0.9.21", "version": "0.9.27",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -37,3 +37,9 @@
{/each} {/each}
{/if} {/if}
</div> </div>
<style>
.spectrum-Radio-input {
opacity: 0;
}
</style>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.21", "version": "0.9.27",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.21", "@budibase/bbui": "^0.9.27",
"@budibase/client": "^0.9.21", "@budibase/client": "^0.9.27",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -136,7 +136,7 @@ const getContextBindings = (asset, componentId) => {
if (!datasource) { if (!datasource) {
return return
} }
const info = getSchemaForDatasource(datasource) const info = getSchemaForDatasource(asset, datasource)
schema = info.schema schema = info.schema
readablePrefix = info.table?.name readablePrefix = info.table?.name
} }
@ -191,7 +191,7 @@ const getContextBindings = (asset, componentId) => {
*/ */
const getUserBindings = () => { const getUserBindings = () => {
let bindings = [] let bindings = []
const { schema } = getSchemaForDatasource({ const { schema } = getSchemaForDatasource(null, {
type: "table", type: "table",
tableId: TableNames.USERS, tableId: TableNames.USERS,
}) })
@ -244,11 +244,15 @@ const getUrlBindings = asset => {
/** /**
* Gets a schema for a datasource object. * Gets a schema for a datasource object.
*/ */
export const getSchemaForDatasource = (datasource, isForm = false) => { export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
let schema, table let schema, table
if (datasource) { if (datasource) {
const { type } = datasource const { type } = datasource
if (type === "query") { if (type === "provider") {
const component = findComponent(asset.props, datasource.providerId)
const source = getDatasourceForProvider(asset, component)
return getSchemaForDatasource(asset, source, isForm)
} else if (type === "query") {
const queries = get(queriesStores).list const queries = get(queriesStores).list
table = queries.find(query => query._id === datasource._id) table = queries.find(query => query._id === datasource._id)
} else { } else {

View File

@ -174,7 +174,7 @@ const fieldTypeToComponentMap = {
} }
export function makeDatasourceFormComponents(datasource) { export function makeDatasourceFormComponents(datasource) {
const { schema } = getSchemaForDatasource(datasource, true) const { schema } = getSchemaForDatasource(null, datasource, true)
let components = [] let components = []
let fields = Object.keys(schema || {}) let fields = Object.keys(schema || {})
fields.forEach(field => { fields.forEach(field => {

View File

@ -43,6 +43,7 @@
} }
let originalName = field.name let originalName = field.name
const linkEditDisabled = originalName != null
let primaryDisplay = let primaryDisplay =
$tables.selected.primaryDisplay == null || $tables.selected.primaryDisplay == null ||
$tables.selected.primaryDisplay === field.name $tables.selected.primaryDisplay === field.name
@ -198,7 +199,7 @@
<Input <Input
label="Name" label="Name"
bind:value={field.name} bind:value={field.name}
disabled={uneditable || field.type === LINK_TYPE} disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
/> />
<Select <Select
@ -285,6 +286,7 @@
{:else if field.type === "link"} {:else if field.type === "link"}
<Select <Select
label="Table" label="Table"
disabled={linkEditDisabled}
bind:value={field.tableId} bind:value={field.tableId}
options={tableOptions} options={tableOptions}
getOptionLabel={table => table.name} getOptionLabel={table => table.name}
@ -292,7 +294,7 @@
/> />
{#if relationshipOptions && relationshipOptions.length > 0} {#if relationshipOptions && relationshipOptions.length > 0}
<RadioGroup <RadioGroup
disabled={originalName != null} disabled={linkEditDisabled}
label="Define the relationship" label="Define the relationship"
bind:value={field.relationshipType} bind:value={field.relationshipType}
options={relationshipOptions} options={relationshipOptions}
@ -300,7 +302,11 @@
getOptionValue={option => option.value} getOptionValue={option => option.value}
/> />
{/if} {/if}
<Input label={`Column name in other table`} bind:value={field.fieldName} /> <Input
disabled={linkEditDisabled}
label={`Column name in other table`}
bind:value={field.fieldName}
/>
{:else if field.type === FORMULA_TYPE} {:else if field.type === FORMULA_TYPE}
<ModalBindableInput <ModalBindableInput
title="Handlebars Formula" title="Handlebars Formula"

View File

@ -18,7 +18,8 @@
"longformfield", "longformfield",
"datetimefield", "datetimefield",
"attachmentfield", "attachmentfield",
"relationshipfield" "relationshipfield",
"daterangepicker"
] ]
}, },
{ {

View File

@ -1,5 +1,8 @@
<script> <script>
import { getBindableProperties } from "builderStore/dataBinding" import {
getBindableProperties,
getDataProviderComponents,
} from "builderStore/dataBinding"
import { import {
Button, Button,
Popover, Popover,
@ -61,6 +64,17 @@
$currentAsset, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: dataProviders = getDataProviderComponents(
$currentAsset,
$store.selectedComponentId
).map(provider => ({
label: provider._instanceName,
name: provider._instanceName,
providerId: provider._id,
value: `{{ literal [${provider._id}] }}`,
type: "provider",
schema: provider.schema,
}))
$: queryBindableProperties = bindableProperties.map(property => ({ $: queryBindableProperties = bindableProperties.map(property => ({
...property, ...property,
category: property.type === "instance" ? "Component" : "Table", category: property.type === "instance" ? "Component" : "Table",
@ -182,7 +196,20 @@
</li> </li>
{/each} {/each}
</ul> </ul>
<Divider size="S" />
<div class="title">
<Heading size="XS">Data Providers</Heading>
</div>
<ul>
{#each dataProviders as provider}
<li
class:selected={value === provider}
on:click={() => handleSelected(provider)}
>
{provider.label}
</li>
{/each}
</ul>
{#if otherSources?.length} {#if otherSources?.length}
<Divider size="S" /> <Divider size="S" />
<div class="title"> <div class="title">

View File

@ -14,11 +14,11 @@
$currentAsset, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: schemaFields = getSchemaFields(parameters?.tableId) $: schemaFields = getSchemaFields($currentAsset, parameters?.tableId)
$: tableOptions = $tables.list || [] $: tableOptions = $tables.list || []
const getSchemaFields = tableId => { const getSchemaFields = (asset, tableId) => {
const { schema } = getSchemaForDatasource({ type: "table", tableId }) const { schema } = getSchemaForDatasource(asset, { type: "table", tableId })
return Object.values(schema || {}) return Object.values(schema || {})
} }

View File

@ -5,11 +5,13 @@ import ExecuteQuery from "./ExecuteQuery.svelte"
import TriggerAutomation from "./TriggerAutomation.svelte" import TriggerAutomation from "./TriggerAutomation.svelte"
import ValidateForm from "./ValidateForm.svelte" import ValidateForm from "./ValidateForm.svelte"
// defines what actions are available, when adding a new one // Defines which actions are available to configure in the front end.
// the component is the setup panel for the action // Unfortunately the "name" property is used as the identifier so please don't
// NOTE that the "name" is used by the client library, // change them.
// so if you want to change it, you must change it client lib too // The client library removes any spaces when processing actions, so they can
// be considered as camel case too.
// There is technical debt here to sanitize all these and standardise them
// across the packages but it's a breaking change to existing apps.
export default [ export default [
{ {
name: "Save Row", name: "Save Row",

View File

@ -14,7 +14,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(datasource).schema $: schema = getSchemaForDatasource($currentAsset, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidValue(value, options) $: boundValue = getValidValue(value, options)

View File

@ -27,19 +27,16 @@
? tempValue.length ? tempValue.length
: Object.keys(tempValue || {}).length : Object.keys(tempValue || {}).length
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance) $: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(dataSource)?.schema $: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
$: schemaFields = Object.values(schema || {}) $: schemaFields = Object.values(schema || {})
$: internalTable = dataSource?.type === "table" $: internalTable = dataSource?.type === "table"
// Reset value if value is wrong type for the datasource. // Reset value if value is wrong type for the datasource.
// Lucene editor needs an array, and simple editor needs an object. // Lucene editor needs an array, and simple editor needs an object.
$: { $: {
if (internalTable && !Array.isArray(value)) { if (!Array.isArray(value)) {
tempValue = [] tempValue = []
dispatch("change", []) dispatch("change", [])
} else if (!internalTable && Array.isArray(value)) {
tempValue = {}
dispatch("change", {})
} }
} }
@ -63,28 +60,7 @@
constaints. constaints.
{/if} {/if}
</Body> </Body>
{#if internalTable} <LuceneFilterBuilder bind:value={tempValue} {schemaFields} />
<LuceneFilterBuilder bind:value={tempValue} {schemaFields} />
{:else}
<div class="fields">
<SaveFields
parameterFields={Array.isArray(value) ? {} : value}
{schemaFields}
valueLabel="Equals"
on:change={e => (tempValue = e.detail)}
/>
</div>
{/if}
</Layout> </Layout>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
<style>
.fields {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
align-items: center;
grid-template-columns: auto 1fr auto 1fr auto;
}
</style>

View File

@ -18,7 +18,7 @@
component => component._component === "@budibase/standard-components/form" component => component._component === "@budibase/standard-components/form"
) )
$: datasource = getDatasourceForProvider($currentAsset, form) $: datasource = getDatasourceForProvider($currentAsset, form)
$: schema = getSchemaForDatasource(datasource, true).schema $: schema = getSchemaForDatasource($currentAsset, datasource, true).schema
$: options = getOptions(schema, type) $: options = getOptions(schema, type)
const getOptions = (schema, fieldType) => { const getOptions = (schema, fieldType) => {

View File

@ -14,7 +14,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource(datasource).schema $: schema = getSchemaForDatasource($currentAsset, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidOptions(value, options) $: boundValue = getValidOptions(value, options)

View File

@ -43,6 +43,10 @@
title: "Theming", title: "Theming",
href: "/builder/portal/settings/theming", href: "/builder/portal/settings/theming",
}, },
{
title: "Updates",
href: "/builder/portal/settings/update",
},
]) ])
} else { } else {
menu = menu.concat([ menu = menu.concat([

View File

@ -12,7 +12,7 @@
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { auth, organisation } from "stores/portal" import { auth, organisation } from "stores/portal"
import { post } from "builderStore/api" import { post, get } from "builderStore/api"
import analytics from "analytics" import analytics from "analytics"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
@ -130,10 +130,10 @@
<Toggle text="" bind:value={$values.analytics} /> <Toggle text="" bind:value={$values.analytics} />
</div> </div>
</div> </div>
<div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div>
</Layout> </Layout>
<div>
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
</div>
</Layout> </Layout>
{/if} {/if}

View File

@ -0,0 +1,70 @@
<script>
import {
Layout,
Heading,
Body,
Button,
Divider,
Label,
Input,
Toggle,
Dropzone,
notifications,
} from "@budibase/bbui"
import { auth, organisation } from "stores/portal"
import { post, get } from "builderStore/api"
import analytics from "analytics"
import { writable } from "svelte/store"
import { redirect } from "@roxi/routify"
// Only admins allowed here
$: {
if (!$auth.isAdmin) {
$redirect("../../portal")
}
}
async function updateBudibase() {
try {
notifications.info("Updating budibase..")
const response = await fetch("/v1/update", {
headers: {
Authorization: "Bearer budibase",
},
})
notifications.success("Your budibase installation is up to date.")
} catch (err) {
notifications.error(`Error installing budibase update ${err}`)
}
}
</script>
{#if $auth.isAdmin}
<Layout>
<Layout gap="XS" noPadding>
<Heading size="M">Update</Heading>
<Body>
Keep your budibase installation up to date to take advantage of the
latest features, security updates and much more.
</Body>
</Layout>
<Divider size="S" />
<div class="fields">
<div class="field">
<Button cta on:click={updateBudibase}>Check For Updates</Button>
</div>
</div>
</Layout>
{/if}
<style>
.fields {
display: grid;
grid-gap: var(--spacing-m);
}
.field {
display: grid;
grid-template-columns: 33% 1fr;
align-items: center;
}
</style>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.21", "version": "0.9.27",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -18,13 +18,13 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@budibase/standard-components": "^0.9.21", "@budibase/standard-components": "^0.9.27",
"@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-node-resolve": "^11.2.1",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",

View File

@ -55,13 +55,13 @@
} }
// Enriches any string component props using handlebars // Enriches any string component props using handlebars
const updateComponentProps = async (definition, context) => { const updateComponentProps = (definition, context) => {
// Record the timestamp so we can reference it after enrichment // Record the timestamp so we can reference it after enrichment
latestUpdateTime = Date.now() latestUpdateTime = Date.now()
const enrichmentTime = latestUpdateTime const enrichmentTime = latestUpdateTime
// Enrich props with context // Enrich props with context
const enrichedProps = await enrichProps(definition, context) const enrichedProps = enrichProps(definition, context)
// Abandon this update if a newer update has started // Abandon this update if a newer update has started
if (enrichmentTime !== latestUpdateTime) { if (enrichmentTime !== latestUpdateTime) {

View File

@ -5,4 +5,5 @@ export const TableNames = {
export const ActionTypes = { export const ActionTypes = {
ValidateForm: "ValidateForm", ValidateForm: "ValidateForm",
RefreshDatasource: "RefreshDatasource", RefreshDatasource: "RefreshDatasource",
SetDataProviderQuery: "SetDataProviderQuery",
} }

View File

@ -9,6 +9,7 @@ import {
import { styleable } from "./utils/styleable" import { styleable } from "./utils/styleable"
import transition from "./utils/transition" import transition from "./utils/transition"
import { linkable } from "./utils/linkable" import { linkable } from "./utils/linkable"
import { getAction } from "./utils/getAction"
import Provider from "./components/Provider.svelte" import Provider from "./components/Provider.svelte"
import { ActionTypes } from "./constants" import { ActionTypes } from "./constants"
@ -22,6 +23,7 @@ export default {
styleable, styleable,
transition, transition,
linkable, linkable,
getAction,
Provider, Provider,
ActionTypes, ActionTypes,
} }

View File

@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => {
* Enriches component props. * Enriches component props.
* Data bindings are enriched, and button actions are enriched. * Data bindings are enriched, and button actions are enriched.
*/ */
export const enrichProps = async (props, context) => { export const enrichProps = (props, context) => {
// Exclude all private props that start with an underscore // Exclude all private props that start with an underscore
let validProps = {} let validProps = {}
Object.entries(props) Object.entries(props)
@ -41,7 +41,7 @@ export const enrichProps = async (props, context) => {
} }
// Enrich all data bindings in top level props // Enrich all data bindings in top level props
let enrichedProps = await enrichDataBindings(validProps, totalContext) let enrichedProps = enrichDataBindings(validProps, totalContext)
// Enrich click actions if they exist // Enrich click actions if they exist
if (enrichedProps.onClick) { if (enrichedProps.onClick) {

View File

@ -1,5 +1,5 @@
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { processString, processObject } from "@budibase/string-templates" import { processString, processObjectSync } from "@budibase/string-templates"
// Regex to test inputs with to see if they are likely candidates for template strings // Regex to test inputs with to see if they are likely candidates for template strings
const looksLikeTemplate = /{{.*}}/ const looksLikeTemplate = /{{.*}}/
@ -23,6 +23,6 @@ export const enrichDataBinding = async (input, context) => {
* Recursively enriches all props in a props object and returns the new props. * Recursively enriches all props in a props object and returns the new props.
* Props are deeply cloned so that no mutation is done to the source object. * Props are deeply cloned so that no mutation is done to the source object.
*/ */
export const enrichDataBindings = async (props, context) => { export const enrichDataBindings = (props, context) => {
return await processObject(cloneDeep(props), context) return processObjectSync(cloneDeep(props), context)
} }

View File

@ -0,0 +1,19 @@
import { get } from "svelte/store"
import { getContext } from "svelte"
/**
* Gets a component action.
* @param id The component ID that provides the action
* @param type The action type to get
* @returns {null|*} The action function
*/
export const getAction = (id, type) => {
if (!id || !type) {
return null
}
const context = getContext("context")
if (!context) {
return null
}
return get(context)?.[`${id}_${type}`]
}

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/electron.js", "main": "src/electron.js",
"repository": { "repository": {
@ -55,9 +55,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.21", "@budibase/auth": "^0.9.27",
"@budibase/client": "^0.9.21", "@budibase/client": "^0.9.27",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",
@ -109,7 +109,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4", "@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.21", "@budibase/standard-components": "^0.9.27",
"@jest/test-sequencer": "^24.8.0", "@jest/test-sequencer": "^24.8.0",
"babel-jest": "^27.0.2", "babel-jest": "^27.0.2",
"docker-compose": "^0.23.6", "docker-compose": "^0.23.6",

View File

@ -99,12 +99,18 @@ async function createInstance(template) {
// replicate the template data to the instance DB // replicate the template data to the instance DB
// this is currently very hard to test, downloading and importing template files // this is currently very hard to test, downloading and importing template files
/* istanbul ignore next */ /* istanbul ignore next */
let _rev
if (template && template.useTemplate === "true") { if (template && template.useTemplate === "true") {
const { ok } = await db.load(await getTemplateStream(template)) const { ok } = await db.load(await getTemplateStream(template))
if (!ok) { if (!ok) {
throw "Error loading database dump from template." throw "Error loading database dump from template."
} }
var { _rev } = await db.get(DocumentTypes.APP_METADATA) try {
const response = await db.get(DocumentTypes.APP_METADATA)
_rev = response._rev
} catch (err) {
_rev = null
}
} else { } else {
// create the users table // create the users table
await db.put(USERS_TABLE_SCHEMA) await db.put(USERS_TABLE_SCHEMA)

View File

@ -5,7 +5,7 @@ const { getFullUser } = require("../../utilities/users")
exports.fetchSelf = async ctx => { exports.fetchSelf = async ctx => {
const appId = ctx.appId const appId = ctx.appId
const { userId } = ctx.user let userId = ctx.user.userId || ctx.user._id
/* istanbul ignore next */ /* istanbul ignore next */
if (!userId) { if (!userId) {
ctx.body = {} ctx.body = {}

View File

@ -63,10 +63,6 @@ exports.fetch = async ctx => {
exports.clientFetch = async ctx => { exports.clientFetch = async ctx => {
const routing = await getRoutingStructure(ctx.appId) const routing = await getRoutingStructure(ctx.appId)
let roleId = ctx.user.role._id let roleId = ctx.user.role._id
// builder is a special case, always return the full routing structure
if (roleId === BUILTIN_ROLE_IDS.BUILDER) {
roleId = BUILTIN_ROLE_IDS.ADMIN
}
const roleIds = await getUserRoleHierarchy(ctx.appId, roleId) const roleIds = await getUserRoleHierarchy(ctx.appId, roleId)
for (let topLevel of Object.values(routing.routes)) { for (let topLevel of Object.values(routing.routes)) {
for (let subpathKey of Object.keys(topLevel.subpaths)) { for (let subpathKey of Object.keys(topLevel.subpaths)) {

View File

@ -31,7 +31,6 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
*, *,
*:before, *:before,
*:after { *:after {
@ -41,9 +40,9 @@
</svelte:head> </svelte:head>
<body id="app"> <body id="app">
<script src={clientLibPath}> <script type="application/javascript" src={clientLibPath}>
</script> </script>
<script> <script type="application/javascript">
loadBudibase() loadBudibase()
</script> </script>
</body> </body>

View File

@ -4,7 +4,6 @@ const {
getUserMetadataParams, getUserMetadataParams,
} = require("../../db/utils") } = require("../../db/utils")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { const {
getGlobalUsers, getGlobalUsers,
addAppRoleToUser, addAppRoleToUser,
@ -47,10 +46,6 @@ exports.fetchMetadata = async function (ctx) {
exports.updateSelfMetadata = async function (ctx) { exports.updateSelfMetadata = async function (ctx) {
// overwrite the ID with current users // overwrite the ID with current users
ctx.request.body._id = ctx.user._id ctx.request.body._id = ctx.user._id
if (ctx.user.builder && ctx.user.builder.global) {
// specific case, update self role in global user
await addAppRoleToUser(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN)
}
// make sure no stale rev // make sure no stale rev
delete ctx.request.body._rev delete ctx.request.body._rev
await exports.updateMetadata(ctx) await exports.updateMetadata(ctx)

View File

@ -1,5 +1,5 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const { buildAuthMiddleware } = require("@budibase/auth").auth const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth
const currentApp = require("../middleware/currentapp") const currentApp = require("../middleware/currentapp")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
@ -37,6 +37,7 @@ router
}) })
) )
.use(currentApp) .use(currentApp)
.use(auditLog)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

View File

@ -28,9 +28,7 @@ describe("/routing", () => {
it("returns the correct routing for basic user", async () => { it("returns the correct routing for basic user", async () => {
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => { workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
return { return {
roles: { roleId: BUILTIN_ROLE_IDS.BASIC,
[appId]: BUILTIN_ROLE_IDS.BASIC,
}
} }
}) })
const res = await request const res = await request

View File

@ -2,6 +2,7 @@ const rowController = require("../../../controllers/row")
const appController = require("../../../controllers/application") const appController = require("../../../controllers/application")
const CouchDB = require("../../../../db") const CouchDB = require("../../../../db")
const { AppStatus } = require("../../../../db/utils") const { AppStatus } = require("../../../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
function Request(appId, params) { function Request(appId, params) {
this.appId = appId this.appId = appId
@ -77,11 +78,17 @@ exports.checkPermissionsEndpoint = async ({
.set(passHeader) .set(passHeader)
.expect(200) .expect(200)
user = await config.createUser("fail@budibase.com", password, failRole) let failHeader
const failHeader = await config.login("fail@budibase.com", password, { if (failRole === BUILTIN_ROLE_IDS.PUBLIC) {
roleId: failRole, failHeader = config.publicHeaders()
userId: user.globalId, } else {
}) user = await config.createUser("fail@budibase.com", password, failRole)
failHeader = await config.login("fail@budibase.com", password, {
roleId: failRole,
userId: user.globalId,
builder: false,
})
}
await exports await exports
.createRequest(config.request, method, url, body) .createRequest(config.request, method, url, body)

View File

@ -5,7 +5,7 @@ require("@budibase/auth").init(CouchDB)
const Koa = require("koa") const Koa = require("koa")
const destroyable = require("server-destroy") const destroyable = require("server-destroy")
const koaBody = require("koa-body") const koaBody = require("koa-body")
const logger = require("koa-pino-logger") const pino = require("koa-pino-logger")
const http = require("http") const http = require("http")
const api = require("./api") const api = require("./api")
const eventEmitter = require("./events") const eventEmitter = require("./events")
@ -29,7 +29,7 @@ app.use(
) )
app.use( app.use(
logger({ pino({
prettyPrint: { prettyPrint: {
levelFirst: true, levelFirst: true,
}, },

View File

@ -4,6 +4,7 @@ const createRow = require("./steps/createRow")
const updateRow = require("./steps/updateRow") const updateRow = require("./steps/updateRow")
const deleteRow = require("./steps/deleteRow") const deleteRow = require("./steps/deleteRow")
const executeScript = require("./steps/executeScript") const executeScript = require("./steps/executeScript")
const bash = require("./steps/bash")
const executeQuery = require("./steps/executeQuery") const executeQuery = require("./steps/executeQuery")
const outgoingWebhook = require("./steps/outgoingWebhook") const outgoingWebhook = require("./steps/outgoingWebhook")
const env = require("../environment") const env = require("../environment")
@ -21,6 +22,7 @@ const BUILTIN_ACTIONS = {
DELETE_ROW: deleteRow.run, DELETE_ROW: deleteRow.run,
OUTGOING_WEBHOOK: outgoingWebhook.run, OUTGOING_WEBHOOK: outgoingWebhook.run,
EXECUTE_SCRIPT: executeScript.run, EXECUTE_SCRIPT: executeScript.run,
EXECUTE_BASH: bash.run,
EXECUTE_QUERY: executeQuery.run, EXECUTE_QUERY: executeQuery.run,
} }
const BUILTIN_DEFINITIONS = { const BUILTIN_DEFINITIONS = {
@ -32,6 +34,7 @@ const BUILTIN_DEFINITIONS = {
OUTGOING_WEBHOOK: outgoingWebhook.definition, OUTGOING_WEBHOOK: outgoingWebhook.definition,
EXECUTE_SCRIPT: executeScript.definition, EXECUTE_SCRIPT: executeScript.definition,
EXECUTE_QUERY: executeQuery.definition, EXECUTE_QUERY: executeQuery.definition,
EXECUTE_BASH: bash.definition,
} }
let MANIFEST = null let MANIFEST = null

View File

@ -0,0 +1,62 @@
const { execSync } = require("child_process")
const { processStringSync } = require("@budibase/string-templates")
module.exports.definition = {
name: "Bash Scripting",
tagline: "Execute a bash command",
icon: "ri-terminal-box-line",
description: "Run a bash script",
type: "ACTION",
stepId: "EXECUTE_BASH",
inputs: {},
schema: {
inputs: {
properties: {
code: {
type: "string",
customType: "code",
title: "Code",
},
},
required: ["code"],
},
outputs: {
properties: {
stdout: {
type: "string",
description: "Standard output of your bash command or script.",
},
},
},
required: ["stdout"],
},
}
module.exports.run = async function ({ inputs, context }) {
if (inputs.code == null) {
return {
stdout: "Budibase bash automation failed: Invalid inputs",
}
}
try {
const command = processStringSync(inputs.code, context)
let stdout
try {
stdout = execSync(command, { timeout: 500 })
} catch (err) {
stdout = err.message
}
return {
stdout,
}
} catch (err) {
console.error(err)
return {
success: false,
response: err,
}
}
}

View File

@ -1,7 +1,7 @@
const scriptController = require("../../api/controllers/script") const scriptController = require("../../api/controllers/script")
module.exports.definition = { module.exports.definition = {
name: "Scripting", name: "JS Scripting",
tagline: "Execute JavaScript Code", tagline: "Execute JavaScript Code",
icon: "ri-terminal-box-line", icon: "ri-terminal-box-line",
description: "Run a piece of JavaScript code in your automation", description: "Run a piece of JavaScript code in your automation",

View File

@ -33,7 +33,7 @@ module.exports = async (ctx, next) => {
updateCookie = true updateCookie = true
appId = requestAppId appId = requestAppId
// retrieving global user gets the right role // retrieving global user gets the right role
roleId = globalUser.roleId roleId = globalUser.roleId || BUILTIN_ROLE_IDS.PUBLIC
} else if (appCookie != null) { } else if (appCookie != null) {
appId = appCookie.appId appId = appCookie.appId
roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC

View File

@ -101,7 +101,7 @@ class TestConfiguration {
userId: GLOBAL_USER_ID, userId: GLOBAL_USER_ID,
} }
const app = { const app = {
roleId: BUILTIN_ROLE_IDS.BUILDER, roleId: BUILTIN_ROLE_IDS.ADMIN,
appId: this.appId, appId: this.appId,
} }
const authToken = jwt.sign(auth, env.JWT_SECRET) const authToken = jwt.sign(auth, env.JWT_SECRET)
@ -306,12 +306,9 @@ class TestConfiguration {
return await this._req(config, null, controllers.layout.save) return await this._req(config, null, controllers.layout.save)
} }
async createUser(roleId = BUILTIN_ROLE_IDS.POWER) { async createUser() {
const globalId = `us_${Math.random()}` const globalId = `us_${Math.random()}`
const resp = await this.globalUser( const resp = await this.globalUser(globalId)
globalId,
roleId === BUILTIN_ROLE_IDS.BUILDER
)
return { return {
...resp, ...resp,
globalId, globalId,
@ -319,7 +316,6 @@ class TestConfiguration {
} }
async login(email, password, { roleId, userId, builder } = {}) { async login(email, password, { roleId, userId, builder } = {}) {
roleId = !roleId ? BUILTIN_ROLE_IDS.BUILDER : roleId
userId = !userId ? `us_uuid1` : userId userId = !userId ? `us_uuid1` : userId
if (!this.request) { if (!this.request) {
throw "Server has not been opened, cannot login." throw "Server has not been opened, cannot login."

View File

@ -30,5 +30,7 @@ exports.uploadClientLibrary = async appId => {
const sourcepath = require.resolve("@budibase/client") const sourcepath = require.resolve("@budibase/client")
const destPath = join(appId, "budibase-client.js") const destPath = join(appId, "budibase-client.js")
await streamUpload(BUCKET_NAME, destPath, fs.createReadStream(sourcepath)) await streamUpload(BUCKET_NAME, destPath, fs.createReadStream(sourcepath), {
ContentType: "application/javascript",
})
} }

View File

@ -12,12 +12,8 @@ exports.init = async () => {
} }
exports.shutdown = async () => { exports.shutdown = async () => {
if (devAppClient != null) { if (devAppClient) await devAppClient.finish()
await devAppClient.finish() if (debounceClient) await debounceClient.finish()
}
if (debounceClient != null) {
await debounceClient.finish()
}
} }
exports.doesUserHaveLock = async (devAppId, user) => { exports.doesUserHaveLock = async (devAppId, user) => {

View File

@ -169,9 +169,14 @@ exports.inputProcessing = (user = {}, table, row) => {
let clonedRow = cloneDeep(row) let clonedRow = cloneDeep(row)
// need to copy the table so it can be differenced on way out // need to copy the table so it can be differenced on way out
const copiedTable = cloneDeep(table) const copiedTable = cloneDeep(table)
const dontCleanseKeys = ["type", "_id", "_rev", "tableId"]
for (let [key, value] of Object.entries(clonedRow)) { for (let [key, value] of Object.entries(clonedRow)) {
const field = table.schema[key] const field = table.schema[key]
// cleanse fields that aren't in the schema
if (!field) { if (!field) {
if (dontCleanseKeys.indexOf(key) === -1) {
delete clonedRow[key]
}
continue continue
} }
clonedRow[key] = exports.coerce(value, field.type) clonedRow[key] = exports.coerce(value, field.type)

View File

@ -9,19 +9,26 @@ function getAppRole(appId, user) {
if (!user.roles) { if (!user.roles) {
return user return user
} }
// always use the deployed app if (user.builder && user.builder.global) {
user.roleId = user.roles[getDeployedAppID(appId)] user.roleId = BUILTIN_ROLE_IDS.ADMIN
if (!user.roleId) { } else {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC // always use the deployed app
user.roleId = user.roles[getDeployedAppID(appId)]
if (!user.roleId) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
}
} }
delete user.roles delete user.roles
return user return user
} }
function request(ctx, request) { function request(ctx, request, noApiKey) {
if (!request.headers) { if (!request.headers) {
request.headers = {} request.headers = {}
} }
if (!noApiKey) {
request.headers["x-budibase-api-key"] = env.INTERNAL_API_KEY
}
if (request.body && Object.keys(request.body).length > 0) { if (request.body && Object.keys(request.body).length > 0) {
request.headers["Content-Type"] = "application/json" request.headers["Content-Type"] = "application/json"
request.body = request.body =
@ -44,9 +51,6 @@ exports.sendSmtpEmail = async (to, from, subject, contents) => {
checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`), checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`),
request(null, { request(null, {
method: "POST", method: "POST",
headers: {
"x-budibase-api-key": env.INTERNAL_API_KEY,
},
body: { body: {
email: to, email: to,
from, from,
@ -86,16 +90,6 @@ exports.getDeployedApps = async ctx => {
} }
} }
exports.deleteGlobalUser = async (ctx, globalId) => {
const endpoint = `/api/admin/users/${globalId}`
const reqCfg = { method: "DELETE" }
const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + endpoint),
request(ctx, reqCfg)
)
return response.json()
}
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
const endpoint = globalId const endpoint = globalId
? `/api/admin/users/${globalId}` ? `/api/admin/users/${globalId}`
@ -121,7 +115,8 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
const endpoint = `/api/admin/users/self` const endpoint = `/api/admin/users/self`
const response = await fetch( const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + endpoint), checkSlashesInUrl(env.WORKER_URL + endpoint),
request(ctx, { method: "GET" }) // we don't want to use API key when getting self
request(ctx, { method: "GET" }, true)
) )
if (response.status !== 200) { if (response.status !== 200) {
ctx.throw(400, "Unable to get self globally.") ctx.throw(400, "Unable to get self globally.")
@ -172,9 +167,6 @@ exports.removeAppFromUserRoles = async appId => {
checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`), checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`),
request(null, { request(null, {
method: "DELETE", method: "DELETE",
headers: {
"x-budibase-api-key": env.INTERNAL_API_KEY,
},
}) })
) )
if (response.status !== 200) { if (response.status !== 200) {

View File

@ -1505,5 +1505,38 @@
"context": { "context": {
"type": "schema" "type": "schema"
} }
},
"daterangepicker": {
"name": "Date Range",
"icon": "Date",
"styleable": true,
"hasChildren": false,
"info": "Your data provider will be automatically filtered to the given date range.",
"settings": [
{
"type": "dataProvider",
"label": "Provider",
"key": "dataProvider"
},
{
"type": "field",
"label": "Date field",
"key": "field"
},
{
"type": "select",
"label": "Default range",
"key": "defaultValue",
"options": [
"Last 1 day",
"Last 7 days",
"Last 30 days",
"Last 3 months",
"Last 6 months",
"Last 1 year"
],
"defaultValue": "Last 30 days"
}
]
} }
} }

View File

@ -29,14 +29,15 @@
"keywords": [ "keywords": [
"svelte" "svelte"
], ],
"version": "0.9.21", "version": "0.9.27",
"license": "MIT", "license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.21", "@budibase/bbui": "^0.9.27",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1", "apexcharts": "^3.22.1",
"dayjs": "^1.10.5",
"svelte-apexcharts": "^1.0.2", "svelte-apexcharts": "^1.0.2",
"svelte-flatpickr": "^3.1.0" "svelte-flatpickr": "^3.1.0"
} }

View File

@ -1,6 +1,12 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import { ProgressCircle, Pagination } from "@budibase/bbui" import { ProgressCircle, Pagination } from "@budibase/bbui"
import {
buildLuceneQuery,
luceneQuery,
luceneSort,
luceneLimit,
} from "./lucene"
export let dataSource export let dataSource
export let filter export let filter
@ -25,9 +31,11 @@
let schema = {} let schema = {}
let bookmarks = [null] let bookmarks = [null]
let pageNumber = 0 let pageNumber = 0
let query = null
$: query = buildLuceneQuery(filter)
$: internalTable = dataSource?.type === "table" $: internalTable = dataSource?.type === "table"
$: query = internalTable ? buildLuceneQuery(filter) : null $: nestedProvider = dataSource?.type === "provider"
$: hasNextPage = bookmarks[pageNumber + 1] != null $: hasNextPage = bookmarks[pageNumber + 1] != null
$: hasPrevPage = pageNumber > 0 $: hasPrevPage = pageNumber > 0
$: getSchema(dataSource) $: getSchema(dataSource)
@ -48,22 +56,38 @@
} }
} }
$: { $: {
// Sort and limit rows in memory when we aren't searching internal tables
if (internalTable) { if (internalTable) {
// Internal tables are already processed server-side
rows = allRows rows = allRows
} else { } else {
const sortedRows = sortRows(allRows, sortColumn, sortOrder) // For anything else we use client-side implementations to filter, sort
rows = limitRows(sortedRows, limit) // and limit
const filtered = luceneQuery(allRows, query)
const sorted = luceneSort(filtered, sortColumn, sortOrder, sortType)
rows = luceneLimit(sorted, limit)
} }
} }
$: actions = [ $: actions = [
{ {
type: ActionTypes.RefreshDatasource, type: ActionTypes.RefreshDatasource,
callback: () => fetchData(dataSource), callback: () => refresh(),
metadata: { dataSource }, metadata: { dataSource },
}, },
{
type: ActionTypes.SetDataProviderQuery,
callback: newQuery => (query = newQuery),
},
] ]
$: dataContext = { rows, schema, rowsLength: rows.length } $: dataContext = {
rows,
schema,
rowsLength: rows.length,
// Undocumented properties. These aren't supposed to be used in builder
// bindings, but are used internally by other components
id: $component?.id,
state: { query },
}
const getSortType = (schema, sortColumn) => { const getSortType = (schema, sortColumn) => {
if (!schema || !sortColumn || !schema[sortColumn]) { if (!schema || !sortColumn || !schema[sortColumn]) {
@ -73,36 +97,18 @@
return type === "number" ? "number" : "string" return type === "number" ? "number" : "string"
} }
const buildLuceneQuery = filter => { const refresh = async () => {
let query = { if (schemaLoaded && !nestedProvider) {
string: {}, fetchData(
fuzzy: {}, dataSource,
range: {}, query,
equal: {}, limit,
notEqual: {}, sortColumn,
empty: {}, sortOrder,
notEmpty: {}, sortType,
paginate
)
} }
if (Array.isArray(filter)) {
filter.forEach(({ operator, field, type, value }) => {
if (operator.startsWith("range")) {
if (!query.range[field]) {
query.range[field] = {
low: type === "number" ? Number.MIN_SAFE_INTEGER : "0000",
high: type === "number" ? Number.MAX_SAFE_INTEGER : "9999",
}
}
if (operator === "rangeLow") {
query.range[field].low = value
} else if (operator === "rangeHigh") {
query.range[field].high = value
}
} else if (query[operator]) {
query[operator][field] = value
}
})
}
return query
} }
const fetchData = async ( const fetchData = async (
@ -116,6 +122,7 @@
) => { ) => {
loading = true loading = true
if (dataSource?.type === "table") { if (dataSource?.type === "table") {
// For internal tables we use server-side processing
const res = await API.searchTable({ const res = await API.searchTable({
tableId: dataSource.tableId, tableId: dataSource.tableId,
query, query,
@ -132,55 +139,27 @@
} else { } else {
bookmarks = [null] bookmarks = [null]
} }
} else if (dataSource?.type === "provider") {
// For providers referencing another provider, just use the rows it
// provides
allRows = dataSource?.value?.rows ?? []
} else { } else {
const rows = await API.fetchDatasource(dataSource) // For other data sources like queries or views, fetch all rows from the
allRows = inMemoryFilterRows(rows, filter) // server
allRows = await API.fetchDatasource(dataSource)
} }
loading = false loading = false
loaded = true loaded = true
} }
const inMemoryFilterRows = (rows, filter) => {
let filteredData = [...rows]
Object.entries(filter || {}).forEach(([field, value]) => {
if (value != null && value !== "") {
filteredData = filteredData.filter(row => {
return row[field] === value
})
}
})
return filteredData
}
const sortRows = (rows, sortColumn, sortOrder) => {
if (!sortColumn || !sortOrder) {
return rows
}
return rows.slice().sort((a, b) => {
const colA = a[sortColumn]
const colB = b[sortColumn]
if (sortOrder === "Descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
}
const limitRows = (rows, limit) => {
const numLimit = parseFloat(limit)
if (isNaN(numLimit)) {
return rows
}
return rows.slice(0, numLimit)
}
const getSchema = async dataSource => { const getSchema = async dataSource => {
if (dataSource?.schema) { if (dataSource?.schema) {
schema = dataSource.schema schema = dataSource.schema
} else if (dataSource?.tableId) { } else if (dataSource?.tableId) {
const definition = await API.fetchTableDefinition(dataSource.tableId) const definition = await API.fetchTableDefinition(dataSource.tableId)
schema = definition?.schema ?? {} schema = definition?.schema ?? {}
} else if (dataSource?.type === "provider") {
schema = dataSource.value?.schema ?? {}
} else { } else {
schema = {} schema = {}
} }

View File

@ -0,0 +1,77 @@
<script>
import { Select } from "@budibase/bbui"
import { getContext } from "svelte"
import dayjs from "dayjs"
import utc from "dayjs/plugin/utc"
import { onMount } from "svelte"
dayjs.extend(utc)
export let dataProvider
export let field
export let defaultValue
const dataContext = getContext("context")
const component = getContext("component")
const { styleable, builderStore, ActionTypes, getAction } = getContext("sdk")
const setQuery = getAction(dataProvider?.id, ActionTypes.SetDataProviderQuery)
const options = [
"Last 1 day",
"Last 7 days",
"Last 30 days",
"Last 3 months",
"Last 6 months",
"Last 1 year",
]
let value = options.includes(defaultValue) ? defaultValue : "Last 30 days"
const updateDateRange = option => {
const query = dataProvider?.state?.query
if (!query || !setQuery) {
return
}
value = option
let low = dayjs.utc().subtract(1, "year")
let high = dayjs.utc().add(1, "day")
if (option === "Last 1 day") {
low = dayjs.utc().subtract(1, "day")
} else if (option === "Last 7 days") {
low = dayjs.utc().subtract(7, "days")
} else if (option === "Last 30 days") {
low = dayjs.utc().subtract(30, "days")
} else if (option === "Last 3 months") {
low = dayjs.utc().subtract(3, "months")
} else if (option === "Last 6 months") {
low = dayjs.utc().subtract(6, "months")
}
// Update data provider query with the new filter
setQuery({
...query,
range: {
...query.range,
[field]: {
high: high.format(),
low: low.format(),
},
},
})
}
// Update the range on mount to the initial value
onMount(() => {
updateDateRange(value)
})
</script>
<div use:styleable={$component.styles}>
<Select
placeholder={null}
{options}
{value}
on:change={e => updateDateRange(e.detail)}
/>
</div>

View File

@ -22,6 +22,7 @@
> >
{#if fieldState} {#if fieldState}
<CoreTextField <CoreTextField
updateOnChange={false}
value={$fieldState.value} value={$fieldState.value}
on:change={e => fieldApi.setValue(e.detail)} on:change={e => fieldApi.setValue(e.detail)}
disabled={$fieldState.disabled} disabled={$fieldState.disabled}

View File

@ -27,6 +27,7 @@ export { default as cardhorizontal } from "./CardHorizontal.svelte"
export { default as cardstat } from "./CardStat.svelte" export { default as cardstat } from "./CardStat.svelte"
export { default as icon } from "./Icon.svelte" export { default as icon } from "./Icon.svelte"
export { default as backgroundimage } from "./BackgroundImage.svelte" export { default as backgroundimage } from "./BackgroundImage.svelte"
export { default as daterangepicker } from "./DateRangePicker.svelte"
export * from "./charts" export * from "./charts"
export * from "./forms" export * from "./forms"
export * from "./table" export * from "./table"

View File

@ -0,0 +1,146 @@
/**
* Builds a lucene JSON query from the filter structure generated in the builder
* @param filter the builder filter structure
*/
export const buildLuceneQuery = filter => {
let query = {
string: {},
fuzzy: {},
range: {},
equal: {},
notEqual: {},
empty: {},
notEmpty: {},
}
if (Array.isArray(filter)) {
filter.forEach(({ operator, field, type, value }) => {
if (operator.startsWith("range")) {
if (!query.range[field]) {
query.range[field] = {
low:
type === "number"
? Number.MIN_SAFE_INTEGER
: "0000-00-00T00:00:00.000Z",
high:
type === "number"
? Number.MAX_SAFE_INTEGER
: "9999-00-00T00:00:00.000Z",
}
}
if (operator === "rangeLow" && value != null && value !== "") {
query.range[field].low = value
} else if (operator === "rangeHigh" && value != null && value !== "") {
query.range[field].high = value
}
} else if (query[operator]) {
query[operator][field] = value
}
})
}
return query
}
/**
* Performs a client-side lucene search on an array of data
* @param docs the data
* @param query the JSON lucene query
*/
export const luceneQuery = (docs, query) => {
if (!query) {
return docs
}
// Iterates over a set of filters and evaluates a fail function against a doc
const match = (type, failFn) => doc => {
const filters = Object.entries(query[type] || {})
for (let i = 0; i < filters.length; i++) {
if (failFn(filters[i][0], filters[i][1], doc)) {
return false
}
}
return true
}
// Process a string match (fails if the value does not start with the string)
const stringMatch = match("string", (key, value, doc) => {
return !doc[key] || !doc[key].startsWith(value)
})
// Process a range match
const rangeMatch = match("range", (key, value, doc) => {
return !doc[key] || doc[key] < value.low || doc[key] > value.high
})
// Process an equal match (fails if the value is different)
const equalMatch = match("equal", (key, value, doc) => {
return doc[key] !== value
})
// Process a not-equal match (fails if the value is the same)
const notEqualMatch = match("notEqual", (key, value, doc) => {
return doc[key] === value
})
// Process an empty match (fails if the value is not empty)
const emptyMatch = match("empty", (key, value, doc) => {
return doc[key] != null && doc[key] !== ""
})
// Process a not-empty match (fails is the value is empty)
const notEmptyMatch = match("notEmpty", (key, value, doc) => {
return doc[key] == null || doc[key] === ""
})
// Match a document against all criteria
const docMatch = doc => {
return (
stringMatch(doc) &&
rangeMatch(doc) &&
equalMatch(doc) &&
notEqualMatch(doc) &&
emptyMatch(doc) &&
notEmptyMatch(doc)
)
}
// Process all docs
return docs.filter(docMatch)
}
/**
* Performs a client-side sort from the equivalent server-side lucene sort
* parameters.
* @param docs the data
* @param sort the sort column
* @param sortOrder the sort order ("ascending" or "descending")
* @param sortType the type of sort ("string" or "number")
*/
export const luceneSort = (docs, sort, sortOrder, sortType = "string") => {
if (!sort || !sortOrder || !sortType) {
return docs
}
const parse = sortType === "string" ? x => `${x}` : x => parseFloat(x)
return docs.slice().sort((a, b) => {
const colA = parse(a[sort])
const colB = parse(b[sort])
if (sortOrder === "Descending") {
return colA > colB ? -1 : 1
} else {
return colA > colB ? 1 : -1
}
})
}
/**
* Limits the specified docs to the specified number of rows from the equivalent
* server-side lucene limit parameters.
* @param docs the data
* @param limit the number of docs to limit to
*/
export const luceneLimit = (docs, limit) => {
const numLimit = parseFloat(limit)
if (isNaN(numLimit)) {
return docs
}
return docs.slice(0, numLimit)
}

View File

@ -2,6 +2,59 @@
# yarn lockfile v1 # yarn lockfile v1
"@adobe/spectrum-css-workflow-icons@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
"@budibase/bbui@^0.9.12":
version "0.9.16"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.16.tgz#6cf454790d3e8d776d4d642379958a56260b0840"
integrity sha512-8hV4Vj3h3clzLclNTvVxPdOK9f02xTIcXA/lW1QPD9HZLOUxDogphujpDpb+E0/NevPlPYMHaX68kVtefNL5Jw==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
"@spectrum-css/actiongroup" "^1.0.1"
"@spectrum-css/avatar" "^3.0.2"
"@spectrum-css/button" "^3.0.1"
"@spectrum-css/buttongroup" "^3.0.2"
"@spectrum-css/checkbox" "^3.0.2"
"@spectrum-css/dialog" "^3.0.1"
"@spectrum-css/divider" "^1.0.1"
"@spectrum-css/dropzone" "^3.0.2"
"@spectrum-css/fieldgroup" "^3.0.2"
"@spectrum-css/fieldlabel" "^3.0.1"
"@spectrum-css/icon" "^3.0.1"
"@spectrum-css/illustratedmessage" "^3.0.2"
"@spectrum-css/inputgroup" "^3.0.2"
"@spectrum-css/label" "^2.0.10"
"@spectrum-css/link" "^3.1.1"
"@spectrum-css/menu" "^3.0.1"
"@spectrum-css/modal" "^3.0.1"
"@spectrum-css/pagination" "^3.0.3"
"@spectrum-css/picker" "^1.0.1"
"@spectrum-css/popover" "^3.0.1"
"@spectrum-css/progressbar" "^1.0.2"
"@spectrum-css/progresscircle" "^1.0.2"
"@spectrum-css/radio" "^3.0.2"
"@spectrum-css/search" "^3.0.2"
"@spectrum-css/sidenav" "^3.0.2"
"@spectrum-css/statuslight" "^3.0.2"
"@spectrum-css/switch" "^1.0.2"
"@spectrum-css/table" "^3.0.1"
"@spectrum-css/tabs" "^3.0.1"
"@spectrum-css/tags" "^3.0.2"
"@spectrum-css/textfield" "^3.0.1"
"@spectrum-css/toast" "^3.0.1"
"@spectrum-css/tooltip" "^3.0.3"
"@spectrum-css/treeview" "^3.0.2"
"@spectrum-css/typography" "^3.0.1"
"@spectrum-css/underlay" "^2.0.9"
"@spectrum-css/vars" "^3.0.1"
dayjs "^1.10.4"
svelte-flatpickr "^3.1.0"
svelte-portal "^1.0.0"
"@rollup/pluginutils@^4.1.0": "@rollup/pluginutils@^4.1.0":
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838"
@ -10,6 +63,98 @@
estree-walker "^2.0.1" estree-walker "^2.0.1"
picomatch "^2.2.2" picomatch "^2.2.2"
"@spectrum-css/actionbutton@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.3.tgz#8f7342a69b303c5acdcfa0a59f5e9267b9f3cb30"
integrity sha512-P9qoCPSiZ1SB6ZYqK5hub0vGty00YYqonQE0KTjtb1i+T1nYR/87vWqVPERx9j63uhgZncjwFYaThTvRqye7eQ==
"@spectrum-css/actiongroup@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actiongroup/-/actiongroup-1.0.3.tgz#4713ce65e6f5c72c404a7b638fbc3b4fd7e3874f"
integrity sha512-NlB9Q4ZlWixXxymoPIYG6g2hkwAGKFodHWPFfxHD8ddkjXWRB9G2akUP7cfsJ4DcYn4VisUORCOYQwqDiSmboQ==
"@spectrum-css/avatar@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/avatar/-/avatar-3.0.2.tgz#4f1826223eae330e64b6d3cc899e9bc2e98dac95"
integrity sha512-wEczvSqxttTWSiL3cOvXV/RmGRwSkw2w6+slcHhnf0kb7ovymMM+9oz8vvEpEsSeo5u598bc+7ktrKFpAd6soQ==
"@spectrum-css/button@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.3.tgz#2df1efaab6c7e0b3b06cb4b59e1eae59c7f1fc84"
integrity sha512-6CnLPqqtaU/PcSSIGeGRi0iFIIxIUByYLKFO6zn5NEUc12KQ28dJ4PLwB6WBa0L8vRoAGlnWWH2ZZweTijbXgg==
"@spectrum-css/buttongroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.3.tgz#719d868845ac9d2c4f939c1b9f6044507902d5aa"
integrity sha512-eXl8U4QWMWXqyTu654FdQdEGnmczgOYlpIFSHyCMVjhtPqZp2xwnLFiGh6LKw+bLio6eeOZ0L+vpk1GcoYqgkw==
"@spectrum-css/checkbox@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.3.tgz#8577067fc8b97e4609f92bd242364937a533a7bb"
integrity sha512-QVG9uMHq+lh70Dh6mDNnY+vEvNz2p7VC6xgLfDYfijp2qeiqYPq72fQK6p/SiyqPk96ZACzNRwgeROU6Xf6Wgg==
"@spectrum-css/dialog@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dialog/-/dialog-3.0.3.tgz#7715a4ea435e753afb623d99ca5917ed1bcd6f34"
integrity sha512-AhmKgfRIVyTe3ABiJ8lLUQL34VB/H6fN16na2LlbDRJvyRMzkdN1Xf2i6U3f4OMd3qQ8Gm5xat4BvMxIQPBAUQ==
"@spectrum-css/divider@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/divider/-/divider-1.0.3.tgz#639e2ebaa0834efa40f42397668bbd5c153ea385"
integrity sha512-Zy4Rn40w8UtzMh3wx/U9+CepSCpm1aOCGftHgWDub0XZuVTzh0c1WwyzTuLCx2Hf21z5VRGNiDh8bGEEzSbtNA==
dependencies:
"@spectrum-css/vars" "^3.0.2"
"@spectrum-css/dropzone@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dropzone/-/dropzone-3.0.3.tgz#aee71697a2c195947599d7541b858c3c198741dc"
integrity sha512-ujrswdtB6bHigklyHsm6zomFd6rUnKJ3xVVRjroVF4+ouK4DxK5tX0iVd0EW6AOfOjx4Cc28uyFot3fpxp+MQw==
"@spectrum-css/fieldgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.0.3.tgz#85d85da048d08200f25ceab378026dd2b11e0bb2"
integrity sha512-wXUXTXN1CPnR7M4Ltd+3uh7BfcNGUV1+Xe0/h0tCpq/j+S2Sd4xo7/pUMdU19sIDrAAtpEFp1tt+nouHcU5HGQ==
"@spectrum-css/fieldlabel@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.3.tgz#f73c04d20734d4718ffb620dc46458904685b449"
integrity sha512-nEvIkEXCD5n4fW67Unq6Iu7VXoauEd/JGpfTY02VsC5p4FJLnwKfPDbJUuUsqClAxqw7nAsmXVKtn4zQFf5yPQ==
"@spectrum-css/icon@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.3.tgz#5c612822380927087aebd526855d82ed2c3e2cba"
integrity sha512-hyloKOejPCXhP3MBNsm3SjR9j8xT1R1S19p32KW/0Qhj+VMUtfyEPmevyFptpn7wcovQccdl/vZVIVDuML/imw==
"@spectrum-css/illustratedmessage@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.2.tgz#6a480be98b027e050b086e7899e40d87adb0a8c0"
integrity sha512-dqnE8X27bGcO0HN8+dYx8O4o0dNNIAqeivOzDHhe2El+V4dTzMrNIerF6G0NLm3GjVf6XliwmitsZK+K6FmbtA==
"@spectrum-css/inputgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.3.tgz#00c9a370ddc2c55cf0f37dd6069faa9501fd7eb5"
integrity sha512-FqRJTiLL7jiGfzDVXWUGVLqHryJjCcqQIrqAi+Tq0oenapzsBe2qc/zIrKgh2wtMI+NTIBLXTECvij3L1HlqAg==
"@spectrum-css/label@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001"
integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ==
"@spectrum-css/link@^3.1.1":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==
"@spectrum-css/menu@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.3.tgz#46a9b221bb5f470a2f8a934bdfd512d84d2fdc4d"
integrity sha512-qKA9J/MrikNKIpCEHsAazG2vY3im5tjGCmo6p9Pdnu8/aQMsiuZDHZayukeCttJUZkrb9guDVL9OIHlK5RZvcQ==
"@spectrum-css/modal@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/modal/-/modal-3.0.2.tgz#58b6621cab65f90788d310374f40df1f7090473f"
integrity sha512-YnIivJhoaao7Otu+HV7sgebPyFbO6sd/oMvTN/Rb2wwgnaMnIIuIRdGandSrcgotN2uNgs+P0knG6mv/xA1/dg==
"@spectrum-css/page@^3.0.1": "@spectrum-css/page@^3.0.1":
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846" resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846"
@ -17,11 +162,111 @@
dependencies: dependencies:
"@spectrum-css/vars" "^3.0.1" "@spectrum-css/vars" "^3.0.1"
"@spectrum-css/pagination@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/pagination/-/pagination-3.0.3.tgz#b204c3ada384c4af751a354bc428346d82eeea65"
integrity sha512-OJ/v9GeNXJOZ9Yr9LDBYPrR2NCiLOWP9wANT/a5sqFuugRnQbn/HYMnRp9TBxwpDY6ihaPo0T/wi7kLiAJFdDw==
"@spectrum-css/picker@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.3.tgz#21379bcf8ae94277deeb6ad65dcd9e2bbfacb487"
integrity sha512-oHLGxBx5BwVCSGo7/T1C9PTHX1+/5AmVjyLiTJ4UoIdSJmOERw9YcRZbcGZgBJNWbxcjr4TyGtwj1EcSjEy97w==
"@spectrum-css/popover@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.3.tgz#6fb69873474fb968afb738eacb9e121f93e83a09"
integrity sha512-KvmXv4TV19FBx39KfmgVlDYtvtBqv/8RRK7RRLDDHGViTxZtShjVsVpwIgfkfgn4iJztCnXpWzFqRXWUu2XCpQ==
"@spectrum-css/progressbar@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/progressbar/-/progressbar-1.0.3.tgz#f70bcc38a2a21cff2f422ec825724ebbb9455e67"
integrity sha512-vJHplefUuy8+NjCw1X7fLbqHVGNVBpvGFXNAeaIj4SFf4ygxiUq/5c9iRhhsCQixEsJlfD/b7BnGXU7BUDkr6Q==
"@spectrum-css/progresscircle@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/progresscircle/-/progresscircle-1.0.2.tgz#258ea9170fb70f795edda03e38a61d93bef4487c"
integrity sha512-JLULpyzjIY95lzlWR1yE1gv4l1K6p+scQ+edmuZZUHBzwM3pUtkvHJmUlA9TYdResUYW6Uka60VRdY6lZ8gnFQ==
"@spectrum-css/radio@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/radio/-/radio-3.0.3.tgz#25c3bc5e9c30a8a8ae728717b7c7fb736cdae640"
integrity sha512-LaLGfz/eGNR2iyqouXYILIA+pKRqF769iPdwM0REm5RpWvMQDD7rPZ/kWlg18owjaFsyllEp25gEjmhRJIIVOw==
"@spectrum-css/search@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/search/-/search-3.0.3.tgz#3415dc106aca0d5dd996e87084a1b47c2b95a882"
integrity sha512-kdLpKTt0obljuhS1q1tukujRlvSs8sBwij76D4Qp8KpMzwePfZyvv1kYzuWPNZfTeISxWsmyZ6Wxd1uvzjn+UA==
"@spectrum-css/sidenav@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/sidenav/-/sidenav-3.0.3.tgz#132141fbd2500a927c312fa4e1d712c438b3d597"
integrity sha512-cQ+CgwjxGftrcc79i1XnGd66QTl7H7zopSU0UTV4Qq7hvqfrjjWxfZ6b+3tezrrhNlDope1ff9o8sm67PsPXtg==
"@spectrum-css/statuslight@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w==
"@spectrum-css/switch@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"
integrity sha512-zqmHpgWPNg1gEwdUNFYV3CBX5JaeALfIqcJIxE0FLZqr9d1C4+oLE0ItIFzt1bwr4bFAOmkEpvtiY+amluzGxQ==
"@spectrum-css/table@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.3.tgz#7f7f19905ef3275cbf907ce3a5818e63c30b2caf"
integrity sha512-nxwzVjLPsXoY/v4sdxOVYLcC+cEbGgJyLcLclT5LT9MGSbngFeUMJzzVR4EvehzuN4dH7hrATG7Mbuq29Mf0Hg==
"@spectrum-css/tabs@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.0.3.tgz#51dd6f168c897b0fdc3a7e9f901df7bd2288b4fc"
integrity sha512-iLP1I72bJWz9APdQB1jiw+pOv5a7N+hYOCJvRoc56Us/hJKVzowkyGRe3uH+8v36nCG9bHxiAQNLoU8eXisVrg==
"@spectrum-css/tags@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.3.tgz#fc76d2735cdc442de91b7eb3bee49a928c0767ac"
integrity sha512-SL8vPxVDfWcY5VdIuyl0TImEXcOU1I7yCyXkk7MudMwfnYs81FaIyY32hFV9OHj0Tz/36UzRzc7AVMSuRQ53pw==
"@spectrum-css/textfield@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.2.tgz#907f62d2dc82852dd6236a820be99e252b531631"
integrity sha512-nkFgAb0cP4jUodkUBErMNfyF78jJLtgL1Mrr9/rvGpGobo10IAbb8zZY4CkZ64o8XmMy/85+wZTKcx+KHatqpg==
"@spectrum-css/toast@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/toast/-/toast-3.0.3.tgz#97c1527384707600832ecda35643ed304615250f"
integrity sha512-CjLeaMs+cjUXojCCRtbj0YkD2BoZW16kjj2o5omkEpUTjA34IJ8xJ1a+CCtDILWekhXvN0MBN4sbumcnwcnx8w==
"@spectrum-css/tooltip@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tooltip/-/tooltip-3.0.3.tgz#26b8ca3b3d30e29630244d85eb4fc11d0c841281"
integrity sha512-ztRF7WW1FzyNavXBRc+80z67UoOrY9wl3cMYsVD3MpDnyxdzP8cjza1pCcolKBaFqRTcQKkxKw3GWtGICRKR5A==
"@spectrum-css/treeview@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046"
integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw==
"@spectrum-css/typography@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38"
integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA==
"@spectrum-css/underlay@^2.0.9":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/underlay/-/underlay-2.0.10.tgz#8b75b646605a311850f6620caa18d4996cd64ed7"
integrity sha512-PmsmkzeGD/rY4pp3ILXHt9w8BW7uaEqXe08hQRS7rGki7wqCpG4mE0/8N3yEcA3QxWQclmG9gdkg5uz6wMmYzA==
"@spectrum-css/vars@^3.0.1": "@spectrum-css/vars@^3.0.1":
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e" resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e"
integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w== integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w==
"@spectrum-css/vars@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999"
integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw==
"@sveltejs/vite-plugin-svelte@^1.0.0-next.5": "@sveltejs/vite-plugin-svelte@^1.0.0-next.5":
version "1.0.0-next.5" version "1.0.0-next.5"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.5.tgz#8cf608f7a3c33dfa5b648397aae1ba90e6a4883f" resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.5.tgz#8cf608f7a3c33dfa5b648397aae1ba90e6a4883f"
@ -80,6 +325,11 @@ colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
dayjs@^1.10.4, dayjs@^1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986"
integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==
debug@^4.3.2: debug@^4.3.2:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
@ -226,6 +476,11 @@ svelte-hmr@^0.13.3:
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897" resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897"
integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q== integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q==
svelte-portal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3"
integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q==
svelte@^3.38.2: svelte@^3.38.2:
version "3.38.2" version "3.38.2"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.21", "version": "0.9.27",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -33,7 +33,7 @@ function testObject(object) {
*/ */
module.exports.processObject = async (object, context) => { module.exports.processObject = async (object, context) => {
testObject(object) testObject(object)
for (let key of Object.keys(object)) { for (let key of Object.keys(object || {})) {
if (object[key] != null) { if (object[key] != null) {
let val = object[key] let val = object[key]
if (typeof val === "string") { if (typeof val === "string") {
@ -68,7 +68,7 @@ module.exports.processString = async (string, context) => {
*/ */
module.exports.processObjectSync = (object, context) => { module.exports.processObjectSync = (object, context) => {
testObject(object) testObject(object)
for (let key of Object.keys(object)) { for (let key of Object.keys(object || {})) {
let val = object[key] let val = object[key]
if (typeof val === "string") { if (typeof val === "string") {
object[key] = module.exports.processStringSync(object[key], context) object[key] = module.exports.processStringSync(object[key], context)

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.21", "version": "0.9.27",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -21,8 +21,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.21", "@budibase/auth": "^0.9.27",
"@budibase/string-templates": "^0.9.21", "@budibase/string-templates": "^0.9.27",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"aws-sdk": "^2.811.0", "aws-sdk": "^2.811.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",

View File

@ -2,7 +2,7 @@ const Router = require("@koa/router")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
const { routes } = require("./routes") const { routes } = require("./routes")
const { buildAuthMiddleware } = require("@budibase/auth").auth const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth
const PUBLIC_ENDPOINTS = [ const PUBLIC_ENDPOINTS = [
{ {
@ -62,6 +62,7 @@ router
} }
return next() return next()
}) })
.use(auditLog)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

View File

@ -51,12 +51,8 @@ exports.init = async () => {
* make sure redis connection is closed. * make sure redis connection is closed.
*/ */
exports.shutdown = async () => { exports.shutdown = async () => {
if (pwResetClient != null) { if (pwResetClient) await pwResetClient.finish()
await pwResetClient.finish() if (invitationClient) await invitationClient.finish()
}
if (invitationClient != null) {
await invitationClient.finish()
}
} }
/** /**

View File

@ -910,7 +910,7 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asn1@~0.2.3: asn1@~0.2.0, asn1@~0.2.3:
version "0.2.4" version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
@ -1076,7 +1076,7 @@ base@^0.11.1:
mixin-deep "^1.2.0" mixin-deep "^1.2.0"
pascalcase "^0.1.1" pascalcase "^0.1.1"
bcrypt-pbkdf@^1.0.0: bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
@ -1093,6 +1093,15 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
boxen@^4.2.0: boxen@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
@ -1337,6 +1346,11 @@ chokidar@^3.2.2:
optionalDependencies: optionalDependencies:
fsevents "~2.3.1" fsevents "~2.3.1"
chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
ci-info@^2.0.0: ci-info@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@ -1773,6 +1787,24 @@ diff-sequences@^26.6.2:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
docker-modem@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.0.tgz#cb912ad8daed42f858269fb3be6944df281ec12d"
integrity sha512-WwFajJ8I5geZ/dDZ5FDMDA6TBkWa76xWwGIGw8uzUjNUGCN0to83wJ8Oi1AxrJTC0JBn+7fvIxUctnawtlwXeg==
dependencies:
debug "^4.1.1"
readable-stream "^3.5.0"
split-ca "^1.0.1"
ssh2 "^0.8.7"
dockerode@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.0.tgz#bedaf48ef9fa9124275a54a9881a92374c51008e"
integrity sha512-St08lfOjpYCOXEM8XA0VLu3B3hRjtddODphNW5GFoA0AS3JHgoPQKOz0Qmdzg3P+hUPxhb02g1o1Cu1G+U3lRg==
dependencies:
docker-modem "^3.0.0"
tar-fs "~2.0.1"
domexception@^2.0.1: domexception@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
@ -1857,7 +1889,7 @@ encoding-down@^6.3.0:
level-codec "^9.0.0" level-codec "^9.0.0"
level-errors "^2.0.0" level-errors "^2.0.0"
end-of-stream@^1.1.0: end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@ -2238,6 +2270,11 @@ fresh@~0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -3998,6 +4035,11 @@ mixin-deep@^1.2.0:
for-in "^1.0.2" for-in "^1.0.2"
is-extendable "^1.0.1" is-extendable "^1.0.1"
mkdirp-classic@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@^0.5.0: mkdirp@^0.5.0:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@ -4829,7 +4871,7 @@ readable-stream@1.1.14:
isarray "0.0.1" isarray "0.0.1"
string_decoder "~0.10.x" string_decoder "~0.10.x"
"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stream@^3.6.0: "readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -5326,6 +5368,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65"
integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==
split-ca@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6"
integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=
split-string@^3.0.1, split-string@^3.0.2: split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -5345,6 +5392,22 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssh2-streams@~0.4.10:
version "0.4.10"
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
dependencies:
asn1 "~0.2.0"
bcrypt-pbkdf "^1.0.2"
streamsearch "~0.1.2"
ssh2@^0.8.7:
version "0.8.9"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3"
integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
dependencies:
ssh2-streams "~0.4.10"
sshpk@^1.7.0: sshpk@^1.7.0:
version "1.16.1" version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@ -5390,6 +5453,11 @@ stealthy-require@^1.1.1:
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
streamsearch@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
string-length@^4.0.1: string-length@^4.0.1:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
@ -5529,6 +5597,27 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tar-fs@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==
dependencies:
chownr "^1.1.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.0.0"
tar-stream@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
dependencies:
bl "^4.0.3"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
term-size@^2.1.0: term-size@^2.1.0:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"