1
0
Fork 0
mirror of synced 2024-06-21 11:51:00 +12:00

pouchDB integration, use app id instead of app name for keying app packages

This commit is contained in:
Martin McKeaveney 2020-04-23 14:37:08 +01:00
parent 228999aba5
commit 8a7c0f8ba3
43 changed files with 1017 additions and 897 deletions

View file

@ -1,34 +1,34 @@
import { flatten, values, uniq, map } from "lodash/fp"
import { pipe } from "components/common/core"
export const loadLibs = async (appName, appPackage) => {
export const loadLibs = async (appId, appPackage) => {
const allLibraries = {}
for (let lib of libsFromPages(appPackage.pages)) {
const libModule = await import(makeLibraryUrl(appName, lib))
const libModule = await import(makeLibraryUrl(appId, lib))
allLibraries[lib] = libModule
}
return allLibraries
}
export const loadLibUrls = (appName, appPackage) => {
export const loadLibUrls = (appId, appPackage) => {
const allLibraries = []
for (let lib of libsFromPages(appPackage.pages)) {
const libUrl = makeLibraryUrl(appName, lib)
const libUrl = makeLibraryUrl(appId, lib)
allLibraries.push({ libName: lib, importPath: libUrl })
}
return allLibraries
}
export const loadLib = async (appName, lib, allLibs) => {
allLibs[lib] = await import(makeLibraryUrl(appName, lib))
export const loadLib = async (appId, lib, allLibs) => {
allLibs[lib] = await import(makeLibraryUrl(appId, lib))
return allLibs
}
export const makeLibraryUrl = (appName, lib) =>
`/_builder/${appName}/componentlibrary?lib=${encodeURI(lib)}`
export const makeLibraryUrl = (appId, lib) =>
`/_builder/${appId}/componentlibrary?lib=${encodeURI(lib)}`
export const libsFromPages = pages =>
pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq])

View file

@ -1,15 +1,9 @@
import { writable } from "svelte/store"
import api from "../api"
import { cloneDeep, sortBy, find, remove } from "lodash/fp"
import { hierarchy as hierarchyFunctions } from "../../../../core/src"
import { find } from "lodash/fp"
import {
getNode,
validate,
constructHierarchy,
templateApi,
isIndex,
canDeleteIndex,
canDeleteModel,
} from "components/common/core"
export const getBackendUiStore = () => {
@ -20,6 +14,7 @@ export const getBackendUiStore = () => {
},
breadcrumbs: [],
models: [],
users: [],
selectedDatabase: {},
selectedModel: {},
}
@ -103,38 +98,24 @@ export const saveBackend = async state => {
},
accessLevels: state.accessLevels,
})
const instances_currentFirst = state.selectedDatabase
? [
state.appInstances.find(i => i.id === state.selectedDatabase.id),
...state.appInstances.filter(i => i.id !== state.selectedDatabase.id),
]
: state.appInstances
for (let instance of instances_currentFirst) {
await api.post(
`/_builder/instance/${state.appname}/${instance.id}/api/upgradeData`,
{ newHierarchy: state.hierarchy, accessLevels: state.accessLevels }
)
}
}
export const newModel = (store, useRoot) => () => {
store.update(state => {
state.currentNodeIsNew = true
const shadowHierarchy = createShadowHierarchy(state.hierarchy)
const parent = useRoot
? shadowHierarchy
: getNode(shadowHierarchy, state.currentNode.nodeId)
state.errors = []
state.currentNode = templateApi(shadowHierarchy).getNewModelTemplate(
parent,
"",
true
)
return state
})
}
// export const newModel = (store, useRoot) => () => {
// store.update(state => {
// state.currentNodeIsNew = true
// const shadowHierarchy = createShadowHierarchy(state.hierarchy)
// const parent = useRoot
// ? shadowHierarchy
// : getNode(shadowHierarchy, state.currentNode.nodeId)
// state.errors = []
// state.currentNode = templateApi(shadowHierarchy).getNewModelTemplate(
// parent,
// "",
// true
// )
// return state
// })
// }
export const selectExistingNode = store => nodeId => {
store.update(state => {
@ -145,133 +126,133 @@ export const selectExistingNode = store => nodeId => {
})
}
export const newIndex = (store, useRoot) => () => {
store.update(state => {
state.shadowHierarchy = createShadowHierarchy(state.hierarchy)
state.currentNodeIsNew = true
state.errors = []
const parent = useRoot
? state.shadowHierarchy
: getNode(state.shadowHierarchy, state.currentNode.nodeId)
// export const newIndex = (store, useRoot) => () => {
// store.update(state => {
// state.shadowHierarchy = createShadowHierarchy(state.hierarchy)
// state.currentNodeIsNew = true
// state.errors = []
// const parent = useRoot
// ? state.shadowHierarchy
// : getNode(state.shadowHierarchy, state.currentNode.nodeId)
state.currentNode = templateApi(state.shadowHierarchy).getNewIndexTemplate(
parent
)
return state
})
}
// state.currentNode = templateApi(state.shadowHierarchy).getNewIndexTemplate(
// parent
// )
// return state
// })
// }
export const saveCurrentNode = store => () => {
store.update(state => {
const errors = validate.node(state.currentNode)
state.errors = errors
if (errors.length > 0) {
return state
}
const parentNode = getNode(
state.hierarchy,
state.currentNode.parent().nodeId
)
// export const saveCurrentNode = store => () => {
// store.update(state => {
// const errors = validate.node(state.currentNode)
// state.errors = errors
// if (errors.length > 0) {
// return state
// }
// const parentNode = getNode(
// state.hierarchy,
// state.currentNode.parent().nodeId
// )
const existingNode = getNode(state.hierarchy, state.currentNode.nodeId)
// const existingNode = getNode(state.hierarchy, state.currentNode.nodeId)
let index = parentNode.children.length
if (existingNode) {
// remove existing
index = existingNode.parent().children.indexOf(existingNode)
if (isIndex(existingNode)) {
parentNode.indexes = parentNode.indexes.filter(
node => node.nodeId !== existingNode.nodeId
)
} else {
parentNode.children = parentNode.children.filter(
node => node.nodeId !== existingNode.nodeId
)
}
}
// let index = parentNode.children.length
// if (existingNode) {
// // remove existing
// index = existingNode.parent().children.indexOf(existingNode)
// if (isIndex(existingNode)) {
// parentNode.indexes = parentNode.indexes.filter(
// node => node.nodeId !== existingNode.nodeId
// )
// } else {
// parentNode.children = parentNode.children.filter(
// node => node.nodeId !== existingNode.nodeId
// )
// }
// }
// should add node into existing hierarchy
const cloned = cloneDeep(state.currentNode)
templateApi(state.hierarchy).constructNode(parentNode, cloned)
// // should add node into existing hierarchy
// const cloned = cloneDeep(state.currentNode)
// templateApi(state.hierarchy).constructNode(parentNode, cloned)
if (isIndex(existingNode)) {
parentNode.children = sortBy("name", parentNode.children)
} else {
parentNode.indexes = sortBy("name", parentNode.indexes)
}
// if (isIndex(existingNode)) {
// parentNode.children = sortBy("name", parentNode.children)
// } else {
// parentNode.indexes = sortBy("name", parentNode.indexes)
// }
if (!existingNode && state.currentNode.type === "record") {
const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate(
cloned.parent()
)
defaultIndex.name = hierarchyFunctions.isTopLevelIndex(cloned)
? `all_${cloned.name}s`
: `${cloned.parent().name}_${cloned.name}s`
// if (!existingNode && state.currentNode.type === "record") {
// const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate(
// cloned.parent()
// )
// defaultIndex.name = hierarchyFunctions.isTopLevelIndex(cloned)
// ? `all_${cloned.name}s`
// : `${cloned.parent().name}_${cloned.name}s`
defaultIndex.allowedModelNodeIds = [cloned.nodeId]
}
// defaultIndex.allowedModelNodeIds = [cloned.nodeId]
// }
state.currentNodeIsNew = false
// state.currentNodeIsNew = false
saveBackend(state)
// saveBackend(state)
return state
})
}
// return state
// })
// }
export const deleteCurrentNode = store => () => {
store.update(state => {
const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId)
state.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent())
? state.hierarchy.children.find(node => node !== state.currentNode)
: nodeToDelete.parent()
// export const deleteCurrentNode = store => () => {
// store.update(state => {
// const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId)
// state.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent())
// ? state.hierarchy.children.find(node => node !== state.currentNode)
// : nodeToDelete.parent()
const isModel = hierarchyFunctions.isModel(nodeToDelete)
// const isModel = hierarchyFunctions.isModel(nodeToDelete)
const check = isModel
? canDeleteModel(nodeToDelete)
: canDeleteIndex(nodeToDelete)
// const check = isModel
// ? canDeleteModel(nodeToDelete)
// : canDeleteIndex(nodeToDelete)
if (!check.canDelete) {
state.errors = check.errors.map(e => ({ error: e }))
return state
}
// if (!check.canDelete) {
// state.errors = check.errors.map(e => ({ error: e }))
// return state
// }
const recordOrIndexKey = isModel ? "children" : "indexes"
// const recordOrIndexKey = isModel ? "children" : "indexes"
// remove the selected record or index
const newCollection = remove(
node => node.nodeId === nodeToDelete.nodeId,
nodeToDelete.parent()[recordOrIndexKey]
)
// // remove the selected record or index
// const newCollection = remove(
// node => node.nodeId === nodeToDelete.nodeId,
// nodeToDelete.parent()[recordOrIndexKey]
// )
nodeToDelete.parent()[recordOrIndexKey] = newCollection
// nodeToDelete.parent()[recordOrIndexKey] = newCollection
state.errors = []
saveBackend(state)
return state
})
}
// state.errors = []
// saveBackend(state)
// return state
// })
// }
export const saveField = store => field => {
store.update(state => {
state.currentNode.fields = state.currentNode.fields.filter(
f => f.id !== field.id
)
// export const saveField = store => field => {
// store.update(state => {
// state.currentNode.fields = state.currentNode.fields.filter(
// f => f.id !== field.id
// )
templateApi(state.hierarchy).addField(state.currentNode, field)
return state
})
}
// templateApi(state.hierarchy).addField(state.currentNode, field)
// return state
// })
// }
export const deleteField = store => field => {
store.update(state => {
state.currentNode.fields = state.currentNode.fields.filter(
f => f.name !== field.name
)
return state
})
}
// export const deleteField = store => field => {
// store.update(state => {
// state.currentNode.fields = state.currentNode.fields.filter(
// f => f.name !== field.name
// )
// return state
// })
// }
const incrementAccessLevelsVersion = state => {
state.accessLevels.version = state.accessLevels.version

View file

@ -43,22 +43,14 @@ export const getStore = () => {
libraries: null,
showSettings: false,
useAnalytics: true,
neoAppId: "84a14e3065c5f15ef8410a5e4c000d68"
appId: "",
clientId: "budibase"
}
const store = writable(initial)
store.setPackage = setPackage(store, initial)
store.newChildModel = backendStoreActions.newModel(store, false)
store.newRootModel = backendStoreActions.newModel(store, true)
store.selectExistingNode = backendStoreActions.selectExistingNode(store)
store.newChildIndex = backendStoreActions.newIndex(store, false)
store.newRootIndex = backendStoreActions.newIndex(store, true)
store.saveCurrentNode = backendStoreActions.saveCurrentNode(store)
store.deleteCurrentNode = backendStoreActions.deleteCurrentNode(store)
store.saveField = backendStoreActions.saveField(store)
store.deleteField = backendStoreActions.deleteField(store)
store.saveLevel = backendStoreActions.saveLevel(store)
store.deleteLevel = backendStoreActions.deleteLevel(store)
store.createDatabaseForApp = backendStoreActions.createDatabaseForApp(store)
@ -102,9 +94,9 @@ export default getStore
const setPackage = (store, initial) => async (pkg) => {
const [main_screens, unauth_screens] = await Promise.all([
api.get(`/_builder/api/${pkg.application.name}/pages/main/screens`).then(r => r.json()),
api.get(`/_builder/api/${pkg.application._id}/pages/main/screens`).then(r => r.json()),
api
.get(`/_builder/api/${pkg.application.name}/pages/unauthenticated/screens`)
.get(`/_builder/api/${pkg.application._id}/pages/unauthenticated/screens`)
.then(r => r.json()),
])
@ -119,12 +111,13 @@ const setPackage = (store, initial) => async (pkg) => {
},
}
initial.libraries = await loadLibs(pkg.application.name, pkg)
initial.libraries = await loadLibs(pkg.application._id, pkg)
initial.loadLibraryUrls = pageName => {
const libs = libUrlsForPreview(pkg, pageName)
return libs
}
initial.appname = pkg.application.name
initial.appId = pkg.application._id
initial.pages = pkg.pages
initial.hasAppPackage = true
initial.hierarchy = pkg.appDefinition.hierarchy
@ -138,15 +131,7 @@ const setPackage = (store, initial) => async (pkg) => {
initial.actions = values(pkg.appDefinition.actions)
initial.triggers = pkg.appDefinition.triggers
initial.appInstances = pkg.application.instances
initial.appId = pkg.application.id
if (!!initial.hierarchy && !isEmpty(initial.hierarchy)) {
initial.hierarchy = constructHierarchy(initial.hierarchy)
const shadowHierarchy = createShadowHierarchy(initial.hierarchy)
if (initial.currentNode !== null) {
initial.currentNode = getNode(shadowHierarchy, initial.currentNode.nodeId)
}
}
initial.appId = pkg.application._id
store.set(initial)
return initial
@ -180,9 +165,6 @@ const importAppDefinition = store => appDefinition => {
})
}
const createShadowHierarchy = hierarchy =>
constructHierarchy(JSON.parse(JSON.stringify(hierarchy)))
const saveScreen = store => screen => {
store.update(s => {
return _saveScreen(store, s, screen)
@ -194,7 +176,7 @@ const _saveScreen = async (store, s, screen) => {
await api
.post(
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`,
screen
)
.then(() => {
@ -227,7 +209,7 @@ const _saveScreen = async (store, s, screen) => {
const _saveScreenApi = (screen, s) =>
api
.post(
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`,
screen
)
.then(() => _savePage(s))
@ -277,7 +259,7 @@ const createGeneratedComponents = store => components => {
const doCreate = async () => {
for (let c of components) {
await api.post(`/_builder/api/${s.appname}/screen`, c)
await api.post(`/_builder/api/${s.appId}/screen`, c)
}
await _savePage(s)
@ -302,7 +284,7 @@ const deleteScreen = store => name => {
s.currentFrontEndType = ""
}
api.delete(`/_builder/api/${s.appname}/screen/${name}`)
api.delete(`/_builder/api/${s.appId}/screen/${name}`)
return s
})
@ -330,12 +312,12 @@ const renameScreen = store => (oldname, newname) => {
const saveAllChanged = async () => {
for (let screenName of changedScreens) {
const changedScreen = getExactComponent(screens, screenName)
await api.post(`/_builder/api/${s.appname}/screen`, changedScreen)
await api.post(`/_builder/api/${s.appId}/screen`, changedScreen)
}
}
api
.patch(`/_builder/api/${s.appname}/screen`, {
.patch(`/_builder/api/${s.appId}/screen`, {
oldname,
newname,
})
@ -362,7 +344,7 @@ const savePage = store => async page => {
const addComponentLibrary = store => async lib => {
const response = await api.get(
`/_builder/api/${s.appname}/componentlibrary?lib=${encodeURI(lib)}`,
`/_builder/api/${s.appId}/componentlibrary?lib=${encodeURI(lib)}`,
undefined,
false
)
@ -419,7 +401,7 @@ const removeStylesheet = store => stylesheet => {
const _savePage = async s => {
const page = s.pages[s.currentPageName]
await api.post(`/_builder/api/${s.appname}/pages/${s.currentPageName}`, {
await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, {
page: { componentLibraries: s.pages.componentLibraries, ...page },
uiFunctions: s.currentPageFunctions,
screens: page._screens,

View file

@ -1,16 +1,17 @@
import api from "builderStore/api"
import { getNewRecord, getNewInstance } from "components/common/core"
export async function createUser(password, user, { appname, instanceId }) {
const CREATE_USER_URL = `/_builder/instance/${appname}/${instanceId}/api/createUser`
const response = await api.post(CREATE_USER_URL, { user, password })
return await response.json()
export async function createUser(user, instanceId) {
const CREATE_USER_URL = `/api/${instanceId}/users`
const response = await api.post(CREATE_USER_URL, user)
const json = await response.json()
return json.user;
}
export async function createDatabase(appname, instanceName) {
const CREATE_DATABASE_URL = `/_builder/instance/_master/0/api/record/`
const database = getNewInstance(appname, instanceName)
const response = await api.post(CREATE_DATABASE_URL, database)
export async function createDatabase(clientId, appname, instanceName) {
const CREATE_DATABASE_URL = `/api/${clientId}/${appname}/instances`
const response = await api.post(CREATE_DATABASE_URL, {
name: instanceName
})
return await response.json()
}

View file

@ -9,8 +9,8 @@
let databaseName
async function createDatabase() {
const response = await api.createDatabase($store.appId, databaseName)
store.createDatabaseForApp(response)
const response = await api.createDatabase($store.clientId, $store.appId, databaseName)
store.createDatabaseForApp(response.instance)
onClosed()
}
</script>

View file

@ -7,23 +7,15 @@
let username
let password
let accessLevels = []
$: valid = username && password && accessLevels.length
$: currentAppInfo = {
appname: $store.appname,
instanceId: $backendUiStore.selectedDatabase.id,
}
$: valid = username && password
$: instanceId = $backendUiStore.selectedDatabase.id
async function createUser() {
const user = {
name: username,
accessLevels,
enabled: true,
temporaryAccessId: "",
}
const response = await api.createUser(password, user, currentAppInfo)
backendUiStore.actions.users.save(user)
const user = { name: username, username, password }
const response = await api.createUser(user, instanceId);
console.log(response);
backendUiStore.actions.users.create(response)
onClosed()
}
</script>
@ -35,11 +27,6 @@
<label class="uk-form-label" for="form-stacked-text">Password</label>
<input class="uk-input" type="password" bind:value={password} />
<label class="uk-form-label" for="form-stacked-text">Access Levels</label>
<select multiple bind:value={accessLevels}>
{#each $store.accessLevels.levels as level}
<option value={level.name}>{level.name}</option>
{/each}
</select>
</div>
<footer>
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>

View file

@ -13,7 +13,7 @@
}
async function deleteDatabase(database) {
const DELETE_DATABASE_URL = `/api/instances/${database.id}`
const DELETE_DATABASE_URL = `/api/instances/${database.name}`
const response = await api.delete(DELETE_DATABASE_URL)
store.update(state => {
state.appInstances = state.appInstances.filter(

View file

@ -10,8 +10,6 @@
return { name, props }
}
let users = []
$: currentAppInfo = {
appname: $store.appname,
instanceId: $backendUiStore.selectedDatabase.id,
@ -20,7 +18,7 @@
async function fetchUsers() {
const FETCH_USERS_URL = `/api/${currentAppInfo.instanceId}/users`
const response = await api.get(FETCH_USERS_URL)
users = await response.json()
const users = await response.json()
backendUiStore.update(state => {
state.users = users
return state
@ -32,7 +30,7 @@
<div class="root">
<ul>
{#each users as user}
{#each $backendUiStore.users as user}
<li>
<i class="ri-user-4-line" />
<button class:active={user.id === $store.currentUserId}>

View file

@ -13,7 +13,7 @@
<div>
<h4 style="margin-bottom: 20px">Choose an Application</h4>
{#each apps as app}
<a href={`/_builder/${app.name}`} class="app-link">{app.name}</a>
<a href={`/_builder/${app._id}`} class="app-link">{app.name}</a>
{/each}
</div>
</div>

View file

@ -13,7 +13,7 @@
let promise = getPackage()
async function getPackage() {
const res = await fetch(`/api/budibase/${$store.neoAppId}/appPackage`)
const res = await fetch(`/api/${$store.clientId}/${application}/appPackage`)
const pkg = await res.json()
if (res.ok) {

View file

@ -1,4 +1,5 @@
<script>
import { store } from "builderStore";
import AppList from "components/start/AppList.svelte"
import { onMount } from "svelte"
import IconButton from "components/common/IconButton.svelte"
@ -7,7 +8,7 @@
let promise = getApps()
async function getApps() {
const res = await fetch(`/api/budibase/applications`)
const res = await fetch(`/api/${$store.clientId}/applications`)
const json = await res.json()
if (res.ok) {

View file

@ -1,43 +1,6 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
node_modules_ubuntu
node_modules_windows
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
.idea
npm-debug.log.*
dist
node_modules/
myapps/
config.js
/builder/*
!/builder/assets/
public/

View file

@ -1,22 +0,0 @@
MIT License
-----------
Copyright (C) 2010-2014 Philipp Dunkel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,78 +0,0 @@
# fsevents [![NPM](https://nodei.co/npm/fsevents.png)](https://nodei.co/npm/fsevents/)
Native access to OS X FSEvents in [Node.js](http://nodejs.org/)
The FSEvents API in OS X allows applications to register for notifications of
changes to a given directory tree. It is a very fast and lightweight alternative
to kqueue.
This is a low-level library. For a cross-compatible file watching module that
uses fsevents, check out [Chokidar](https://www.npmjs.com/package/chokidar).
* [Module Site & GitHub](https://github.com/strongloop/fsevents)
* [NPM Page](https://npmjs.org/package/fsevents)
## Installation
$ npm install fsevents
## Usage
```js
var fsevents = require('fsevents');
var watcher = fsevents(__dirname);
watcher.on('fsevent', function(path, flags, id) { }); // RAW Event as emitted by OS-X
watcher.on('change', function(path, info) { }); // Common Event for all changes
watcher.start() // To start observation
watcher.stop() // To end observation
```
### Events
* *fsevent* - RAW Event as emitted by OS-X
* *change* - Common Event for all changes
* *created* - A File-System-Item has been created
* *deleted* - A File-System-Item has been deleted
* *modified* - A File-System-Item has been modified
* *moved-out* - A File-System-Item has been moved away from this location
* *moved-in* - A File-System-Item has been moved into this location
All events except *fsevent* take an *info* object as the second parameter of the callback. The structure of this object is:
```js
{
"event": "<event-type>",
"id": <eventi-id>,
"path": "<path-that-this-is-about>",
"type": "<file|directory|symlink>",
"changes": {
"inode": true, // Has the iNode Meta-Information changed
"finder": false, // Has the Finder Meta-Data changed
"access": false, // Have the access permissions changed
"xattrs": false // Have the xAttributes changed
},
"flags": <raw-flags>
}
```
## MIT License
Copyright (C) 2010-2014 Philipp Dunkel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,106 +0,0 @@
/*
** © 2014 by Philipp Dunkel <pip@pipobscure.com>
** Licensed under MIT License.
*/
/* jshint node:true */
'use strict';
if (process.platform !== 'darwin')
throw new Error('Module \'fsevents\' is not compatible with platform \'' + process.platform + '\'');
var Native = require("bindings")("fse");
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var inherits = require('util').inherits;
function FSEvents(path, handler) {
EventEmitter.call(this);
Object.defineProperty(this, '_impl', {
value: new Native.FSEvents(String(path || ''), handler),
enumerable: false,
writable: false
});
}
inherits(FSEvents, EventEmitter);
proxies(FSEvents, Native.FSEvents);
module.exports = watch;
module.exports.getInfo = getInfo;
module.exports.FSEvents = Native.FSEvents;
module.exports.Constants = Native.Constants;
var defer = global.setImmediate || process.nextTick;
function watch(path) {
var fse = new FSEvents(String(path || ''), handler);
EventEmitter.call(fse);
return fse;
function handler(path, flags, id) {
defer(function() {
fse.emit('fsevent', path, flags, id);
var info = getInfo(path, flags);
info.id = id;
if (info.event === 'moved') {
fs.stat(info.path, function(err, stat) {
info.event = (err || !stat) ? 'moved-out' : 'moved-in';
fse.emit('change', path, info);
fse.emit(info.event, path, info);
});
} else {
fse.emit('change', path, info);
fse.emit(info.event, path, info);
}
});
}
}
function proxies(ctor, target) {
Object.keys(target.prototype).filter(function(key) {
return typeof target.prototype[key] === 'function';
}).forEach(function(key) {
ctor.prototype[key] = function() {
this._impl[key].apply(this._impl, arguments);
return this;
}
});
}
function getFileType(flags) {
if (Native.Constants.kFSEventStreamEventFlagItemIsFile & flags) return 'file';
if (Native.Constants.kFSEventStreamEventFlagItemIsDir & flags) return 'directory';
if (Native.Constants.kFSEventStreamEventFlagItemIsSymlink & flags) return 'symlink';
}
function getEventType(flags) {
if (Native.Constants.kFSEventStreamEventFlagItemRemoved & flags) return 'deleted';
if (Native.Constants.kFSEventStreamEventFlagItemRenamed & flags) return 'moved';
if (Native.Constants.kFSEventStreamEventFlagItemCreated & flags) return 'created';
if (Native.Constants.kFSEventStreamEventFlagItemModified & flags) return 'modified';
if (Native.Constants.kFSEventStreamEventFlagRootChanged & flags) return 'root-changed';
return 'unknown';
}
function getFileChanges(flags) {
return {
inode: !! (Native.Constants.kFSEventStreamEventFlagItemInodeMetaMod & flags),
finder: !! (Native.Constants.kFSEventStreamEventFlagItemFinderInfoMod & flags),
access: !! (Native.Constants.kFSEventStreamEventFlagItemChangeOwner & flags),
xattrs: !! (Native.Constants.kFSEventStreamEventFlagItemXattrMod & flags)
};
}
function getInfo(path, flags) {
return {
path: path,
event: getEventType(flags),
type: getFileType(flags),
changes: getFileChanges(flags),
flags: flags
};
}

View file

@ -1,36 +0,0 @@
{
"name": "fsevents",
"version": "1.2.12",
"description": "Native Access to Mac OS-X FSEvents",
"main": "fsevents.js",
"dependencies": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
},
"os": [
"darwin"
],
"engines": {
"node": ">= 4.0"
},
"scripts": {
"test": "node ./test/fsevents.js && node ./test/function.js 2> /dev/null"
},
"repository": {
"type": "git",
"url": "https://github.com/strongloop/fsevents.git"
},
"keywords": [
"fsevents",
"mac"
],
"author": "Philipp Dunkel <pip@pipobscure.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/strongloop/fsevents/issues"
},
"bundledDependencies": [
"node-pre-gyp"
],
"homepage": "https://github.com/strongloop/fsevents"
}

Binary file not shown.

View file

@ -0,0 +1 @@
MANIFEST-000004

View file

View file

@ -0,0 +1,5 @@
2020/04/23-10:07:34.635926 70000d912000 Recovering log #3
2020/04/23-10:07:34.636067 70000d912000 Level-0 table #5: started
2020/04/23-10:07:34.636467 70000d912000 Level-0 table #5: 629 bytes OK
2020/04/23-10:07:34.637440 70000d912000 Delete type=0 #3
2020/04/23-10:07:34.637605 70000d912000 Delete type=3 #2

View file

@ -0,0 +1 @@
2020/04/23-10:07:12.630205 70000d6bd000 Delete type=3 #1

Binary file not shown.

View file

@ -0,0 +1 @@
MANIFEST-000002

View file

View file

@ -0,0 +1 @@
2020/04/23-10:07:12.642112 70000dec0000 Delete type=3 #1

Binary file not shown.

View file

@ -1,6 +1,11 @@
const nano = require("nano")
// const nano = require("nano")
const PouchDB = require("pouchdb");
const COUCH_DB_URL =
process.env.COUCH_DB_URL || "http://admin:password@localhost:5984"
module.exports = nano(COUCH_DB_URL)
const CouchDB = PouchDB.defaults({
prefix: COUCH_DB_URL
});
module.exports = CouchDB;

View file

@ -0,0 +1,15 @@
const jwt = require("jsonwebtoken");
module.exports = async (ctx, next) => {
if (!ctx.headers.authorization) ctx.throw(403, "No token provided");
const [_, token] = ctx.headers.authorization.split(" ");
try {
ctx.request.jwtPayload = jwt.verify(token, ctx.config.jwtSecret);
} catch (err) {
ctx.throw(err.status || 403, err.text);
}
await next();
};

View file

@ -1,11 +1,11 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
const {
getPackageForBuilder,
} = require("../../utilities/builder")
exports.fetch = async function(ctx) {
const clientDb = couchdb.db.use(`client-${ctx.params.clientId}`);
const body = await clientDb.view("client", "by_type", {
const clientDb = new CouchDB(`client-${ctx.params.clientId}`);
const body = await clientDb.query("client/by_type", {
include_docs: true,
key: ["app"]
});
@ -14,14 +14,14 @@ exports.fetch = async function(ctx) {
};
exports.fetchAppPackage = async function(ctx) {
const clientDb = couchdb.db.use(`client-${ctx.params.clientId}`);
const clientDb = new CouchDB(`client-${ctx.params.clientId}`);
const application = await clientDb.get(ctx.params.applicationId);
ctx.body = await getPackageForBuilder(ctx.config, application);
}
exports.create = async function(ctx) {
const clientDb = couchdb.db.use(`client-${ctx.params.clientId}`);
const { id, rev } = await clientDb.insert({
const clientDb = new CouchDB(`client-${ctx.params.clientId}`);
const { id, rev } = await clientDb.post({
type: "app",
instances: [],
...ctx.request.body,

View file

@ -1,30 +1,41 @@
const couchdb = require("../../db");
const jwt = require("jsonwebtoken");
const CouchDB = require("../../db");
const bcrypt = require("../../utilities/bcrypt");
const controller = {
forgotPassword: async ctx => {
exports.forgotPassword = async ctx => {
};
},
setPassword: async ctx => {
exports.setPassword = async ctx => { };
},
changePassword: async ctx => {
exports.changePassword = async ctx => {
};
},
authenticate: async ctx => {
// const user = await ctx.master.authenticate(
// ctx.sessionId,
// ctx.params.appname,
// ctx.request.body.username,
// ctx.request.body.password
// )
// if (!user) {
// ctx.throw(StatusCodes.UNAUTHORIZED, "invalid username or password")
// }
exports.authenticate = async ctx => {
const { username, password } = ctx.request.body;
// ctx.body = user.user_json
// ctx.response.status = StatusCodes.OK
if (!username) ctx.throw(400, "Username Required.");
if (!password) ctx.throw(400, "Password Required");
// query couch for their username
const db = new CouchDB(ctx.params.instanceId);
const dbUser = await db.query("database/by_username", {
include_docs: true,
key: username
});
if (await bcrypt.compare(password, dbUser.password)) {
const payload = {
userId: dbUser._id,
accessLevel: "",
instanceId: ctx.params.instanceId
};
const token = jwt.sign(payload, ctx.config.secret, {
expiresIn: "1 day"
});
ctx.body = token;
} else {
ctx.throw(401, "Invalid credentials.");
}
}
module.exports = controller;
}

View file

@ -1,10 +1,11 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
exports.create = async function(ctx) {
const clientId = `client-${ctx.request.body.clientId}`;
await couchdb.db.create(clientId);
const db = new CouchDB(clientId);
await couchdb.db.use(clientId).insert({
await db.put({
_id: "_design/client",
views: {
by_type: {
map: function(doc) {
@ -12,7 +13,7 @@ exports.create = async function(ctx) {
}
}
}
}, '_design/client');
});
ctx.body = {
message: `Client Database ${clientId} successfully provisioned.`
@ -22,7 +23,7 @@ exports.create = async function(ctx) {
exports.destroy = async function(ctx) {
const dbId = `client-${ctx.params.clientId}`;
await couchdb.db.destroy(dbId);
await new CouchDB(dbId).destroy();
ctx.body = {
status: 200,

View file

@ -1,11 +1,13 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
exports.create = async function(ctx) {
const instanceName = ctx.request.body.name;
await couchdb.db.create(instanceName);
// await couchdb.db.create(instanceName);
const { clientId, applicationId } = ctx.params;
await couchdb.db.use(instanceName).insert({
const db = new CouchDB(instanceName);
await db.put({
_id: "_design/database",
metadata: {
clientId,
applicationId
@ -17,35 +19,34 @@ exports.create = async function(ctx) {
}
}
}
}, '_design/database');
});
// Add the new instance under the app clientDB
const clientDatabaseId = `client-${clientId}`
const clientDb = await couchdb.db.use(clientDatabaseId);
const clientDb = new CouchDB(clientDatabaseId);
const budibaseApp = await clientDb.get(applicationId);
budibaseApp.instances.push({
id: instanceName,
name: instanceName
});
await clientDb.insert(budibaseApp, budibaseApp._id);
const instance = { id: instanceName, name: instanceName };
budibaseApp.instances.push(instance);
await clientDb.put(budibaseApp);
ctx.body = {
message: `Instance Database ${instanceName} successfully provisioned.`,
status: 200
status: 200,
instance
}
};
exports.destroy = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId);
const db = new CouchDB(ctx.params.instanceId);
const designDoc = await db.get("_design/database");
await couchdb.db.destroy(ctx.params.instanceId)
await db.destroy();
// remove instance from client application document
const { metadata } = designDoc;
const clientDb = await couchdb.db.use(metadata.clientId);
const clientDb = new CouchDB(metadata.clientId);
const budibaseApp = await clientDb.get(metadata.applicationId);
budibaseApp.instances = budibaseApp.instances.filter(instance => instance !== ctx.params.instanceId);
await clientDb.insert(budibaseApp, budibaseApp._id);
await clientDb.put(budibaseApp);
ctx.body = {
message: `Instance Database ${ctx.params.instanceId} successfully destroyed.`,

View file

@ -1,8 +1,8 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
exports.fetch = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId);
const body = await db.view("database", "by_type", {
const db = new CouchDB(ctx.params.instanceId);
const body = await db.query("database/by_type", {
include_docs: true,
key: ["model"]
});
@ -10,8 +10,8 @@ exports.fetch = async function(ctx) {
}
exports.create = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId);
const newModel = await db.insert({
const db = new CouchDB(ctx.params.instanceId);
const newModel = await db.post({
type: "model",
...ctx.request.body
});
@ -27,7 +27,7 @@ exports.create = async function(ctx) {
}`
}
};
await db.insert(designDoc, designDoc._id);
await db.put(designDoc);
ctx.body = {
message: `Model ${ctx.request.body.name} created successfully.`,
@ -44,21 +44,21 @@ exports.update = async function(ctx) {
}
exports.destroy = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId)
const db = new CouchDB(ctx.params.instanceId)
const model = await db.destroy(ctx.params.modelId, ctx.params.revId);
const model = await db.remove(ctx.params.modelId, ctx.params.revId);
const modelViewId = `all_${model.id}`
// Delete all records for that model
const records = await db.view("database", modelViewId);
await db.bulk({
docs: records.rows.map(record => ({ id: record.id, _deleted: true }))
});
const records = await db.query(`database/${modelViewId}`);
await db.bulkDocs(
records.rows.map(record => ({ id: record.id, _deleted: true }))
);
// delete the "all" view
const designDoc = await db.get("_design/database");
delete designDoc.views[modelViewId];
await db.insert(designDoc, designDoc._id);
await db.put(designDoc);
ctx.body = {
message: `Model ${model.id} deleted.`,

View file

@ -2,10 +2,10 @@ const couchdb = require("../../db")
const {
events,
schemaValidator
} = require("@budibase/common")
} = require("../../../common");
exports.save = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId);
const db = new CouchDB(ctx.params.instanceId);
const record = ctx.request.body;
// validation with ajv
@ -27,7 +27,7 @@ exports.save = async function(ctx) {
const existingRecord = record._id && await db.get(record._id);
if (existingRecord) {
const response = await db.insert(record, existingRecord._id)
const response = await db.put({ ...record, _id: existingRecord._id });
ctx.body = {
message: "Record updated successfully.",
status: 200,
@ -36,7 +36,7 @@ exports.save = async function(ctx) {
return;
}
const response = await db.insert({
const response = await db.post({
modelId: ctx.params.modelId,
type: "record",
...record
@ -54,10 +54,9 @@ exports.save = async function(ctx) {
}
exports.fetch = async function(ctx) {
const db = couchdb.db.use(ctx.params.instanceId)
const response = await db.view(
"database",
ctx.params.viewName,
const db = new CouchDB(ctx.params.instanceId)
const response = await db.query(
`database/${ctx.params.viewName}`,
{
include_docs: true
}
@ -76,6 +75,6 @@ exports.find = async function(ctx) {
exports.destroy = async function(ctx) {
const databaseId = ctx.params.instanceId;
const db = couchdb.db.use(databaseId)
const db = new CouchDB(databaseId)
ctx.body = await db.destroy(ctx.params.recordId, ctx.params.revId);
};

View file

@ -1,8 +1,9 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
const bcrypt = require("../../utilities/bcrypt");
exports.fetch = async function(ctx) {
const database = couchdb.db.use(ctx.params.instanceId);
const data = await database.view("database", "by_type", {
const database = new CouchDB(ctx.params.instanceId);
const data = await database.query("database/by_type", {
include_docs: true,
key: ["user"]
});
@ -11,20 +12,32 @@ exports.fetch = async function(ctx) {
};
exports.create = async function(ctx) {
const database = couchdb.db.use(ctx.params.instanceId);
const response = await database.insert({
...ctx.request.body,
const database = new CouchDB(ctx.params.instanceId);
const { username, password, name } = ctx.request.body;
if (!username || !password) ctx.throw(400, "Username and Password Required.");
const response = await database.post({
username,
password: await bcrypt.hash(password),
name,
type: "user"
});
ctx.body = {
...response,
user: {
id: response.id,
rev: response.rev,
username,
name
},
message: `User created successfully.`,
status: 200
}
};
exports.destroy = async function(ctx) {
const database = couchdb.db.use(ctx.params.instanceId);
const database = new CouchDB(ctx.params.instanceId);
const response = await database.destroy(ctx.params.userId)
ctx.body = {
...response,

View file

@ -1,13 +1,13 @@
const couchdb = require("../../db");
const CouchDB = require("../../db");
const controller = {
fetch: async ctx => {
const db = couchdb.db.use(ctx.params.instanceId);
const db = new CouchDB(ctx.params.instanceId);
const designDoc = await db.get("_design/database");
ctx.body = designDoc.views;
},
create: async ctx => {
const db = couchdb.db.use(ctx.params.instanceId);
const db = new CouchDB(ctx.params.instanceId);
const { name, ...viewDefinition } = ctx.request.body;
const designDoc = await db.get("_design/database");
@ -15,7 +15,7 @@ const controller = {
...designDoc.views,
[name]: viewDefinition
};
const newView = await db.insert(designDoc, designDoc._id);
const newView = await db.put(designDoc);
ctx.body = {
...newView,
@ -24,8 +24,8 @@ const controller = {
}
},
destroy: async ctx => {
const db = couchdb.db.use(ctx.params.instanceId);
ctx.body = await database.destroy(ctx.params.userId)
const db = new CouchDB(ctx.params.instanceId);
ctx.body = await db.destroy(ctx.params.userId)
}
}

View file

@ -14,13 +14,13 @@ router.get("/_builder/api/apps", async ctx => {
ctx.response.status = StatusCodes.OK
})
router.get("/_builder/api/:appname/appPackage", async ctx => {
const application = await ctx.master.getApplicationWithInstances(
ctx.params.appname
)
ctx.body = await getPackageForBuilder(ctx.config, application)
ctx.response.status = StatusCodes.OK
})
// router.get("/_builder/api/:appname/appPackage", async ctx => {
// const application = await ctx.master.getApplicationWithInstances(
// ctx.params.appname
// )
// ctx.body = await getPackageForBuilder(ctx.config, application)
// ctx.response.status = StatusCodes.OK
// })
router
.post("/_builder/api/:appname/backend", async ctx => {

View file

@ -2,7 +2,6 @@ const supertest = require("supertest");
const app = require("../../../../app");
const {
createInstanceDatabase,
insertDocument,
destroyDatabase
} = require("./couchTestUtils");

View file

@ -19,10 +19,11 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/client": "^0.0.32",
"@budibase/common": "0.0.32",
"@budibase/core": "^0.0.32",
"@koa/router": "^8.0.0",
"bcryptjs": "^2.4.3",
"fs-extra": "^8.1.0",
"jsonwebtoken": "^8.5.1",
"koa": "^2.7.0",
"koa-body": "^4.1.0",
"koa-logger": "^3.2.1",
@ -30,7 +31,7 @@
"koa-session": "^5.12.0",
"koa-static": "^5.0.0",
"lodash": "^4.17.13",
"nano": "^8.2.2",
"pouchdb": "^7.2.1",
"squirrelly": "^7.5.0",
"tar-fs": "^2.0.0",
"uuid": "^3.3.2",

View file

@ -0,0 +1,11 @@
const bcrypt = require("bcryptjs");
const SALT_ROUNDS = process.env.SALT_ROUNDS || 10;
exports.hash = async data => {
const salt = await bcrypt.genSalt(SALT_ROUNDS);
const result = await bcrypt.hash(data, salt);
return result;
};
exports.compare = async (data, encrypted) => await bcrypt.compare(data, encrypted);

View file

@ -28,7 +28,7 @@ const getAppDefinition = async appPath =>
await readJSON(`${appPath}/appDefinition.json`)
module.exports.getPackageForBuilder = async (config, application) => {
const appPath = resolve(process.cwd(), config.latestPackagesFolder, application.name);
const appPath = resolve(process.cwd(), config.latestPackagesFolder, application._id);
const pages = await getPages(appPath)

View file

@ -336,6 +336,7 @@ module.exports = async context => {
const getApplicationWithInstances = async appname => {
const app = cloneDeep(await getApplication(appname))
app.instances = await bb.indexApi.listItems(
`/applications/${app.id}/allinstances`
)

File diff suppressed because it is too large Load diff