Merge branch 'master' of github.com:Budibase/budibase into form-builder

This commit is contained in:
Andrew Kingston 2021-02-01 09:11:48 +00:00
commit 4630699f13
23 changed files with 265 additions and 49 deletions

View File

@ -103,6 +103,33 @@
"contributions": [
"code"
]
},
{
"login": "victoriasloan",
"name": "victoriasloan",
"avatar_url": "https://avatars.githubusercontent.com/u/9913651?v=4",
"profile": "https://github.com/victoriasloan",
"contributions": [
"code"
]
},
{
"login": "yashank09",
"name": "yashank09",
"avatar_url": "https://avatars.githubusercontent.com/u/37672190?v=4",
"profile": "https://github.com/yashank09",
"contributions": [
"code"
]
},
{
"login": "SOVLOOKUP",
"name": "SOVLOOKUP",
"avatar_url": "https://avatars.githubusercontent.com/u/53158137?v=4",
"profile": "https://github.com/SOVLOOKUP",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -174,6 +174,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<tr>
<td align="center"><a href="https://github.com/pngwn"><img src="https://avatars1.githubusercontent.com/u/12937446?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pngwn</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/HugoLd"><img src="https://avatars0.githubusercontent.com/u/26521848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>HugoLd</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=HugoLd" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/victoriasloan"><img src="https://avatars.githubusercontent.com/u/9913651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>victoriasloan</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=victoriasloan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yashank09"><img src="https://avatars.githubusercontent.com/u/37672190?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yashank09</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=yashank09" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/SOVLOOKUP"><img src="https://avatars.githubusercontent.com/u/53158137?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SOVLOOKUP</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=SOVLOOKUP" title="Code">💻</a></td>
</tr>
</table>

1
hosting/update.sh Normal file
View File

@ -0,0 +1 @@
docker-compose --env-file hosting.properties pull && ./start.sh

View File

@ -74,6 +74,7 @@
"codemirror": "^5.59.0",
"d3-selection": "^1.4.1",
"deepmerge": "^4.2.2",
"downloadjs": "^1.4.7",
"fast-sort": "^2.2.0",
"lodash": "^4.17.13",
"posthog-js": "1.4.5",

View File

@ -9,6 +9,7 @@
let permissions = []
let selectedRole = {}
let errors = []
let builtInRoles = ['Admin', 'Power', 'Basic', 'Public']
$: selectedRoleId = selectedRole._id
$: otherRoles = $backendUiStore.roles.filter(
role => role._id !== selectedRoleId
@ -102,7 +103,7 @@
{/each}
</Select>
{#if selectedRole}
<Input label="Name" bind:value={selectedRole.name} thin />
<Input label="Name" bind:value={selectedRole.name} thin disabled={builtInRoles.includes(selectedRole.name)}/>
<Select
thin
secondary

View File

@ -73,7 +73,7 @@
.notOneOf(existingAppUrls),
}
} else {
nameValidation = { name: string.required(nameError) }
nameValidation = { name: string().required(nameError) }
}
})
</script>

View File

@ -2,7 +2,26 @@
import { TextButton } from "@budibase/bbui"
import { Heading } from "@budibase/bbui"
import { Spacer } from "@budibase/bbui"
import api from "builderStore/api"
import { notifier } from "builderStore/store/notifications"
import Spinner from "components/common/Spinner.svelte"
import download from "downloadjs"
export let name, _id
let appExportLoading = false
async function exportApp() {
appExportLoading = true
try {
download(`/api/backups/export?appId=${_id}`)
notifier.success("App Export Complete.")
} catch (err) {
notifier.danger("App Export Failed.")
} finally {
appExportLoading = false
}
}
</script>
<div class="apps-card">
@ -14,6 +33,9 @@
{name}
</TextButton>
{#if appExportLoading}
<Spinner size="10" />
{:else}<i class="ri-folder-download-line" on:click={exportApp} />{/if}
</div>
</div>
@ -31,7 +53,17 @@
.card-footer {
display: flex;
flex-direction: row;
align-items: baseline;
align-items: center;
justify-content: space-between;
}
i {
font-size: var(--font-size-l);
cursor: pointer;
transition: 0.2s all;
}
i:hover {
color: var(--blue);
}
</style>

View File

@ -1,14 +1,51 @@
<script>
import { Label, Heading, Input } from "@budibase/bbui"
import Dropzone from "components/common/Dropzone.svelte"
const BYTES_IN_MB = 1000000
const FILE_SIZE_LIMIT = BYTES_IN_MB * 5
export let validationErrors
export let template
let blurred = { appName: false }
let file
function handleFile(evt) {
const fileArray = Array.from(evt.target.files)
if (fileArray.some(file => file.size >= FILE_SIZE_LIMIT)) {
notifier.danger(
`Files cannot exceed ${FILE_SIZE_LIMIT /
BYTES_IN_MB}MB. Please try again with smaller files.`
)
return
}
file = fileArray[0]
template.fileImportPath = file.path
}
</script>
<h2>Create your Web App</h2>
{#if template?.fromFile}
<h2>Import Your Web App From A File</h2>
{:else}
<h2>Create your Web App</h2>
{/if}
<div class="container">
{#if template}
{#if template?.fromFile}
<div class="template">
<Label extraSmall grey>Import File</Label>
<div class="dropzone">
<input
id="file-upload"
accept=".txt"
type="file"
on:change={handleFile} />
<label for="file-upload" class:uploaded={file}>
{#if file}{file.name}{:else}Import{/if}
</label>
</div>
</div>
{:else if template}
<div class="template">
<Label extraSmall grey>Selected Template</Label>
<Heading small>{template.name}</Heading>
@ -33,4 +70,48 @@
/* Fix layout due to LH 0 on heading */
margin-bottom: 16px;
}
.dropzone {
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
border-radius: 10px;
transition: all 0.3s;
}
.uploaded {
color: var(--blue);
}
input[type="file"] {
display: none;
}
label {
font-family: var(--font-sans);
cursor: pointer;
font-weight: 500;
box-sizing: border-box;
overflow: hidden;
border-radius: var(--border-radius-s);
color: var(--ink);
padding: var(--spacing-m) var(--spacing-l);
transition: all 0.2s ease 0s;
display: inline-flex;
text-rendering: optimizeLegibility;
min-width: auto;
outline: none;
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
-webkit-box-align: center;
user-select: none;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 100%;
background-color: var(--grey-2);
font-size: var(--font-size-xs);
line-height: normal;
border: var(--border-transparent);
}
</style>

View File

@ -4,7 +4,7 @@
import AppList from "components/start/AppList.svelte"
import { get } from "builderStore/api"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import { Button, Heading, Modal } from "@budibase/bbui"
import { Button, Heading, Modal, Spacer } from "@budibase/bbui"
import TemplateList from "components/start/TemplateList.svelte"
import analytics from "analytics"
@ -43,13 +43,22 @@
modal.show()
}
function initiateAppImport() {
template = { fromFile: true }
modal.show()
}
checkIfKeysAndApps()
</script>
<div class="container">
<div class="header">
<Heading medium black>Welcome to the Budibase Beta</Heading>
<Button primary on:click={modal.show}>Create New Web App</Button>
<div class="button-group">
<Button secondary on:click={initiateAppImport}>Import Web App</Button>
<Spacer medium />
<Button primary on:click={modal.show}>Create New Web App</Button>
</div>
</div>
<div class="banner">
@ -103,4 +112,9 @@
color: white;
font-weight: 500;
}
.button-group {
display: flex;
flex-direction: row;
}
</style>

View File

@ -2995,6 +2995,11 @@ dotenv@^8.2.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
downloadjs@^1.4.7:
version "1.4.7"
resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c"
integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=
duplexer@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"

View File

@ -18,15 +18,12 @@ const handleError = error => {
const makeApiCall = async ({ method, url, body, json = true }) => {
try {
const requestBody = json ? JSON.stringify(body) : body
let headers = {
const inBuilder = window["##BUDIBASE_IN_BUILDER##"]
const headers = {
Accept: "application/json",
"x-budibase-app-id": window["##BUDIBASE_APP_ID##"],
}
if (json) {
headers["Content-Type"] = "application/json"
}
if (!window["##BUDIBASE_IN_BUILDER##"]) {
headers["x-budibase-type"] = "client"
...(json && { "Content-Type": "application/json" }),
...(!inBuilder && { "x-budibase-type": "client" }),
}
const response = await fetch(url, {
method,

View File

@ -22,7 +22,8 @@
"maintainer": "Budibase",
"icon": "./build/icons/",
"target": [
"deb"
"deb",
"AppImage"
],
"category": "Development"
},

View File

@ -105,10 +105,16 @@ async function createInstance(template) {
// replicate the template data to the instance DB
if (template) {
const templatePath = await downloadTemplate(...template.key.split("/"))
const dbDumpReadStream = fs.createReadStream(
join(templatePath, "db", "dump.txt")
)
let dbDumpReadStream
if (template.fileImportPath) {
dbDumpReadStream = fs.createReadStream(template.fileImportPath)
} else {
const templatePath = await downloadTemplate(...template.key.split("/"))
dbDumpReadStream = fs.createReadStream(
join(templatePath, "db", "dump.txt")
)
}
const { ok } = await db.load(dbDumpReadStream)
if (!ok) {
throw "Error loading database dump from template."

View File

@ -0,0 +1,41 @@
const { performDump } = require("../../utilities/templates")
const path = require("path")
const os = require("os")
const fs = require("fs-extra")
exports.exportAppDump = async function(ctx) {
const { appId } = ctx.query
const backupsDir = path.join(os.homedir(), ".budibase", "backups")
fs.ensureDirSync(backupsDir)
const backupIdentifier = `${appId} Backup: ${new Date()}.txt`
await performDump({
dir: backupsDir,
appId,
name: backupIdentifier,
})
ctx.status = 200
const backupFile = path.join(backupsDir, backupIdentifier)
ctx.attachment(backupIdentifier)
ctx.body = fs.createReadStream(backupFile)
// ctx.body = {
// url: `/api/backups/download/${backupIdentifier}`,
// }
}
// exports.downloadAppDump = async function(ctx) {
// const fileName = ctx.params.fileName
// const backupsDir = path.join(os.homedir(), ".budibase", "backups")
// fs.ensureDirSync(backupsDir)
// const backupFile = path.join(backupsDir, fileName)
// ctx.attachment(fileName)
// ctx.body = fs.createReadStream(backupFile)
// }

View File

@ -83,7 +83,7 @@ const controller = {
ctx.message = `View ${ctx.params.viewName} saved successfully.`
},
exportView: async ctx => {
const view = ctx.request.body
const view = ctx.query.view
const format = ctx.query.format
// Fetch view rows
@ -102,14 +102,6 @@ const controller = {
const filename = `${view.name}.${format}`
fs.writeFileSync(join(os.tmpdir(), filename), exportedFile)
ctx.body = {
url: `/api/views/export/download/${filename}`,
name: view.name,
}
},
downloadExport: async ctx => {
const filename = ctx.params.fileName
ctx.attachment(filename)
ctx.body = fs.createReadStream(join(os.tmpdir(), filename))
},

View File

@ -5,6 +5,7 @@ const zlib = require("zlib")
const { budibaseAppsDir } = require("../utilities/budibaseDir")
const { isDev } = require("../utilities")
const { mainRoutes, authRoutes, staticRoutes } = require("./routes")
const pkg = require("../../package.json")
const router = new Router()
const env = require("../environment")
@ -32,6 +33,7 @@ router
await next()
})
.use("/health", ctx => (ctx.status = 200))
.use("/version", ctx => (ctx.body = pkg.version))
.use(authenticated)
// error handling middleware

View File

@ -0,0 +1,15 @@
const Router = require("@koa/router")
const controller = require("../controllers/backup")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions")
const router = Router()
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
// .get(
// "/api/backups/download/:fileName",
// authorized(BUILDER),
// controller.downloadAppDump
// )
module.exports = router

View File

@ -21,6 +21,7 @@ const permissionRoutes = require("./permission")
const datasourceRoutes = require("./datasource")
const queryRoutes = require("./query")
const hostingRoutes = require("./hosting")
const backupRoutes = require("./backup")
exports.mainRoutes = [
deployRoutes,
@ -42,6 +43,7 @@ exports.mainRoutes = [
datasourceRoutes,
queryRoutes,
hostingRoutes,
backupRoutes,
// these need to be handled last as they still use /api/:tableId
// this could be breaking as koa may recognise other routes as this
tableRoutes,

View File

@ -26,10 +26,5 @@ router
)
.post("/api/views", authorized(BUILDER), usage, viewController.save)
.post("/api/views/export", authorized(BUILDER), viewController.exportView)
.get(
"/api/views/export/download/:fileName",
authorized(BUILDER),
viewController.downloadExport
)
module.exports = router

View File

@ -10,7 +10,6 @@ const streamPipeline = promisify(stream.pipeline)
const { budibaseAppsDir } = require("./budibaseDir")
const env = require("../environment")
const CouchDB = require("../db")
const { DocumentTypes } = require("../db/utils")
const DEFAULT_TEMPLATES_BUCKET =
"prod-budi-templates.s3-eu-west-1.amazonaws.com"
@ -56,6 +55,15 @@ exports.downloadTemplate = async function(type, name) {
return dirName
}
async function performDump({ dir, appId, name = "dump.txt" }) {
const writeStream = fs.createWriteStream(join(dir, name))
// perform couch dump
const instanceDb = new CouchDB(appId)
await instanceDb.dump(writeStream, {})
}
exports.performDump = performDump
exports.exportTemplateFromApp = async function({ templateName, appId }) {
// Copy frontend files
const templatesDir = join(
@ -67,13 +75,6 @@ exports.exportTemplateFromApp = async function({ templateName, appId }) {
"db"
)
fs.ensureDirSync(templatesDir)
const writeStream = fs.createWriteStream(join(templatesDir, "dump.txt"))
// perform couch dump
const instanceDb = new CouchDB(appId)
await instanceDb.dump(writeStream, {
filter: doc => {
return !doc._id.startsWith(DocumentTypes.USER)
},
})
await performDump({ dir: templatesDir, appId })
return templatesDir
}

View File

@ -40,7 +40,7 @@
"gitHead": "62ebf3cedcd7e9b2494b4f8cbcfb90927609b491",
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
"@budibase/bbui": "^1.52.4",
"@budibase/bbui": "^1.55.1",
"@budibase/svelte-ag-grid": "^0.0.16",
"@spectrum-css/actionbutton": "^1.0.0-beta.1",
"@spectrum-css/button": "^3.0.0-beta.6",

View File

@ -135,8 +135,7 @@
}
const updateRow = async row => {
const schema = (await API.fetchTableDefinition(row.tableId)).schema
await API.updateRow(schema, { data: row })
await API.updateRow(row)
}
const deleteRows = async () => {

View File

@ -44,10 +44,10 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@budibase/bbui@^1.52.4":
version "1.52.4"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.52.4.tgz#ae3c17e1f49f14e65831703958bcddc6e64afd24"
integrity sha512-/wiv5dSyvXLgy2/zGEslnCsjwE8qqng1D8k5ScSOPEyMab8tzzd1XxfZAN9rp84zIMgAXeH6s5a4j4riR+jVkg==
"@budibase/bbui@^1.55.1":
version "1.55.1"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.55.1.tgz#291fb6fa10479b49f078d3a911ad0ed42c2e6b12"
integrity sha512-bxsHBwkOqCtuFz89e0hAXwvwycfS4xPPrEge5PxK1Lh3uqetO4bXoIxYaIDjfi2Ku7CYIzEmOwSloNaQWeTF4g==
dependencies:
markdown-it "^12.0.2"
quill "^1.3.7"