mirror of https://github.com/Budibase/budibase.git
126 changed files with 1856 additions and 1940 deletions
@ -0,0 +1,3 @@ |
|||
module.exports = { |
|||
user: require("./src/cache/user"), |
|||
} |
|||
@ -0,0 +1 @@ |
|||
module.exports = require("./src/security/sessions") |
|||
@ -0,0 +1,21 @@ |
|||
const { getDB } = require("../db") |
|||
const { StaticDatabases } = require("../db/utils") |
|||
const redis = require("../redis/authRedis") |
|||
|
|||
const EXPIRY_SECONDS = 3600 |
|||
|
|||
exports.getUser = async userId => { |
|||
const client = await redis.getUserClient() |
|||
// try cache
|
|||
let user = await client.get(userId) |
|||
if (!user) { |
|||
user = await getDB(StaticDatabases.GLOBAL.name).get(userId) |
|||
client.store(userId, user, EXPIRY_SECONDS) |
|||
} |
|||
return user |
|||
} |
|||
|
|||
exports.invalidateUser = async userId => { |
|||
const client = await redis.getUserClient() |
|||
await client.delete(userId) |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
const Client = require("./index") |
|||
const utils = require("./utils") |
|||
|
|||
let userClient, sessionClient |
|||
|
|||
async function init() { |
|||
userClient = await new Client(utils.Databases.USER_CACHE).init() |
|||
sessionClient = await new Client(utils.Databases.SESSIONS).init() |
|||
} |
|||
|
|||
process.on("exit", async () => { |
|||
if (userClient) await userClient.finish() |
|||
if (sessionClient) await sessionClient.finish() |
|||
}) |
|||
|
|||
module.exports = { |
|||
getUserClient: async () => { |
|||
if (!userClient) { |
|||
await init() |
|||
} |
|||
return userClient |
|||
}, |
|||
getSessionClient: async () => { |
|||
if (!sessionClient) { |
|||
await init() |
|||
} |
|||
return sessionClient |
|||
}, |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
const redis = require("../redis/authRedis") |
|||
|
|||
const EXPIRY_SECONDS = 86400 |
|||
|
|||
async function getSessionsForUser(userId) { |
|||
const client = await redis.getSessionClient() |
|||
const sessions = await client.scan(userId) |
|||
return sessions.map(session => session.value) |
|||
} |
|||
|
|||
function makeSessionID(userId, sessionId) { |
|||
return `${userId}/${sessionId}` |
|||
} |
|||
|
|||
exports.createASession = async (userId, sessionId) => { |
|||
const client = await redis.getSessionClient() |
|||
const session = { |
|||
createdAt: new Date().toISOString(), |
|||
lastAccessedAt: new Date().toISOString(), |
|||
sessionId, |
|||
userId, |
|||
} |
|||
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS) |
|||
} |
|||
|
|||
exports.invalidateSessions = async (userId, sessionId = null) => { |
|||
let sessions = [] |
|||
if (sessionId) { |
|||
sessions.push({ key: makeSessionID(userId, sessionId) }) |
|||
} else { |
|||
sessions = await getSessionsForUser(userId) |
|||
} |
|||
const client = await redis.getSessionClient() |
|||
const promises = [] |
|||
for (let session of sessions) { |
|||
promises.push(client.delete(session.key)) |
|||
} |
|||
await Promise.all(promises) |
|||
} |
|||
|
|||
exports.updateSessionTTL = async session => { |
|||
const client = await redis.getSessionClient() |
|||
const key = makeSessionID(session.userId, session.sessionId) |
|||
session.lastAccessedAt = new Date().toISOString() |
|||
await client.store(key, session, EXPIRY_SECONDS) |
|||
} |
|||
|
|||
exports.endSession = async (userId, sessionId) => { |
|||
const client = await redis.getSessionClient() |
|||
await client.delete(makeSessionID(userId, sessionId)) |
|||
} |
|||
|
|||
exports.getUserSessions = getSessionsForUser |
|||
|
|||
exports.getSession = async (userId, sessionId) => { |
|||
try { |
|||
const client = await redis.getSessionClient() |
|||
return client.get(makeSessionID(userId, sessionId)) |
|||
} catch (err) { |
|||
// if can't get session don't error, just don't return anything
|
|||
return null |
|||
} |
|||
} |
|||
|
|||
exports.getAllSessions = async () => { |
|||
const client = await redis.getSessionClient() |
|||
const sessions = await client.scan() |
|||
return sessions.map(session => session.value) |
|||
} |
|||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,97 @@ |
|||
<script> |
|||
import { Icon, Combobox, Drawer, Button } from "@budibase/bbui" |
|||
import { |
|||
readableToRuntimeBinding, |
|||
runtimeToReadableBinding, |
|||
} from "builderStore/dataBinding" |
|||
import BindingPanel from "components/common/bindings/BindingPanel.svelte" |
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
export let panel = BindingPanel |
|||
export let value = "" |
|||
export let bindings = [] |
|||
export let title = "Bindings" |
|||
export let placeholder |
|||
export let label |
|||
export let disabled = false |
|||
export let options |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
let bindingDrawer |
|||
$: tempValue = Array.isArray(value) ? value : [] |
|||
$: readableValue = runtimeToReadableBinding(bindings, value) |
|||
|
|||
const handleClose = () => { |
|||
onChange(tempValue) |
|||
bindingDrawer.hide() |
|||
} |
|||
|
|||
const onChange = value => { |
|||
dispatch("change", readableToRuntimeBinding(bindings, value)) |
|||
} |
|||
</script> |
|||
|
|||
<div class="control"> |
|||
<Combobox |
|||
{label} |
|||
{disabled} |
|||
value={readableValue} |
|||
on:change={event => onChange(event.detail)} |
|||
{placeholder} |
|||
{options} |
|||
/> |
|||
{#if !disabled} |
|||
<div class="icon" on:click={bindingDrawer.show}> |
|||
<Icon size="S" name="FlashOn" /> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
<Drawer bind:this={bindingDrawer} {title}> |
|||
<svelte:fragment slot="description"> |
|||
Add the objects on the left to enrich your text. |
|||
</svelte:fragment> |
|||
<Button cta slot="buttons" on:click={handleClose}>Save</Button> |
|||
<svelte:component |
|||
this={panel} |
|||
slot="body" |
|||
value={readableValue} |
|||
close={handleClose} |
|||
on:update={event => (tempValue = event.detail)} |
|||
bindableProperties={bindings} |
|||
/> |
|||
</Drawer> |
|||
|
|||
<style> |
|||
.control { |
|||
flex: 1; |
|||
position: relative; |
|||
} |
|||
|
|||
.icon { |
|||
right: 31px; |
|||
bottom: 1px; |
|||
position: absolute; |
|||
justify-content: center; |
|||
align-items: center; |
|||
display: flex; |
|||
flex-direction: row; |
|||
box-sizing: border-box; |
|||
border-left: 1px solid var(--spectrum-alias-border-color); |
|||
border-right: 1px solid var(--spectrum-alias-border-color); |
|||
width: 31px; |
|||
color: var(--spectrum-alias-text-color); |
|||
background-color: var(--spectrum-global-color-gray-75); |
|||
transition: background-color |
|||
var(--spectrum-global-animation-duration-100, 130ms), |
|||
box-shadow var(--spectrum-global-animation-duration-100, 130ms), |
|||
border-color var(--spectrum-global-animation-duration-100, 130ms); |
|||
height: calc(var(--spectrum-alias-item-height-m) - 2px); |
|||
} |
|||
|
|||
.icon:hover { |
|||
cursor: pointer; |
|||
color: var(--spectrum-alias-text-color-hover); |
|||
background-color: var(--spectrum-global-color-gray-50); |
|||
border-color: var(--spectrum-alias-border-color-hover); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,38 @@ |
|||
<script> |
|||
import { Select } from "@budibase/bbui" |
|||
import { store } from "builderStore" |
|||
|
|||
const themeOptions = [ |
|||
{ |
|||
label: "Lightest", |
|||
value: "spectrum--lightest", |
|||
}, |
|||
{ |
|||
label: "Light", |
|||
value: "spectrum--light", |
|||
}, |
|||
{ |
|||
label: "Dark", |
|||
value: "spectrum--dark", |
|||
}, |
|||
{ |
|||
label: "Darkest", |
|||
value: "spectrum--darkest", |
|||
}, |
|||
] |
|||
</script> |
|||
|
|||
<div> |
|||
<Select |
|||
value={$store.theme || "spectrum--light"} |
|||
options={themeOptions} |
|||
placeholder={null} |
|||
on:change={e => store.actions.theme.save(e.detail)} |
|||
/> |
|||
</div> |
|||
|
|||
<style> |
|||
div { |
|||
padding-right: 8px; |
|||
} |
|||
</style> |
|||
@ -1,42 +1,8 @@ |
|||
<script> |
|||
import { createEventDispatcher } from "svelte" |
|||
import Colorpicker from "@budibase/colorpicker" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
import { ColorPicker } from "@budibase/bbui" |
|||
import { store } from "builderStore" |
|||
|
|||
export let value |
|||
|
|||
const WAIT = 150 |
|||
|
|||
function throttle(callback, wait, immediate = false) { |
|||
let timeout = null |
|||
let initialCall = true |
|||
|
|||
return function () { |
|||
const callNow = immediate && initialCall |
|||
const next = () => { |
|||
callback.apply(this, arguments) |
|||
timeout = null |
|||
} |
|||
|
|||
if (callNow) { |
|||
initialCall = false |
|||
next() |
|||
} |
|||
|
|||
if (!timeout) { |
|||
timeout = setTimeout(next, wait) |
|||
} |
|||
} |
|||
} |
|||
|
|||
const onChange = throttle( |
|||
e => { |
|||
dispatch("change", e.detail) |
|||
}, |
|||
WAIT, |
|||
true |
|||
) |
|||
</script> |
|||
|
|||
<Colorpicker value={value || "#C4C4C4"} on:change={onChange} /> |
|||
<ColorPicker {value} on:change spectrumTheme={$store.theme} /> |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
<script> |
|||
import { store } from "builderStore" |
|||
import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCombobox.svelte" |
|||
|
|||
export let value |
|||
|
|||
$: urlOptions = $store.screens |
|||
.map(screen => screen.routing?.route) |
|||
.filter(x => x != null) |
|||
</script> |
|||
|
|||
<DrawerBindableCombobox {value} on:change options={urlOptions} /> |
|||
@ -0,0 +1,27 @@ |
|||
import * as API from "../api" |
|||
import { get, writable } from "svelte/store" |
|||
|
|||
const createAppStore = () => { |
|||
const store = writable({}) |
|||
|
|||
// Fetches the app definition including screens, layouts and theme
|
|||
const fetchAppDefinition = async () => { |
|||
const appDefinition = await API.fetchAppPackage(get(store).appId) |
|||
store.set(appDefinition) |
|||
} |
|||
|
|||
// Sets the initial app ID
|
|||
const setAppID = id => { |
|||
store.update(state => { |
|||
state.appId = id |
|||
return state |
|||
}) |
|||
} |
|||
|
|||
return { |
|||
subscribe: store.subscribe, |
|||
actions: { setAppID, fetchAppDefinition }, |
|||
} |
|||
} |
|||
|
|||
export const appStore = createAppStore() |
|||
@ -1,7 +1,7 @@ |
|||
import { routeStore } from "./routes" |
|||
import { screenStore } from "./screens" |
|||
import { appStore } from "./app" |
|||
|
|||
export async function initialise() { |
|||
await routeStore.actions.fetchRoutes() |
|||
await screenStore.actions.fetchScreens() |
|||
await appStore.actions.fetchAppDefinition() |
|||
} |
|||
|
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"watch": ["src"], |
|||
"ext": "js,ts,json", |
|||
"ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"], |
|||
"exec": "ts-node src/index.ts" |
|||
} |
|||
@ -1,9 +1,22 @@ |
|||
CREATE DATABASE IF NOT EXISTS main; |
|||
USE main; |
|||
CREATE TABLE Persons ( |
|||
PersonID int NOT NULL PRIMARY KEY, |
|||
PersonID int NOT NULL AUTO_INCREMENT, |
|||
LastName varchar(255), |
|||
FirstName varchar(255), |
|||
Address varchar(255), |
|||
City varchar(255) |
|||
City varchar(255), |
|||
PRIMARY KEY (PersonID) |
|||
); |
|||
CREATE TABLE Tasks ( |
|||
TaskID int NOT NULL AUTO_INCREMENT, |
|||
PersonID INT, |
|||
TaskName varchar(255), |
|||
PRIMARY KEY (TaskID), |
|||
CONSTRAINT fkPersons |
|||
FOREIGN KEY(PersonID) |
|||
REFERENCES Persons(PersonID) |
|||
); |
|||
INSERT INTO Persons (FirstName, LastName, Address, City) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast'); |
|||
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'assembling'); |
|||
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'processing'); |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
#!/bin/bash |
|||
docker-compose down |
|||
docker volume prune -f |
|||
File diff suppressed because it is too large
@ -1,21 +1,34 @@ |
|||
<script> |
|||
import { getContext } from "svelte" |
|||
import Placeholder from "./Placeholder.svelte" |
|||
|
|||
const { styleable } = getContext("sdk") |
|||
const { styleable, builderStore } = getContext("sdk") |
|||
const component = getContext("component") |
|||
|
|||
export let icon = "" |
|||
export let size = "fa-lg" |
|||
export let color = "#f00" |
|||
export let icon |
|||
export let size |
|||
export let color |
|||
export let onClick |
|||
|
|||
$: styles = { |
|||
...$component.styles, |
|||
normal: { |
|||
...$component.styles.normal, |
|||
color, |
|||
color: color || "var(--spectrum-global-color-gray-900)", |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<i use:styleable={styles} class="{icon} {size}" on:click={onClick} /> |
|||
{#if icon} |
|||
<i use:styleable={styles} class="{icon} {size}" on:click={onClick} /> |
|||
{:else if $builderStore.inBuilder} |
|||
<div use:styleable={styles}> |
|||
<Placeholder /> |
|||
</div> |
|||
{/if} |
|||
|
|||
<style> |
|||
div { |
|||
font-style: italic; |
|||
} |
|||
</style> |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue