Compare commits

..
2 Commits
Author SHA1 Message Date
apairon f26f8c7802 feat(schema): add tool schema, demo and admin tool types
continuous-integration/drone/push Build is passing
2026-10-20 22:56:04 +00:00
apairon 439c8773e6 feat(schema): add custom tools config and runtime types
continuous-integration/drone/push Build is passing
2026-10-20 20:45:19 +00:00
4 changed files with 235 additions and 0 deletions

No files matched your search

+12
View File
@@ -4,6 +4,18 @@ namespace: myapi
meta:
# wird in der Admin-UI zum Projekt aufgegeben
imageUrl: https://source.unsplash.com/random/?internet
tools:
- name: demo-overview
label:
de: Demo-Übersicht
en: Demo overview
description:
de: Beispiel-Tool für die Projekt-Übersicht
en: Example project overview tool
icon: view-dashboard-outline
group: tools
module: _/assets/tools/demo-overview/index.js
capabilities: [db, ui, log]
# Liste aller möglichen Kollektionen (Listen, Seiten...) zum Projekt
collections:
Vendored
+115
View File
@@ -593,6 +593,121 @@ declare global {
[key: string]: any
}
// ── Custom Tools (Admin) ─────────────────────────────────────────────────
/** Project config entry (`meta.tools[]`). */
export interface AdminToolConfig {
name: string
label?: string | { [lang: string]: string }
description?: string | { [lang: string]: string }
icon?: string
group?: string
module: string | { eval: string }
permissions?: string[]
capabilities?: Array<"db" | "files" | "xlsx" | "ui" | "log" | "progress" | "state">
settings?: Record<string, any>
hidden?: boolean
}
/** File handle handed to tools (data URL, no live Blob). */
export interface AdminToolFile {
src: string
name: string
type?: string
size?: number
}
/** Fail-fast batch result; no rollback. */
export interface AdminToolBatchResult {
ok: boolean
failedIndex?: number
error?: unknown
}
export interface AdminToolDb {
find(
collection: string,
opts?: Record<string, any>
): Promise<CollectionDocument[]>
count(collection: string, opts?: Record<string, any>): Promise<number>
loadAll(
collection: string,
opts?: Record<string, any>,
onProgress?: (done: number, total: number) => void
): Promise<CollectionDocument[]>
create(collection: string, data: Record<string, any>): Promise<CollectionDocument>
update(collection: string, id: string, data: Record<string, any>): Promise<Partial<CollectionDocument>>
delete(collection: string, id: string): Promise<void>
updateMany(collection: string, items: Array<{ id: string; data: Record<string, any> }>): Promise<AdminToolBatchResult>
deleteMany(collection: string, ids: string[]): Promise<AdminToolBatchResult>
}
export interface AdminToolUi {
supports(type: string): boolean
slot(el: HTMLElement, element: any): void
styles: CSSStyleSheet[]
registerWidget(type: string, factory: (...args: any[]) => any, opts?: { replace?: boolean }): () => void
render(el: HTMLElement, widget: Record<string, any>): void | (() => void)
field(opts: Record<string, any>): any
form(opts: Record<string, any>): any
[key: string]: any
}
export interface AdminToolI18n {
lang: string
languages: string[]
t(key: string, vars?: Record<string, unknown>): string
text(value: string | Record<string, string> | undefined | null, vars?: Record<string, unknown>): string
has(key: string): boolean
add(lang: string, messages: Record<string, unknown>): () => void
add(messages: Record<string, unknown>, lang?: string): () => void
extend(lang: string, messages: Record<string, unknown>): () => void
extend(messages: Record<string, unknown>, lang?: string): () => void
onChange(cb: (lang: string) => void): () => void
dispose?(): void
}
/** Browser context handed to a tool module. `context === ctx`. */
export interface AdminToolContext {
context: AdminToolContext
apiVersion: 1
uiVersion: 1
host: "tool" | "macro" | "server"
capabilities: string[]
project: ProjectInfo
tool: AdminToolConfig
auth: { id: string; username: string; role: number; permissions: string[] } | null
db: AdminToolDb
files: { get(input: File | Blob | string, name?: string): Promise<AdminToolFile> }
xlsx: any
state: Map<string, unknown>
lang: string
languages: string[]
i18n: AdminToolI18n
theme: { mode: "light" | "dark"; tokens: Record<string, string> }
log(message: string, details?: unknown): void
progress(done: number, total: number, label?: string): void
signal: AbortSignal
hostData: Record<string, any>
asset(specifier: string): string
ui: AdminToolUi
}
export interface AdminToolModule {
mount(
el: HTMLElement,
ctx: AdminToolContext
): void | (() => void) | Promise<void | (() => void)>
messages?: Record<string, Record<string, any>>
}
export interface AdminToolException {
status?: number
msg: string
json?: any
}
/** Represents a single audit log entry as received in audit.return hooks */
export interface AuditLogEntry {
id?: string
+29
View File
@@ -65,6 +65,21 @@
]
}
},
"ui": {
"type": "object",
"additionalProperties": false,
"properties": {
"css": {
"type": "string"
}
}
},
"tools": {
"type": "array",
"items": {
"$ref": "tool.schema.json"
}
},
"i18n": {
"type": "object",
"additionalProperties": false,
@@ -89,6 +104,20 @@
"code"
]
}
},
"messages": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"additionalProperties": true
}
]
}
}
},
"required": [
+79
View File
@@ -0,0 +1,79 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Custom Tool",
"description": "Project custom tool entry (meta.tools[]).",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"module"
],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-_]*$"
},
"label": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/i18nText"
}
]
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/i18nText"
}
]
},
"icon": {
"type": "string"
},
"group": {
"type": "string"
},
"module": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/evalString"
}
]
},
"permissions": {
"type": "array",
"items": {
"type": "string"
}
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"db",
"files",
"xlsx",
"ui",
"log",
"progress",
"state"
]
}
},
"settings": {
"type": "object"
},
"hidden": {
"type": "boolean"
}
}
}