Merge branch 'develop' of github.com:Budibase/budibase into lab-day/refactor-app-db
This commit is contained in:
commit
476f34fb93
|
@ -108,6 +108,8 @@ spec:
|
||||||
value: {{ .Values.globals.accountPortalApiKey | quote }}
|
value: {{ .Values.globals.accountPortalApiKey | quote }}
|
||||||
- name: COOKIE_DOMAIN
|
- name: COOKIE_DOMAIN
|
||||||
value: {{ .Values.globals.cookieDomain | quote }}
|
value: {{ .Values.globals.cookieDomain | quote }}
|
||||||
|
- name: HTTP_MIGRATIONS
|
||||||
|
value: {{ .Values.globals.httpMigrations | quote }}
|
||||||
image: budibase/apps:{{ .Values.globals.appVersion }}
|
image: budibase/apps:{{ .Values.globals.appVersion }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: bbapps
|
name: bbapps
|
||||||
|
|
|
@ -99,6 +99,7 @@ globals:
|
||||||
accountPortalApiKey: ""
|
accountPortalApiKey: ""
|
||||||
cookieDomain: ""
|
cookieDomain: ""
|
||||||
platformUrl: ""
|
platformUrl: ""
|
||||||
|
httpMigrations: "0"
|
||||||
|
|
||||||
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -2,4 +2,5 @@ module.exports = {
|
||||||
...require("./src/db/utils"),
|
...require("./src/db/utils"),
|
||||||
...require("./src/db/constants"),
|
...require("./src/db/constants"),
|
||||||
...require("./src/db"),
|
...require("./src/db"),
|
||||||
|
...require("./src/db/views"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -13,6 +13,7 @@ const {
|
||||||
appTenancy,
|
appTenancy,
|
||||||
authError,
|
authError,
|
||||||
csrf,
|
csrf,
|
||||||
|
internalApi,
|
||||||
} = require("./middleware")
|
} = require("./middleware")
|
||||||
|
|
||||||
// Strategies
|
// Strategies
|
||||||
|
@ -44,4 +45,5 @@ module.exports = {
|
||||||
auditLog,
|
auditLog,
|
||||||
authError,
|
authError,
|
||||||
buildCsrfMiddleware: csrf,
|
buildCsrfMiddleware: csrf,
|
||||||
|
internalApi,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ const authenticated = require("./authenticated")
|
||||||
const auditLog = require("./auditLog")
|
const auditLog = require("./auditLog")
|
||||||
const tenancy = require("./tenancy")
|
const tenancy = require("./tenancy")
|
||||||
const appTenancy = require("./appTenancy")
|
const appTenancy = require("./appTenancy")
|
||||||
|
const internalApi = require("./internalApi")
|
||||||
const datasourceGoogle = require("./passport/datasource/google")
|
const datasourceGoogle = require("./passport/datasource/google")
|
||||||
const csrf = require("./csrf")
|
const csrf = require("./csrf")
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ module.exports = {
|
||||||
tenancy,
|
tenancy,
|
||||||
appTenancy,
|
appTenancy,
|
||||||
authError,
|
authError,
|
||||||
|
internalApi,
|
||||||
datasource: {
|
datasource: {
|
||||||
google: datasourceGoogle,
|
google: datasourceGoogle,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const env = require("../environment")
|
||||||
|
const { Headers } = require("../constants")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Key only endpoint.
|
||||||
|
*/
|
||||||
|
module.exports = async (ctx, next) => {
|
||||||
|
const apiKey = ctx.request.headers[Headers.API_KEY]
|
||||||
|
if (apiKey !== env.INTERNAL_API_KEY) {
|
||||||
|
ctx.throw(403, "Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
|
@ -1,20 +1,17 @@
|
||||||
|
const { DEFAULT_TENANT_ID } = require("../constants")
|
||||||
const { DocumentTypes } = require("../db/constants")
|
const { DocumentTypes } = require("../db/constants")
|
||||||
const { getGlobalDB, getTenantId } = require("../tenancy")
|
const { getAllApps } = require("../db/utils")
|
||||||
|
const environment = require("../environment")
|
||||||
|
const {
|
||||||
|
doInTenant,
|
||||||
|
getTenantIds,
|
||||||
|
getGlobalDBName,
|
||||||
|
getTenantId,
|
||||||
|
} = require("../tenancy")
|
||||||
|
|
||||||
exports.MIGRATION_DBS = {
|
exports.MIGRATION_TYPES = {
|
||||||
GLOBAL_DB: "GLOBAL_DB",
|
GLOBAL: "global", // run once, recorded in global db, global db is provided as an argument
|
||||||
}
|
APP: "app", // run per app, recorded in each app db, app db is provided as an argument
|
||||||
|
|
||||||
exports.MIGRATIONS = {
|
|
||||||
USER_EMAIL_VIEW_CASING: "user_email_view_casing",
|
|
||||||
QUOTAS_1: "quotas_1",
|
|
||||||
}
|
|
||||||
|
|
||||||
const DB_LOOKUP = {
|
|
||||||
[exports.MIGRATION_DBS.GLOBAL_DB]: [
|
|
||||||
exports.MIGRATIONS.USER_EMAIL_VIEW_CASING,
|
|
||||||
exports.MIGRATIONS.QUOTAS_1,
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getMigrationsDoc = async db => {
|
exports.getMigrationsDoc = async db => {
|
||||||
|
@ -28,40 +25,90 @@ exports.getMigrationsDoc = async db => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.migrateIfRequired = async (migrationDb, migrationName, migrateFn) => {
|
const runMigration = async (CouchDB, migration, options = {}) => {
|
||||||
const tenantId = getTenantId()
|
const tenantId = getTenantId()
|
||||||
try {
|
const migrationType = migration.type
|
||||||
let db
|
const migrationName = migration.name
|
||||||
if (migrationDb === exports.MIGRATION_DBS.GLOBAL_DB) {
|
|
||||||
db = getGlobalDB()
|
|
||||||
} else {
|
|
||||||
throw new Error(`Unrecognised migration db [${migrationDb}]`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DB_LOOKUP[migrationDb].includes(migrationName)) {
|
// get the db to store the migration in
|
||||||
throw new Error(
|
let dbNames
|
||||||
`Unrecognised migration name [${migrationName}] for db [${migrationDb}]`
|
if (migrationType === exports.MIGRATION_TYPES.GLOBAL) {
|
||||||
)
|
dbNames = [getGlobalDBName()]
|
||||||
}
|
} else if (migrationType === exports.MIGRATION_TYPES.APP) {
|
||||||
|
const apps = await getAllApps(CouchDB, migration.opts)
|
||||||
const doc = await exports.getMigrationsDoc(db)
|
dbNames = apps.map(app => app.appId)
|
||||||
// exit if the migration has been performed
|
} else {
|
||||||
if (doc[migrationName]) {
|
throw new Error(
|
||||||
return
|
`[Tenant: ${tenantId}] Unrecognised migration type [${migrationType}]`
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[Tenant: ${tenantId}] Performing migration: ${migrationName}`)
|
|
||||||
await migrateFn()
|
|
||||||
console.log(`[Tenant: ${tenantId}] Migration complete: ${migrationName}`)
|
|
||||||
|
|
||||||
// mark as complete
|
|
||||||
doc[migrationName] = Date.now()
|
|
||||||
await db.put(doc)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(
|
|
||||||
`[Tenant: ${tenantId}] Error performing migration: ${migrationName}: `,
|
|
||||||
err
|
|
||||||
)
|
)
|
||||||
throw err
|
}
|
||||||
|
|
||||||
|
// run the migration against each db
|
||||||
|
for (const dbName of dbNames) {
|
||||||
|
const db = new CouchDB(dbName)
|
||||||
|
try {
|
||||||
|
const doc = await exports.getMigrationsDoc(db)
|
||||||
|
|
||||||
|
// exit if the migration has been performed already
|
||||||
|
if (doc[migrationName]) {
|
||||||
|
if (
|
||||||
|
options.force &&
|
||||||
|
options.force[migrationType] &&
|
||||||
|
options.force[migrationType].includes(migrationName)
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// the migration has already been performed
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running`
|
||||||
|
)
|
||||||
|
// run the migration with tenant context
|
||||||
|
await migration.fn(db)
|
||||||
|
console.log(
|
||||||
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete`
|
||||||
|
)
|
||||||
|
|
||||||
|
// mark as complete
|
||||||
|
doc[migrationName] = Date.now()
|
||||||
|
await db.put(doc)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Error: `,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.runMigrations = async (CouchDB, migrations, options = {}) => {
|
||||||
|
console.log("Running migrations")
|
||||||
|
let tenantIds
|
||||||
|
if (environment.MULTI_TENANCY) {
|
||||||
|
if (!options.tenantIds || !options.tenantIds.length) {
|
||||||
|
// run for all tenants
|
||||||
|
tenantIds = await getTenantIds()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// single tenancy
|
||||||
|
tenantIds = [DEFAULT_TENANT_ID]
|
||||||
|
}
|
||||||
|
|
||||||
|
// for all tenants
|
||||||
|
for (const tenantId of tenantIds) {
|
||||||
|
// for all migrations
|
||||||
|
for (const migration of migrations) {
|
||||||
|
// run the migration
|
||||||
|
await doInTenant(tenantId, () =>
|
||||||
|
runMigration(CouchDB, migration, options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Migrations complete")
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
exports[`migrations should match snapshot 1`] = `
|
exports[`migrations should match snapshot 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"_id": "migrations",
|
"_id": "migrations",
|
||||||
"_rev": "1-af6c272fe081efafecd2ea49a8fcbb40",
|
"_rev": "1-6277abc4e3db950221768e5a2618a059",
|
||||||
"user_email_view_casing": 1487076708000,
|
"test": 1487076708000,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require("../../tests/utilities/dbConfig")
|
require("../../tests/utilities/dbConfig")
|
||||||
|
|
||||||
const { migrateIfRequired, MIGRATION_DBS, MIGRATIONS, getMigrationsDoc } = require("../index")
|
const { runMigrations, getMigrationsDoc } = require("../index")
|
||||||
const database = require("../../db")
|
const CouchDB = require("../../db").getCouch()
|
||||||
const {
|
const {
|
||||||
StaticDatabases,
|
StaticDatabases,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
|
@ -13,8 +13,14 @@ describe("migrations", () => {
|
||||||
|
|
||||||
const migrationFunction = jest.fn()
|
const migrationFunction = jest.fn()
|
||||||
|
|
||||||
|
const MIGRATIONS = [{
|
||||||
|
type: "global",
|
||||||
|
name: "test",
|
||||||
|
fn: migrationFunction
|
||||||
|
}]
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
db = database.getDB(StaticDatabases.GLOBAL.name)
|
db = new CouchDB(StaticDatabases.GLOBAL.name)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -22,39 +28,29 @@ describe("migrations", () => {
|
||||||
await db.destroy()
|
await db.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
const validMigration = () => {
|
const migrate = () => {
|
||||||
return migrateIfRequired(MIGRATION_DBS.GLOBAL_DB, MIGRATIONS.USER_EMAIL_VIEW_CASING, migrationFunction)
|
return runMigrations(CouchDB, MIGRATIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should run a new migration", async () => {
|
it("should run a new migration", async () => {
|
||||||
await validMigration()
|
await migrate()
|
||||||
expect(migrationFunction).toHaveBeenCalled()
|
expect(migrationFunction).toHaveBeenCalled()
|
||||||
|
const doc = await getMigrationsDoc(db)
|
||||||
|
expect(doc.test).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should match snapshot", async () => {
|
it("should match snapshot", async () => {
|
||||||
await validMigration()
|
await migrate()
|
||||||
const doc = await getMigrationsDoc(db)
|
const doc = await getMigrationsDoc(db)
|
||||||
expect(doc).toMatchSnapshot()
|
expect(doc).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should skip a previously run migration", async () => {
|
it("should skip a previously run migration", async () => {
|
||||||
await validMigration()
|
await migrate()
|
||||||
await validMigration()
|
const previousMigrationTime = await getMigrationsDoc(db).test
|
||||||
|
await migrate()
|
||||||
|
const currentMigrationTime = await getMigrationsDoc(db).test
|
||||||
expect(migrationFunction).toHaveBeenCalledTimes(1)
|
expect(migrationFunction).toHaveBeenCalledTimes(1)
|
||||||
|
expect(currentMigrationTime).toBe(previousMigrationTime)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should reject an unknown migration name", async () => {
|
|
||||||
expect(async () => {
|
|
||||||
await migrateIfRequired(MIGRATION_DBS.GLOBAL_DB, "bogus_name", migrationFunction)
|
|
||||||
}).rejects.toThrow()
|
|
||||||
expect(migrationFunction).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should reject an unknown database name", async () => {
|
|
||||||
expect(async () => {
|
|
||||||
await migrateIfRequired("bogus_db", MIGRATIONS.USER_EMAIL_VIEW_CASING, migrationFunction)
|
|
||||||
}).rejects.toThrow()
|
|
||||||
expect(migrationFunction).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
|
@ -148,3 +148,15 @@ exports.isUserInAppTenant = (appId, user = null) => {
|
||||||
const tenantId = exports.getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
const tenantId = exports.getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
||||||
return tenantId === userTenantId
|
return tenantId === userTenantId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getTenantIds = async () => {
|
||||||
|
const db = getDB(PLATFORM_INFO_DB)
|
||||||
|
let tenants
|
||||||
|
try {
|
||||||
|
tenants = await db.get(TENANT_DOC)
|
||||||
|
} catch (err) {
|
||||||
|
// if theres an error the doc doesn't exist, no tenants exist
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return (tenants && tenants.tenantIds) || []
|
||||||
|
}
|
||||||
|
|
|
@ -20,9 +20,6 @@ const { hash } = require("./hashing")
|
||||||
const userCache = require("./cache/user")
|
const userCache = require("./cache/user")
|
||||||
const env = require("./environment")
|
const env = require("./environment")
|
||||||
const { getUserSessions, invalidateSessions } = require("./security/sessions")
|
const { getUserSessions, invalidateSessions } = require("./security/sessions")
|
||||||
const { migrateIfRequired } = require("./migrations")
|
|
||||||
const { USER_EMAIL_VIEW_CASING } = require("./migrations").MIGRATIONS
|
|
||||||
const { GLOBAL_DB } = require("./migrations").MIGRATION_DBS
|
|
||||||
|
|
||||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
|
|
||||||
|
@ -144,11 +141,6 @@ exports.getGlobalUserByEmail = async email => {
|
||||||
}
|
}
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
|
|
||||||
await migrateIfRequired(GLOBAL_DB, USER_EMAIL_VIEW_CASING, async () => {
|
|
||||||
// re-create the view with latest changes
|
|
||||||
await createUserEmailView(db)
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let users = (
|
let users = (
|
||||||
await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
|
await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
|
||||||
|
|
|
@ -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": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -66,10 +66,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.49-alpha.1",
|
"@budibase/bbui": "^1.0.49-alpha.2",
|
||||||
"@budibase/client": "^1.0.49-alpha.1",
|
"@budibase/client": "^1.0.49-alpha.2",
|
||||||
"@budibase/colorpicker": "1.1.2",
|
"@budibase/colorpicker": "1.1.2",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.1",
|
"@budibase/string-templates": "^1.0.49-alpha.2",
|
||||||
"@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",
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
await auth.setInitInfo({ init_template: $params["?template"] })
|
await auth.setInitInfo({ init_template: $params["?template"] })
|
||||||
}
|
}
|
||||||
|
|
||||||
await auth.checkAuth()
|
await auth.getSelf()
|
||||||
await admin.init()
|
await admin.init()
|
||||||
|
|
||||||
if (useAccountPortal && multiTenancyEnabled) {
|
if (useAccountPortal && multiTenancyEnabled) {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await auth.checkAuth()
|
await auth.getSelf()
|
||||||
await organisation.init()
|
await organisation.init()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -337,6 +337,14 @@
|
||||||
}}
|
}}
|
||||||
class="template-card"
|
class="template-card"
|
||||||
>
|
>
|
||||||
|
<a
|
||||||
|
href={item.url}
|
||||||
|
target="_blank"
|
||||||
|
class="external-link"
|
||||||
|
on:click|stopPropagation
|
||||||
|
>
|
||||||
|
<Icon name="LinkOut" size="S" />
|
||||||
|
</a>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div style="color: {item.background}" class="iconAlign">
|
<div style="color: {item.background}" class="iconAlign">
|
||||||
<svg
|
<svg
|
||||||
|
@ -518,6 +526,7 @@
|
||||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-card:hover {
|
.template-card:hover {
|
||||||
|
@ -528,6 +537,18 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.external-link {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
color: var(--spectrum-global-color-gray-300);
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
.external-link:hover {
|
||||||
|
color: var(--spectrum-global-color-gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
.iconAlign {
|
.iconAlign {
|
||||||
padding: 0 0 0 var(--spacing-m);
|
padding: 0 0 0 var(--spacing-m);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -108,11 +108,7 @@ export function createAuthStore() {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const actions = {
|
||||||
subscribe: store.subscribe,
|
|
||||||
setOrganisation,
|
|
||||||
getInitInfo,
|
|
||||||
setInitInfo,
|
|
||||||
checkQueryString: async () => {
|
checkQueryString: async () => {
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
if (urlParams.has("tenantId")) {
|
if (urlParams.has("tenantId")) {
|
||||||
|
@ -123,7 +119,7 @@ export function createAuthStore() {
|
||||||
setOrg: async tenantId => {
|
setOrg: async tenantId => {
|
||||||
await setOrganisation(tenantId)
|
await setOrganisation(tenantId)
|
||||||
},
|
},
|
||||||
checkAuth: async () => {
|
getSelf: async () => {
|
||||||
const response = await api.get("/api/global/users/self")
|
const response = await api.get("/api/global/users/self")
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
setUser(null)
|
setUser(null)
|
||||||
|
@ -138,13 +134,12 @@ export function createAuthStore() {
|
||||||
`/api/global/auth/${tenantId}/login`,
|
`/api/global/auth/${tenantId}/login`,
|
||||||
creds
|
creds
|
||||||
)
|
)
|
||||||
const json = await response.json()
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setUser(json.user)
|
await actions.getSelf()
|
||||||
} else {
|
} else {
|
||||||
|
const json = await response.json()
|
||||||
throw new Error(json.message ? json.message : "Invalid credentials")
|
throw new Error(json.message ? json.message : "Invalid credentials")
|
||||||
}
|
}
|
||||||
return json
|
|
||||||
},
|
},
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
const response = await api.post(`/api/global/auth/logout`)
|
const response = await api.post(`/api/global/auth/logout`)
|
||||||
|
@ -197,6 +192,14 @@ export function createAuthStore() {
|
||||||
await response.json()
|
await response.json()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
setOrganisation,
|
||||||
|
getInitInfo,
|
||||||
|
setInitInfo,
|
||||||
|
...actions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const auth = createAuthStore()
|
export const auth = createAuthStore()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"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": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"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",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.49-alpha.1",
|
"@budibase/bbui": "^1.0.49-alpha.2",
|
||||||
"@budibase/standard-components": "^0.9.139",
|
"@budibase/standard-components": "^0.9.139",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.1",
|
"@budibase/string-templates": "^1.0.49-alpha.2",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -70,9 +70,9 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.0.3",
|
"@apidevtools/swagger-parser": "^10.0.3",
|
||||||
"@budibase/backend-core": "^1.0.49-alpha.1",
|
"@budibase/backend-core": "^1.0.49-alpha.2",
|
||||||
"@budibase/client": "^1.0.49-alpha.1",
|
"@budibase/client": "^1.0.49-alpha.2",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.1",
|
"@budibase/string-templates": "^1.0.49-alpha.2",
|
||||||
"@bull-board/api": "^3.7.0",
|
"@bull-board/api": "^3.7.0",
|
||||||
"@bull-board/koa": "^3.7.0",
|
"@bull-board/koa": "^3.7.0",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
|
|
@ -77,7 +77,7 @@ function getUserRoleId(ctx) {
|
||||||
: ctx.user.role._id
|
: ctx.user.role._id
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAppUrl(ctx) {
|
exports.getAppUrl = ctx => {
|
||||||
// construct the url
|
// construct the url
|
||||||
let url
|
let url
|
||||||
if (ctx.request.body.url) {
|
if (ctx.request.body.url) {
|
||||||
|
@ -221,7 +221,7 @@ exports.create = async ctx => {
|
||||||
const apps = await getAllApps({ dev: true })
|
const apps = await getAllApps({ dev: true })
|
||||||
const name = ctx.request.body.name
|
const name = ctx.request.body.name
|
||||||
checkAppName(ctx, apps, name)
|
checkAppName(ctx, apps, name)
|
||||||
const url = await getAppUrl(ctx)
|
const url = exports.getAppUrl(ctx)
|
||||||
checkAppUrl(ctx, apps, url)
|
checkAppUrl(ctx, apps, url)
|
||||||
|
|
||||||
const { useTemplate, templateKey, templateString } = ctx.request.body
|
const { useTemplate, templateKey, templateString } = ctx.request.body
|
||||||
|
@ -288,7 +288,7 @@ exports.update = async ctx => {
|
||||||
if (name) {
|
if (name) {
|
||||||
checkAppName(ctx, apps, name, ctx.params.appId)
|
checkAppName(ctx, apps, name, ctx.params.appId)
|
||||||
}
|
}
|
||||||
const url = await getAppUrl(ctx)
|
const url = await exports.getAppUrl(ctx)
|
||||||
if (url) {
|
if (url) {
|
||||||
checkAppUrl(ctx, apps, url, ctx.params.appId)
|
checkAppUrl(ctx, apps, url, ctx.params.appId)
|
||||||
ctx.request.body.url = url
|
ctx.request.body.url = url
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
const { migrate, MIGRATIONS } = require("../../migrations")
|
||||||
|
|
||||||
|
exports.migrate = async ctx => {
|
||||||
|
const options = ctx.request.body
|
||||||
|
// don't await as can take a while, just return
|
||||||
|
migrate(options)
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.fetchDefinitions = async ctx => {
|
||||||
|
ctx.body = MIGRATIONS
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
|
@ -34,9 +34,7 @@ export class RestImporter {
|
||||||
return this.source.getInfo()
|
return this.source.getInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
importQueries = async (
|
importQueries = async (datasourceId: string): Promise<ImportResult> => {
|
||||||
datasourceId: string
|
|
||||||
): Promise<ImportResult> => {
|
|
||||||
// constuct the queries
|
// constuct the queries
|
||||||
let queries = await this.source.getQueries(datasourceId)
|
let queries = await this.source.getQueries(datasourceId)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {
|
||||||
RelationshipTypes,
|
RelationshipTypes,
|
||||||
} from "../../../constants"
|
} from "../../../constants"
|
||||||
import { breakExternalTableId, isSQL } from "../../../integrations/utils"
|
import { breakExternalTableId, isSQL } from "../../../integrations/utils"
|
||||||
import { processObjectSync } from "@budibase/string-templates"
|
import { processObjectSync } from "@budibase/string-templates"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { processFormulas } from "../../../utilities/rowProcessor/utils"
|
import { processFormulas } from "../../../utilities/rowProcessor/utils"
|
||||||
|
@ -216,11 +216,7 @@ module External {
|
||||||
private datasource: Datasource
|
private datasource: Datasource
|
||||||
private tables: { [key: string]: Table } = {}
|
private tables: { [key: string]: Table } = {}
|
||||||
|
|
||||||
constructor(
|
constructor(operation: Operation, tableId: string, datasource: Datasource) {
|
||||||
operation: Operation,
|
|
||||||
tableId: string,
|
|
||||||
datasource: Datasource
|
|
||||||
) {
|
|
||||||
this.operation = operation
|
this.operation = operation
|
||||||
this.tableId = tableId
|
this.tableId = tableId
|
||||||
this.datasource = datasource
|
this.datasource = datasource
|
||||||
|
@ -272,7 +268,9 @@ module External {
|
||||||
newRow[key] = row[key]
|
newRow[key] = row[key]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field?.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(
|
||||||
|
field?.tableId
|
||||||
|
)
|
||||||
// table has to exist for many to many
|
// table has to exist for many to many
|
||||||
if (!linkTableName || !this.tables[linkTableName]) {
|
if (!linkTableName || !this.tables[linkTableName]) {
|
||||||
continue
|
continue
|
||||||
|
@ -559,7 +557,10 @@ module External {
|
||||||
)) {
|
)) {
|
||||||
const table: Table | undefined = this.getTable(tableId)
|
const table: Table | undefined = this.getTable(tableId)
|
||||||
// if its not the foreign key skip it, nothing to do
|
// if its not the foreign key skip it, nothing to do
|
||||||
if (!table || (table.primary && table.primary.indexOf(colName) !== -1)) {
|
if (
|
||||||
|
!table ||
|
||||||
|
(table.primary && table.primary.indexOf(colName) !== -1)
|
||||||
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ const backupRoutes = require("./backup")
|
||||||
const metadataRoutes = require("./metadata")
|
const metadataRoutes = require("./metadata")
|
||||||
const devRoutes = require("./dev")
|
const devRoutes = require("./dev")
|
||||||
const cloudRoutes = require("./cloud")
|
const cloudRoutes = require("./cloud")
|
||||||
|
const migrationRoutes = require("./migrations")
|
||||||
|
|
||||||
exports.mainRoutes = [
|
exports.mainRoutes = [
|
||||||
authRoutes,
|
authRoutes,
|
||||||
|
@ -53,6 +54,7 @@ exports.mainRoutes = [
|
||||||
// this could be breaking as koa may recognise other routes as this
|
// this could be breaking as koa may recognise other routes as this
|
||||||
tableRoutes,
|
tableRoutes,
|
||||||
rowRoutes,
|
rowRoutes,
|
||||||
|
migrationRoutes,
|
||||||
]
|
]
|
||||||
|
|
||||||
exports.staticRoutes = staticRoutes
|
exports.staticRoutes = staticRoutes
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const migrationsController = require("../controllers/migrations")
|
||||||
|
const router = Router()
|
||||||
|
const { internalApi } = require("@budibase/backend-core/auth")
|
||||||
|
|
||||||
|
router
|
||||||
|
.post("/api/migrations/run", internalApi, migrationsController.migrate)
|
||||||
|
.get(
|
||||||
|
"/api/migrations/definitions",
|
||||||
|
internalApi,
|
||||||
|
migrationsController.fetchDefinitions
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -1,7 +1,7 @@
|
||||||
// need to load environment first
|
// need to load environment first
|
||||||
import { ExtendableContext } from "koa"
|
import { ExtendableContext } from "koa"
|
||||||
|
|
||||||
const env = require("./environment")
|
import * as env from "./environment"
|
||||||
const CouchDB = require("./db")
|
const CouchDB = require("./db")
|
||||||
require("@budibase/backend-core").init(CouchDB)
|
require("@budibase/backend-core").init(CouchDB)
|
||||||
const Koa = require("koa")
|
const Koa = require("koa")
|
||||||
|
@ -16,6 +16,7 @@ const Sentry = require("@sentry/node")
|
||||||
const fileSystem = require("./utilities/fileSystem")
|
const fileSystem = require("./utilities/fileSystem")
|
||||||
const bullboard = require("./automations/bullboard")
|
const bullboard = require("./automations/bullboard")
|
||||||
const redis = require("./utilities/redis")
|
const redis = require("./utilities/redis")
|
||||||
|
import * as migrations from "./migrations"
|
||||||
|
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
|
|
||||||
|
@ -84,13 +85,25 @@ module.exports = server.listen(env.PORT || 0, async () => {
|
||||||
await automations.init()
|
await automations.init()
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on("uncaughtException", err => {
|
const shutdown = () => {
|
||||||
console.error(err)
|
|
||||||
server.close()
|
server.close()
|
||||||
server.destroy()
|
server.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on("uncaughtException", err => {
|
||||||
|
console.error(err)
|
||||||
|
shutdown()
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
server.close()
|
shutdown()
|
||||||
server.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// run migrations on startup if not done via http
|
||||||
|
// not recommended in a clustered environment
|
||||||
|
if (!env.HTTP_MIGRATIONS) {
|
||||||
|
migrations.migrate().catch(err => {
|
||||||
|
console.error("Error performing migrations. Exiting.\n", err)
|
||||||
|
shutdown()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ module.exports = {
|
||||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||||
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
|
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
|
||||||
MULTI_TENANCY: process.env.MULTI_TENANCY,
|
MULTI_TENANCY: process.env.MULTI_TENANCY,
|
||||||
|
HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
|
||||||
// environment
|
// environment
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
JEST_WORKER_ID: process.env.JEST_WORKER_ID,
|
JEST_WORKER_ID: process.env.JEST_WORKER_ID,
|
||||||
|
|
|
@ -4,7 +4,6 @@ const {
|
||||||
isExternalTable,
|
isExternalTable,
|
||||||
isRowId: isExternalRowId,
|
isRowId: isExternalRowId,
|
||||||
} = require("../integrations/utils")
|
} = require("../integrations/utils")
|
||||||
const migration = require("../migrations/usageQuotas")
|
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
// currently only counting new writes and deletes
|
// currently only counting new writes and deletes
|
||||||
|
@ -74,7 +73,6 @@ module.exports = async (ctx, next) => {
|
||||||
usage = files.map(file => file.size).reduce((total, size) => total + size)
|
usage = files.map(file => file.size).reduce((total, size) => total + size)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await migration.run()
|
|
||||||
await performRequest(ctx, next, property, usage)
|
await performRequest(ctx, next, property, usage)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
const { DocumentTypes } = require("@budibase/backend-core/db")
|
||||||
|
import { getAppUrl } from "../../api/controllers/application"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date:
|
||||||
|
* January 2022
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Add the url to the app metadata if it doesn't exist
|
||||||
|
*/
|
||||||
|
export const run = async (appDb: any) => {
|
||||||
|
const metadata = await appDb.get(DocumentTypes.APP_METADATA)
|
||||||
|
if (!metadata.url) {
|
||||||
|
const context = {
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
name: metadata.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
metadata.url = getAppUrl(context)
|
||||||
|
console.log(`Adding url to app: ${metadata.url}`)
|
||||||
|
}
|
||||||
|
await appDb.put(metadata)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { runQuotaMigration } from "./usageQuotas"
|
||||||
|
import * as syncApps from "./usageQuotas/syncApps"
|
||||||
|
import * as syncRows from "./usageQuotas/syncRows"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date:
|
||||||
|
* January 2022
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Synchronise the app and row quotas to the state of the db after it was
|
||||||
|
* discovered that the quota resets were still in place and the row quotas
|
||||||
|
* weren't being decremented correctly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const run = async () => {
|
||||||
|
await runQuotaMigration(async () => {
|
||||||
|
await syncApps.run()
|
||||||
|
await syncRows.run()
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
const { DocumentTypes } = require("@budibase/backend-core/db")
|
||||||
|
const env = require("../../../environment")
|
||||||
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
||||||
|
|
||||||
|
const migration = require("../appUrls")
|
||||||
|
|
||||||
|
describe("run", () => {
|
||||||
|
let config = new TestConfig(false)
|
||||||
|
const CouchDB = config.getCouch()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(config.end)
|
||||||
|
|
||||||
|
it("runs successfully", async () => {
|
||||||
|
const app = await config.createApp("testApp")
|
||||||
|
const appDb = new CouchDB(app.appId)
|
||||||
|
let metadata = await appDb.get(DocumentTypes.APP_METADATA)
|
||||||
|
delete metadata.url
|
||||||
|
await appDb.put(metadata)
|
||||||
|
|
||||||
|
await migration.run(appDb)
|
||||||
|
|
||||||
|
metadata = await appDb.get(DocumentTypes.APP_METADATA)
|
||||||
|
expect(metadata.url).toEqual("/testapp")
|
||||||
|
})
|
||||||
|
})
|
|
@ -4,10 +4,10 @@ const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
||||||
const syncApps = jest.fn()
|
const syncApps = jest.fn()
|
||||||
const syncRows = jest.fn()
|
const syncRows = jest.fn()
|
||||||
|
|
||||||
jest.mock("../../usageQuotas/syncApps", () => ({ run: syncApps }) )
|
jest.mock("../usageQuotas/syncApps", () => ({ run: syncApps }) )
|
||||||
jest.mock("../../usageQuotas/syncRows", () => ({ run: syncRows }) )
|
jest.mock("../usageQuotas/syncRows", () => ({ run: syncRows }) )
|
||||||
|
|
||||||
const migrations = require("../../usageQuotas")
|
const migration = require("../quotas1")
|
||||||
|
|
||||||
describe("run", () => {
|
describe("run", () => {
|
||||||
let config = new TestConfig(false)
|
let config = new TestConfig(false)
|
||||||
|
@ -19,8 +19,8 @@ describe("run", () => {
|
||||||
|
|
||||||
afterAll(config.end)
|
afterAll(config.end)
|
||||||
|
|
||||||
it("runs the required migrations", async () => {
|
it("runs ", async () => {
|
||||||
await migrations.run()
|
await migration.run()
|
||||||
expect(syncApps).toHaveBeenCalledTimes(1)
|
expect(syncApps).toHaveBeenCalledTimes(1)
|
||||||
expect(syncRows).toHaveBeenCalledTimes(1)
|
expect(syncRows).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
|
@ -0,0 +1,25 @@
|
||||||
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
||||||
|
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||||
|
|
||||||
|
// mock email view creation
|
||||||
|
const coreDb = require("@budibase/backend-core/db")
|
||||||
|
const createUserEmailView = jest.fn()
|
||||||
|
coreDb.createUserEmailView = createUserEmailView
|
||||||
|
|
||||||
|
const migration = require("../userEmailViewCasing")
|
||||||
|
|
||||||
|
describe("run", () => {
|
||||||
|
let config = new TestConfig(false)
|
||||||
|
const globalDb = getGlobalDB()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(config.end)
|
||||||
|
|
||||||
|
it("runs successfully", async () => {
|
||||||
|
await migration.run(globalDb)
|
||||||
|
expect(createUserEmailView).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,8 @@
|
||||||
|
const { useQuotas } = require("../../../utilities/usageQuota")
|
||||||
|
|
||||||
|
export const runQuotaMigration = async (migration: Function) => {
|
||||||
|
if (!useQuotas()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await migration()
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
// @ts-ignore
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
import { getGlobalDB, getTenantId } from "@budibase/backend-core/tenancy"
|
||||||
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
// @ts-ignore
|
||||||
|
import { getAllApps } from "@budibase/backend-core/db"
|
||||||
|
import { getUsageQuotaDoc } from "../../../utilities/usageQuota"
|
||||||
|
|
||||||
exports.run = async () => {
|
export const run = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
// get app count
|
// get app count
|
||||||
const devApps = await getAllApps({ dev: true })
|
const devApps = await getAllApps({ dev: true })
|
|
@ -1,13 +1,15 @@
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
// @ts-ignore
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
import { getGlobalDB, getTenantId } from "@budibase/backend-core/tenancy"
|
||||||
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
// @ts-ignore
|
||||||
const { getUniqueRows } = require("../../utilities/usageQuota/rows")
|
import { getAllApps } from "@budibase/backend-core/db"
|
||||||
|
import { getUsageQuotaDoc } from "../../../utilities/usageQuota"
|
||||||
|
import { getUniqueRows } from "../../../utilities/usageQuota/rows"
|
||||||
|
|
||||||
exports.run = async () => {
|
export const run = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
// get all rows in all apps
|
// get all rows in all apps
|
||||||
const allApps = await getAllApps({ all: true })
|
const allApps = await getAllApps({ all: true })
|
||||||
const appIds = allApps ? allApps.map(app => app.appId) : []
|
const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : []
|
||||||
const rows = await getUniqueRows(appIds)
|
const rows = await getUniqueRows(appIds)
|
||||||
const rowCount = rows ? rows.length : 0
|
const rowCount = rows ? rows.length : 0
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||||
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
||||||
const { getUsageQuotaDoc, update, Properties } = require("../../../utilities/usageQuota")
|
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
|
||||||
const syncApps = require("../../usageQuotas/syncApps")
|
const syncApps = require("../syncApps")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../../environment")
|
||||||
|
|
||||||
describe("syncApps", () => {
|
describe("syncApps", () => {
|
||||||
let config = new TestConfig(false)
|
let config = new TestConfig(false)
|
|
@ -1,8 +1,8 @@
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||||
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
||||||
const { getUsageQuotaDoc, update, Properties } = require("../../../utilities/usageQuota")
|
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
|
||||||
const syncRows = require("../../usageQuotas/syncRows")
|
const syncRows = require("../syncRows")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../../environment")
|
||||||
|
|
||||||
describe("syncRows", () => {
|
describe("syncRows", () => {
|
||||||
let config = new TestConfig(false)
|
let config = new TestConfig(false)
|
|
@ -0,0 +1,13 @@
|
||||||
|
const { createUserEmailView } = require("@budibase/backend-core/db")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date:
|
||||||
|
* October 2021
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Recreate the user email view to include latest changes i.e. lower casing the email address
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const run = async (db: any) => {
|
||||||
|
await createUserEmailView(db)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import CouchDB from "../db"
|
||||||
|
const {
|
||||||
|
MIGRATION_TYPES,
|
||||||
|
runMigrations,
|
||||||
|
} = require("@budibase/backend-core/migrations")
|
||||||
|
|
||||||
|
// migration functions
|
||||||
|
import * as userEmailViewCasing from "./functions/userEmailViewCasing"
|
||||||
|
import * as quota1 from "./functions/quotas1"
|
||||||
|
import * as appUrls from "./functions/appUrls"
|
||||||
|
|
||||||
|
export interface Migration {
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
opts?: object
|
||||||
|
fn: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* e.g.
|
||||||
|
* {
|
||||||
|
* tenantIds: ['bb'],
|
||||||
|
* force: {
|
||||||
|
* global: ['quota_1']
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export interface MigrationOptions {
|
||||||
|
tenantIds?: string[]
|
||||||
|
forced?: {
|
||||||
|
[type: string]: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MIGRATIONS: Migration[] = [
|
||||||
|
{
|
||||||
|
type: MIGRATION_TYPES.GLOBAL,
|
||||||
|
name: "user_email_view_casing",
|
||||||
|
fn: userEmailViewCasing.run,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: MIGRATION_TYPES.GLOBAL,
|
||||||
|
name: "quotas_1",
|
||||||
|
fn: quota1.run,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: MIGRATION_TYPES.APP,
|
||||||
|
name: "app_urls",
|
||||||
|
opts: { all: true },
|
||||||
|
fn: appUrls.run,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const migrate = async (options?: MigrationOptions) => {
|
||||||
|
await runMigrations(CouchDB, MIGRATIONS, options)
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
const {
|
|
||||||
MIGRATIONS,
|
|
||||||
MIGRATION_DBS,
|
|
||||||
migrateIfRequired,
|
|
||||||
} = require("@budibase/backend-core/migrations")
|
|
||||||
const { useQuotas } = require("../../utilities/usageQuota")
|
|
||||||
const syncApps = require("./syncApps")
|
|
||||||
const syncRows = require("./syncRows")
|
|
||||||
|
|
||||||
exports.run = async () => {
|
|
||||||
if (!useQuotas()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jan 2022
|
|
||||||
await migrateIfRequired(
|
|
||||||
MIGRATION_DBS.GLOBAL_DB,
|
|
||||||
MIGRATIONS.QUOTAS_1,
|
|
||||||
async () => {
|
|
||||||
await syncApps.run()
|
|
||||||
await syncRows.run()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -51,6 +51,10 @@ class TestConfiguration {
|
||||||
return this.appId
|
return this.appId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCouch() {
|
||||||
|
return CouchDB
|
||||||
|
}
|
||||||
|
|
||||||
async _req(config, params, controlFunc) {
|
async _req(config, params, controlFunc) {
|
||||||
const request = {}
|
const request = {}
|
||||||
// fake cookies, we don't need them
|
// fake cookies, we don't need them
|
||||||
|
|
|
@ -52,6 +52,7 @@ exports.getUsageQuotaDoc = async db => {
|
||||||
* Given a specified tenantId this will add to the usage object for the specified property.
|
* Given a specified tenantId this will add to the usage object for the specified property.
|
||||||
* @param {string} property The property which is to be added to (within the nested usageQuota object).
|
* @param {string} property The property which is to be added to (within the nested usageQuota object).
|
||||||
* @param {number} usage The amount (this can be negative) to adjust the number by.
|
* @param {number} usage The amount (this can be negative) to adjust the number by.
|
||||||
|
* @param {object} opts optional - options such as dryRun, to check what update will do.
|
||||||
* @returns {Promise<void>} When this completes the API key will now be up to date - the quota period may have
|
* @returns {Promise<void>} When this completes the API key will now be up to date - the quota period may have
|
||||||
* also been reset after this call.
|
* also been reset after this call.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"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",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.49-alpha.1",
|
"version": "1.0.49-alpha.2",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "^1.0.49-alpha.1",
|
"@budibase/backend-core": "^1.0.49-alpha.2",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.1",
|
"@budibase/string-templates": "^1.0.49-alpha.2",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sentry/node": "^6.0.0",
|
"@sentry/node": "^6.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
|
|
|
@ -74,10 +74,7 @@ async function authInternal(ctx, user, err = null, info = null) {
|
||||||
exports.authenticate = async (ctx, next) => {
|
exports.authenticate = async (ctx, next) => {
|
||||||
return passport.authenticate("local", async (err, user, info) => {
|
return passport.authenticate("local", async (err, user, info) => {
|
||||||
await authInternal(ctx, user, err, info)
|
await authInternal(ctx, user, err, info)
|
||||||
|
ctx.status = 200
|
||||||
delete user.token
|
|
||||||
|
|
||||||
ctx.body = { user }
|
|
||||||
})(ctx, next)
|
})(ctx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue