Skip to content

Jet-labs/jet-admin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

159 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Jet Admin Logo

⚑ Jet Admin

Open Source Analytics Platform & Internal Tools Builder

Connect data sources Β· Build workflows Β· Create dashboards Β· Automate operations

πŸš€ Quick Start β€’ πŸ—οΈ Architecture β€’ ✨ Features β€’ πŸ”§ Development Guide β€’ 🀝 Contributing


πŸ“Œ Project Overview

Jet Admin is a modular, open-source internal tools platform that allows engineering and operations teams to connect data sources, build automation workflows, and create powerful dashboards β€” all from a single extensible system.

Unlike traditional BI tools, Jet Admin is designed around a workflow execution engine with a plugin-based datasource architecture. This makes it suitable not just for querying data, but for building automated pipelines, internal operations tools, and multi-step data processing flows.

The system evolved from a PostgreSQL admin tool into a full internal automation platform.

Why Jet Admin?

Capability Jet Admin Traditional BI
Workflow Automation βœ… Node-based engine ❌
Plugin Datasources βœ… 25+ connectors ⚠️ Limited
Internal Tool Builder βœ… Full widget system ❌
Self-Hostable βœ… Docker ready ⚠️
AI Query Support βœ… Built-in ❌
Multi-Tenancy βœ… RBAC + Isolation ⚠️

✨ Features

πŸ”Œ Data Source Integration

Jet Admin supports 25+ datasource connectors through its package-driven plugin system.

Databases

  • PostgreSQL Β· MySQL Β· MongoDB Β· SQLite Β· SQL Server Β· Redis Β· Neo4j

Cloud & SaaS

  • BigQuery Β· Supabase Β· Firestore Β· Elasticsearch Β· AWS S3

APIs & Services

  • REST Β· GraphQL Β· Slack Β· Stripe Β· Twilio Β· SendGrid

πŸ”„ Workflow Automation Engine

A visual node-based execution system for building multi-step automation pipelines.

Start β†’ Query β†’ Transform β†’ Condition β†’ Loop β†’ Notify β†’ End

Capabilities:

  • βœ… Visual node-based builder
  • βœ… Conditional branching logic
  • βœ… Loop and iterator nodes
  • βœ… Context-driven data passing
  • βœ… Worker-based execution model
  • βœ… Scheduled and triggered workflows
  • βœ… Template variable resolution

πŸ“Š Dashboard & Widget System

Charts: Bar Β· Line Β· Pie Β· Radar Β· Scatter Β· Bubble

Data Widgets: Tables Β· Text Β· Markdown Β· HTML Β· Custom widgets

Features: Real-time updates Β· Export support Β· Responsive layouts Β· Drag-and-drop builder


🏒 Enterprise Features

Security: Multi-tenancy Β· RBAC Β· API authentication Β· Audit logs

User Management: Team invites Β· Permissions Β· Activity tracking


πŸ—οΈ Architecture

High-Level System Design

graph TD
    A[Frontend - React] -->|REST / WebSocket| B[API Gateway]
    B --> C[Module System]
    C --> D[Workflow Engine]
    D --> E[Execution Workers]
    E --> F[Datasource Connectors]
    F --> G[(PostgreSQL / External DBs)]
    C --> H[Widget System]
    C --> I[Dashboard Engine]
Loading

Monorepo Structure

jet-admin/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ backend/              # Node.js API + Workflow Engine
β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”œβ”€β”€ modules/
β”‚   β”‚   β”‚   β”œβ”€β”€ datasource/
β”‚   β”‚   β”‚   β”œβ”€β”€ dataQuery/
β”‚   β”‚   β”‚   β”œβ”€β”€ workflow/
β”‚   β”‚   β”‚   β”œβ”€β”€ widget/
β”‚   β”‚   β”‚   └── dashboard/
β”‚   β”‚   β”œβ”€β”€ prisma/
β”‚   β”‚   └── utils/
β”‚   └── frontend/             # React Application
β”‚       └── src/
β”‚           β”œβ”€β”€ data/          # API layer
β”‚           β”œβ”€β”€ logic/         # Hooks, state, contexts
β”‚           └── presentation/  # UI components only
β”‚
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ datasource-types/      # Shared connector contracts
β”‚   β”œβ”€β”€ datasources-logic/     # Connector implementations
β”‚   β”œβ”€β”€ widgets/               # Widget definitions
β”‚   └── workflow-nodes/        # Node type definitions
β”‚
└── docker-compose.cloud.yml

βš™οΈ Backend Architecture

The backend uses a feature-module architecture instead of a traditional layered monolith. Each feature is fully isolated with its own controller, service, repository, and execution logic.

modules/
β”œβ”€β”€ datasource/
β”‚   β”œβ”€β”€ controller.js
β”‚   β”œβ”€β”€ service.js
β”‚   β”œβ”€β”€ repository.js
β”‚   └── validation.js
β”œβ”€β”€ workflow/
β”‚   β”œβ”€β”€ controller.js
β”‚   β”œβ”€β”€ service.js
β”‚   β”œβ”€β”€ engine.js          ← Workflow execution
β”‚   β”œβ”€β”€ workers/           ← Node workers
β”‚   └── repository.js
└── dataQuery/
    β”œβ”€β”€ controller.js
    β”œβ”€β”€ service.js
    └── repository.js

Design principles enforced:

  • βœ… Feature isolation β€” modules do not directly call each other
  • βœ… Low coupling β€” changes in one module don't cascade
  • βœ… High cohesion β€” all logic for a feature lives in its module
  • βœ… Clear ownership β€” every file has a single responsibility

πŸ”„ Workflow Execution Lifecycle

sequenceDiagram
    participant U as User
    participant E as Workflow Engine
    participant R as Input Resolver
    participant W as Worker
    participant C as Context Store

    U->>E: Trigger Workflow
    E->>E: Load Node Definitions
    E->>R: Resolve Node Inputs
    R->>C: Read Context Variables
    C-->>R: Return Values
    R-->>E: Resolved Inputs
    E->>W: Execute Worker(node, inputs)
    W-->>E: Return Result
    E->>C: Write Output to Context
    E->>E: Determine Next Nodes
    E-->>U: Execution Complete
Loading

🧠 Execution Design Principles

The workflow engine enforces a strict contract between nodes, workers, and the engine.

Node Responsibilities

A Node MUST define A Node MUST NOT do
Input schema Execute business logic
Output schema Mutate workflow context
UI configuration Access the database directly
Worker reference Call other modules
Validation rules Resolve template variables

Execution happens exclusively in workers. Nodes are metadata only.


⚑ Worker Architecture

Workers are the execution units of the workflow engine. This separation is intentional β€” it enables testability, strategy replacement, and future distributed execution.

Node Definition
      ↓
Input Resolution (Engine)
      ↓
Worker Execution
      ↓
Result Returned
      ↓
Context Updated (Engine)
      ↓
Next Nodes Triggered

Worker contract:

// βœ… Correct worker pattern
export async function executeQueryWorker(node, resolvedInputs, context) {
  const result = await queryService.execute(
    resolvedInputs.queryId,
    resolvedInputs.params
  );
  return { result }; // Engine writes this to context
}

Rules workers must follow:

  • βœ… Return deterministic output
  • βœ… Accept only pre-resolved inputs
  • ❌ Never mutate context directly
  • ❌ Never call the workflow engine
  • ❌ Never resolve template variables internally

🧩 Context Resolution Model

Workflow execution is driven by a context-propagation model. Each node reads from context and writes results back through the engine.

Context structure:

context = {
  input: { customerId: 15 },
  node_query_1: { result: [...] },
  node_filter_2: { filtered: [...] }
}

Input types supported:

Type Example
Literal "value": 25
Template "value": "{{node_query_1.result}}"

Resolution flow:

Detect Input Type
       ↓
If Literal β†’ Return Value Immediately
       ↓
If Template β†’ Parse Reference
       ↓
Resolve from Context
       ↓
Return Evaluated Value to Worker

Resolution always happens before worker execution β€” never inside workers.


πŸ”Œ Plugin Architecture

Extensibility is achieved through package-driven plugins. The core system never depends on plugin implementations. Plugins depend on core contracts.

packages/
β”œβ”€β”€ datasources-logic/
β”‚   β”œβ”€β”€ postgres/
β”‚   β”œβ”€β”€ mysql/
β”‚   β”œβ”€β”€ mongodb/
β”‚   └── ...
β”œβ”€β”€ workflow-nodes/
β”‚   β”œβ”€β”€ queryNode/
β”‚   β”œβ”€β”€ transformNode/
β”‚   └── conditionNode/
└── widgets/
    β”œβ”€β”€ barChart/
    β”œβ”€β”€ table/
    └── ...

Creating a Datasource Connector

Every connector must implement the standard contract:

export class PostgresConnector {
  async connect(config) { /* ... */ }
  async disconnect() { /* ... */ }
  async execute(query, params) { /* normalized result */ }
  async validate(config) { /* boolean */ }
  async healthCheck() { /* status */ }
}

Rules:

  • βœ… Return normalized results only
  • βœ… Handle connection errors internally
  • ❌ No workflow logic inside connectors
  • ❌ No cross-connector dependencies

Creating a Workflow Node

export const QueryNode = {
  type: "dataQuery",
  name: "Execute Query",
  inputs: {
    queryId: { type: "string", required: true },
    params:  { type: "object" }
  },
  outputs: {
    result: "array"
  },
  worker: executeQueryWorker,
  uiConfig: { /* form schema */ }
};

Creating a Widget

export const BarChartWidget = {
  type: "barChart",
  configSchema: { /* JSON Schema */ },
  render(data, config) {
    return <BarChart data={data} options={config} />;
  }
};

πŸ“˜ Example Workflow Definition

{
  "id": "sales-report-workflow",
  "name": "Monthly Sales Report",
  "nodes": [
    { "id": "start",    "type": "start" },
    { "id": "query_1",  "type": "dataQuery",    "inputs": { "queryId": "fetchOrders" } },
    { "id": "filter_2", "type": "transform",    "inputs": { "data": "{{query_1.result}}", "filter": "last30days" } },
    { "id": "agg_3",    "type": "aggregate",    "inputs": { "data": "{{filter_2.filtered}}", "field": "revenue" } },
    { "id": "chart_4",  "type": "generateChart","inputs": { "data": "{{agg_3.aggregated}}", "type": "bar" } },
    { "id": "end",      "type": "end" }
  ],
  "edges": [
    { "from": "start",   "to": "query_1"  },
    { "from": "query_1", "to": "filter_2" },
    { "from": "filter_2","to": "agg_3"    },
    { "from": "agg_3",   "to": "chart_4"  },
    { "from": "chart_4", "to": "end"      }
  ]
}

πŸ§ͺ Coding Standards

Naming Conventions

Category Convention Example
Functions camelCase executeWorkflow()
Classes PascalCase WorkflowEngine
Constants UPPER_SNAKE MAX_RETRY_COUNT
Files kebab-case workflow-engine.js
DB columns snake_case (Prisma mapped) created_at

Function Rules

// βœ… Good β€” single responsibility, deterministic
async function resolveNodeInputs(node, context) {
  return node.inputs.map(input => resolveInput(input, context));
}

// ❌ Bad β€” mixed responsibilities
async function resolveAndExecuteAndStore(node, context) {
  const inputs = resolve(node, context);   // resolution
  const result = await execute(inputs);    // execution
  await db.save(result);                   // storage
}

Error Handling

// βœ… Correct β€” errors propagate with context
try {
  await executeWorker(node, resolvedInputs);
} catch (error) {
  logger.error({ nodeId: node.id, executionId, error });
  throw new WorkflowExecutionError(node.id, executionId, error.message);
}

// ❌ Wrong β€” swallowed error, silent failure
try {
  await executeWorker(node, resolvedInputs);
} catch (e) {
  return null;
}

Database Practices

Rule:  Controller β†’ Service β†’ Repository β†’ DB
Never: Controller β†’ DB directly
Never: Worker β†’ DB directly
Always: Use Prisma models only
Always: Wrap multi-step operations in transactions

🎨 Frontend Architecture

Frontend is organized into three strict layers that must never be mixed:

src/
β”œβ”€β”€ data/           # API calls, models, transformers
β”œβ”€β”€ logic/          # Hooks, state, contexts
└── presentation/   # UI components only (no logic)
// βœ… Correct β€” component uses hook, hook uses service
const WorkflowList = () => {
  const { workflows } = useWorkflows();          // logic layer
  return workflows.map(w => <WorkflowCard w={w} />);
};

// ❌ Wrong β€” component calls API directly
const WorkflowList = () => {
  const [workflows, setWorkflows] = useState([]);
  useEffect(() => { axios.get('/api/workflows').then(setWorkflows); }, []);
};

❌ Anti-Patterns (Do NOT do this)

Anti-Pattern Why it's wrong
Business logic in controllers Breaks testability and reuse
Workers mutating context Breaks execution determinism
Nodes executing DB logic Breaks separation of concerns
Template resolution inside workers Engine responsibility, not worker
Cross-module direct calls Creates hidden coupling
Hardcoded datasource logic Breaks plugin isolation
Swallowing errors silently Hides failures during debugging

βš™οΈ Engineering Philosophy

Jet Admin enforces these design decisions deliberately:

Decision Reason
Workers execute, nodes define Enables strategy replacement and testability
Context is read-only for workers Prevents hidden mutations and execution chaos
Inputs resolved before execution Ensures predictable, deterministic worker behavior
Plugins are packages, not inline code Enables independent versioning and release
Module isolation enforced Prevents cascading failures and coupling

These are not opinions β€” they are guarantees the system depends on.


πŸ“ˆ Scaling Strategy

The architecture is designed to support distributed execution in future iterations:

Current:   Synchronous Worker Execution
Next:      Queue-based worker dispatch (RabbitMQ ready)
Future:    Distributed workflow runners + execution snapshots

Why workers remain pure matters: Stateless workers can be picked up by any runner β€” local, queued, or distributed β€” without code changes.


πŸ” Security Model

  • Multi-tenancy: All queries scoped to tenant context
  • RBAC: Role-based permissions enforced at service layer
  • Credential storage: Datasource secrets encrypted at rest
  • Query validation: All user-supplied queries validated before execution
  • Execution sandboxing: Workers run in isolated execution contexts

πŸš€ Quick Start

Prerequisites

  • Node.js 18+
  • PostgreSQL 14+
  • Docker (recommended)
  • Firebase project (for auth)

Docker Setup (Recommended)

git clone https://github.com/Jet-labs/jet-admin.git
cd jet-admin
cp .env.docker.example .env.docker
docker-compose -f docker-compose.cloud.yml up -d

Access: http://localhost:3000

Manual Setup

Backend:

cd apps/backend
npm install
cp .env.example .env
npx prisma migrate dev
npm run dev

Frontend:

cd apps/frontend
npm install
npm run dev

Environment Variables

Backend .env:

DATABASE_URL=postgresql://user:pass@localhost:5432/jetadmin
JWT_SECRET=your_jwt_secret
FIREBASE_PROJECT_ID=your_project_id
RABBITMQ_URL=amqp://localhost
API_PORT=4000

Frontend .env:

VITE_API_URL=http://localhost:4000
VITE_FIREBASE_CONFIG={"apiKey":"..."}

🀝 Contributing

Branch Naming

feature/workflow-loop-node
fix/context-resolution-bug
docs/plugin-development-guide
refactor/worker-execution-pattern

Commit Format

feat: add loop execution node
fix: resolve context variable mutation issue
docs: add datasource connector guide
refactor: extract worker strategy pattern
test: add workflow engine unit tests

Pull Request Checklist

  • No business logic in controllers
  • Workers don't mutate context
  • Modules remain isolated
  • Error handling follows established pattern
  • No console.log statements
  • No unused imports
  • Code follows naming conventions
  • Tests added for new features

Development Process

1. Fork the repository
2. Create a feature branch
3. Implement changes following coding standards
4. Write/update tests
5. Open a PR with description of changes
6. Address review feedback

πŸ“š Documentation

Document Description
Workflow Engine Execution lifecycle, context model, design decisions
Node Spec Node definition contract, worker patterns
Plugin Development Building datasource connectors and widget plugins
Execution Context Context model, template resolution, immutability rules
Worker Design Worker architecture, strategy patterns, scaling
Contribution Rules Coding standards, PR process, anti-patterns

πŸ“ˆ Roadmap

  • Workflow versioning and rollback
  • Visual execution debugger
  • Distributed worker execution
  • AI-powered workflow builder
  • Plugin marketplace
  • Real-time execution tracing
  • Partial workflow resume
  • Execution snapshots

πŸ› οΈ Tech Stack

Layer Technology
Frontend React 18, Tailwind CSS, React Flow, React Query
Backend Node.js, Express, Prisma ORM
Database PostgreSQL
Auth Firebase
Messaging RabbitMQ
Charts Chart.js
Realtime Socket.IO
Infra Docker, Linux

πŸ“œ License

MIT License β€” see LICENSE for details.


πŸ™ Acknowledgements

Built with: React Β· Node.js Β· Prisma Β· React Flow Β· Chart.js Β· Socket.IO Β· PostgreSQL


⭐ Star this repo if it helps you

Report a Bug Β· Request a Feature Β· Discussions

About

πŸš€ Open-source analytics platform with multi-datasource support, visual workflow builder, customizable widgets & dashboards. Connect 25+ data sources, build query-based workflows, and create stunning visualizations with multi-tenant architecture.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages