Merge branch 'master' into fix/dont-validate-ancestors-on-plugins

This commit is contained in:
Adria Navarro 2025-02-27 11:57:55 +01:00 committed by GitHub
commit 59a7bdfe3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 94 additions and 76 deletions

@ -1 +1 @@
Subproject commit 45f5673d5e5ab3c22deb6663cea2e31a628aa133
Subproject commit e3843dd4eaced68ae063355b77df200dbc789c98

View File

@ -11,6 +11,7 @@ import {
UploadPluginResponse,
FetchPluginResponse,
DeletePluginResponse,
PluginMetadata,
} from "@budibase/types"
import env from "../../../environment"
import { clientAppSocket } from "../../../websockets"
@ -53,10 +54,11 @@ export async function create(
const { source, url, headers, githubToken } = ctx.request.body
try {
let metadata
let directory
let metadata: PluginMetadata
let directory: string
// Generating random name as a backup and needed for url
let name = "PLUGIN_" + Math.floor(100000 + Math.random() * 900000)
const name = "PLUGIN_" + Math.floor(100000 + Math.random() * 900000)
switch (source) {
case PluginSource.NPM: {
@ -81,12 +83,14 @@ export async function create(
directory = directoryUrl
break
}
default:
ctx.throw(400, "Invalid source")
}
pluginCore.validate(metadata?.schema)
pluginCore.validate(metadata.schema)
// Only allow components in cloud
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
if (!env.SELF_HOSTED && metadata.schema?.type !== PluginType.COMPONENT) {
throw new Error(
"Only component plugins are supported outside of self-host"
)

View File

@ -7,7 +7,7 @@ import { default as queries } from "./app/queries"
import { default as rows } from "./app/rows"
import { default as links } from "./app/links"
import { default as users } from "./users"
import { default as plugins } from "./plugins"
import * as plugins from "./plugins"
import * as views from "./app/views"
import * as permissions from "./app/permissions"
import * as rowActions from "./app/rowActions"

View File

@ -1,5 +1,41 @@
import * as plugins from "./plugins"
import { KoaFile, Plugin, PluginSource, PluginType } from "@budibase/types"
import {
db as dbCore,
objectStore,
plugins as pluginCore,
tenancy,
} from "@budibase/backend-core"
import { fileUpload } from "../../api/controllers/plugin/file"
import env from "../../environment"
import { clientAppSocket } from "../../websockets"
import { sdk as pro } from "@budibase/pro"
export default {
...plugins,
export async function fetch(type?: PluginType): Promise<Plugin[]> {
const db = tenancy.getGlobalDB()
const response = await db.allDocs(
dbCore.getPluginParams(null, {
include_docs: true,
})
)
let plugins = response.rows.map((row: any) => row.doc) as Plugin[]
plugins = await objectStore.enrichPluginURLs(plugins)
if (type) {
return plugins.filter((plugin: Plugin) => plugin.schema?.type === type)
} else {
return plugins
}
}
export async function processUploaded(plugin: KoaFile, source: PluginSource) {
const { metadata, directory } = await fileUpload(plugin)
pluginCore.validate(metadata.schema)
// Only allow components in cloud
if (!env.SELF_HOSTED && metadata.schema?.type !== PluginType.COMPONENT) {
throw new Error("Only component plugins are supported outside of self-host")
}
const doc = await pro.plugins.storePlugin(metadata, directory, source)
clientAppSocket?.emit("plugin-update", { name: doc.name, hash: doc.hash })
return doc
}

View File

@ -1,41 +0,0 @@
import { KoaFile, Plugin, PluginSource, PluginType } from "@budibase/types"
import {
db as dbCore,
objectStore,
plugins as pluginCore,
tenancy,
} from "@budibase/backend-core"
import { fileUpload } from "../../api/controllers/plugin/file"
import env from "../../environment"
import { clientAppSocket } from "../../websockets"
import { sdk as pro } from "@budibase/pro"
export async function fetch(type?: PluginType): Promise<Plugin[]> {
const db = tenancy.getGlobalDB()
const response = await db.allDocs(
dbCore.getPluginParams(null, {
include_docs: true,
})
)
let plugins = response.rows.map((row: any) => row.doc) as Plugin[]
plugins = await objectStore.enrichPluginURLs(plugins)
if (type) {
return plugins.filter((plugin: Plugin) => plugin.schema?.type === type)
} else {
return plugins
}
}
export async function processUploaded(plugin: KoaFile, source?: PluginSource) {
const { metadata, directory } = await fileUpload(plugin)
pluginCore.validate(metadata?.schema)
// Only allow components in cloud
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
throw new Error("Only component plugins are supported outside of self-host")
}
const doc = await pro.plugins.storePlugin(metadata, directory, source)
clientAppSocket?.emit("plugin-update", { name: doc.name, hash: doc.hash })
return doc
}

View File

@ -1,4 +1,4 @@
import { Plugin } from "@budibase/types"
import { Plugin, PluginUpload } from "@budibase/types"
import { budibaseTempDir } from "../budibaseDir"
import fs from "fs"
import { join } from "path"
@ -8,31 +8,31 @@ import stream from "stream"
const DATASOURCE_PATH = join(budibaseTempDir(), "datasource")
const AUTOMATION_PATH = join(budibaseTempDir(), "automation")
export const getPluginMetadata = async (path: string) => {
let metadata: any = {}
export const getPluginMetadata = async (
path: string
): Promise<PluginUpload> => {
let pkg: any
let schema: any
try {
const pkg = fs.readFileSync(join(path, "package.json"), "utf8")
const schema = fs.readFileSync(join(path, "schema.json"), "utf8")
metadata.schema = JSON.parse(schema)
metadata.package = JSON.parse(pkg)
if (
!metadata.package.name ||
!metadata.package.version ||
!metadata.package.description
) {
throw new Error(
"package.json is missing one of 'name', 'version' or 'description'."
)
pkg = JSON.parse(fs.readFileSync(join(path, "package.json"), "utf8"))
schema = JSON.parse(fs.readFileSync(join(path, "schema.json"), "utf8"))
if (!pkg.name) {
throw new Error("package.json is missing 'name'.")
}
if (!pkg.version) {
throw new Error("package.json is missing 'version'.")
}
if (!pkg.description) {
throw new Error("package.json is missing 'description'.")
}
} catch (err: any) {
throw new Error(
`Unable to process schema.json/package.json in plugin. ${err.message}`
`Unable to process schema.json/package.json in plugin. ${err.message}`,
{ cause: err }
)
}
return { metadata, directory: path }
return { metadata: { package: pkg, schema }, directory: path }
}
async function getPluginImpl(path: string, plugin: Plugin) {

View File

@ -3,7 +3,8 @@ import env from "./environment"
import chokidar from "chokidar"
import fs from "fs"
import { constants, tenancy } from "@budibase/backend-core"
import pluginsSdk from "./sdk/plugins"
import { processUploaded } from "./sdk/plugins"
import { PluginSource } from "@budibase/types"
export function watch() {
const watchPath = path.join(env.PLUGINS_DIR, "./**/*.tar.gz")
@ -27,7 +28,7 @@ export function watch() {
const split = path.split("/")
const name = split[split.length - 1]
console.log("Importing plugin:", path)
await pluginsSdk.processUploaded({ name, path })
await processUploaded({ name, path }, PluginSource.FILE)
} catch (err: any) {
const message = err?.message ? err?.message : err
console.error("Failed to import plugin:", message)

View File

@ -24,10 +24,7 @@ export interface Plugin extends Document {
source: PluginSource
package: { [key: string]: any }
hash: string
schema: {
type: PluginType
[key: string]: any
}
schema: PluginSchema
iconFileName?: string
// Populated on read
jsUrl?: string
@ -36,3 +33,24 @@ export interface Plugin extends Document {
}
export const PLUGIN_TYPE_ARR = Object.values(PluginType)
export interface PluginSchema {
type: PluginType
[key: string]: any
}
interface Package {
name: string
version: string
description: string
}
export interface PluginMetadata {
schema: PluginSchema
package: Package
}
export interface PluginUpload {
metadata: PluginMetadata
directory: string
}