Skip to content

Repository files navigation

⚑ FlowForge

Real-Time Multi-Tenant Workflow Orchestration Engine

Technical Assessment β€” Fullstack Engineer Internship Β· Sevima

CI Node.js NestJS React PostgreSQL MongoDB Redis TypeScript License


πŸ“– Daftar Isi

  1. Tentang Proyek
  2. Arsitektur Sistem
  3. Tech Stack
  4. Struktur Folder
  5. Prasyarat & Instalasi
  6. Konfigurasi Environment
  7. Menjalankan Secara Lokal
  8. Docker Compose (Production)
  9. Dokumentasi API
  10. Panduan Testing Manual (Browser)
  11. CI/CD Pipeline
  12. Database Design
  13. Fitur AI β€” Natural Language Builder
  14. WebSocket Real-Time Events
  15. Trade-offs & Rencana Perbaikan
  16. Keputusan Implementasi

🎯 Tentang Proyek

FlowForge adalah workflow orchestration engine multi-tenant real-time yang dibangun sebagai technical assessment untuk posisi Fullstack Engineer Internship di Sevima. Proyek ini mensimulasikan peran founding engineer yang membangun platform otomasi workflow β€” kombinasi eksekusi model Zapier dan GitHub Actions.

Fitur Utama

Fitur Deskripsi Status
DAG Execution Engine Parse, validasi, dan eksekusi workflow berbasis Directed Acyclic Graph dengan topological sort (Kahn's algorithm) βœ…
Multi-Tenant Isolation Setiap tenant terisolasi penuh via Prisma Proxy yang menyuntikkan tenantId otomatis ke semua query βœ…
Workflow Versioning Setiap update workflow membuat versi baru; rollback ke versi manapun βœ…
Async Queue Execution BullMQ + Redis untuk job queue dengan retry exponential backoff & timeout βœ…
Real-Time Dashboard Socket.IO WebSocket untuk notifikasi perubahan status step secara live βœ…
DAG Visualizer React Flow untuk render graph interaktif dengan color-coded status node βœ…
AI Workflow Builder Natural Language β†’ DAG JSON via Google Gemini API dengan validasi & retry korektif βœ…
Rate Limiting Redis sliding window per-tenant (default 100 req/mnt) βœ…
Webhook Trigger Trigger workflow via URL token publik βœ…
JWT Auth + RBAC Access token (15m) + Refresh token (7d) + role Admin/Editor/Viewer βœ…
Swagger UI Dokumentasi API interaktif auto-generated βœ…
Docker Compose Full stack siap pakai dengan satu perintah βœ…

πŸ—οΈ Arsitektur Sistem

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       React Dashboard (Vite)        β”‚
β”‚  (Auth Β· Workflows Β· Runs Β· AI)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚  REST + WebSocket (Socket.IO)
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   NestJS API Gateway                       β”‚
β”‚                                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Auth   β”‚ β”‚ Workflowsβ”‚ β”‚  Runs    β”‚ β”‚      AI      β”‚  β”‚
β”‚  β”‚JWT+RBAC β”‚ β”‚ CRUD+Ver β”‚ β”‚ History  β”‚ β”‚ NL Builder   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  TenantGuard    β”‚  β”‚  Rate Limiter (Redis window) β”‚    β”‚
β”‚  β”‚ (Prisma Proxy)  β”‚  β”‚  Webhook Controller           β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚            WebSocket Gateway (Socket.IO)             β”‚   β”‚
β”‚  β”‚   room: tenant:{tenantId}:run:{runId}                β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚                      β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚   PostgreSQL 16   β”‚    β”‚  BullMQ (Redis 7)   β”‚
     β”‚   (Prisma ORM)    β”‚    β”‚  Job Queue          β”‚
     β”‚ tenants, users,   β”‚    β”‚  retry/backoff      β”‚
     β”‚ workflows, runs   β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚
                                      β–Ό
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚    DAG Execution Worker     β”‚
                         β”‚  1. Parse definition_json   β”‚
                         β”‚  2. Topological sort (Kahn) β”‚
                         β”‚  3. Execute layer by layer  β”‚
                         β”‚     (parallel per layer)    β”‚
                         β”‚  4. Retry w/ exp. backoff   β”‚
                         β”‚  5. Emit WebSocket events   β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚           β”‚
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”  β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚  MongoDB 7   β”‚  β”‚  External APIs  β”‚
                         β”‚ (exec logs,  β”‚  β”‚ (HTTP step,     β”‚
                         β”‚  append-only)β”‚  β”‚  Gemini API)    β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  └────────────────-β”˜

Alur Eksekusi Workflow

Client β†’ POST /workflows/:id/trigger
    ↓
API: buat workflow_run (status: queued) β†’ push job BullMQ
    ↓
Worker: dequeue job β†’ parse DAG β†’ topological sort
    ↓  
Worker: eksekusi step per layer (step dalam layer = paralel)
    ↓  (per step)
Worker: catat log β†’ MongoDB
Worker: update step_run β†’ PostgreSQL
Worker: emit event β†’ WebSocket room
    ↓
Dashboard: terima event β†’ update node color β†’ re-render

πŸ›  Tech Stack

Layer Teknologi Versi Alasan
Runtime Node.js 22.x LTS terbaru, performa terbaik, ESM native
Backend Framework NestJS 11.x Modular, DI, built-in validation
Language TypeScript 5.7 Type-safety lintas layer
ORM Prisma 5.22 Type-safe queries, migration otomatis
Database Relasional PostgreSQL 16 ACID, JSONB, relasi workflow/tenant
Log Store MongoDB 7 Write-heavy, skema fleksibel
Cache & Queue Broker Redis 7 BullMQ + rate limiting
Job Queue BullMQ 5.x Retry, backoff, delay built-in
Real-Time Socket.IO 4.x WebSocket + polling fallback
Auth JWT + Passport - Access 15m + Refresh 7d
Frontend React + Vite 18 / 5 Fast HMR, React Flow, TanStack Query
Styling TailwindCSS + Vanilla CSS - Estetika Minimalist Editorial premium, performa optimal
DAG Visualizer React Flow - Render graph interaktif
State/Fetch TanStack Query - Cache, optimistic update
AI Google Gemini 3.5 Flash / Fallbacks - Generasi JSON DAG tangguh dengan array model cadangan
API Docs Swagger/OpenAPI - Auto-generate dari NestJS decorator
CI/CD GitHub Actions - Lint β†’ Test β†’ Build β†’ Docker
Container Docker multi-stage - Builder + Runner stage

πŸ“ Struktur Folder

flowforge/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ api/                          # NestJS Backend
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ auth/                 # JWT strategy, guards, RBAC decorator
β”‚   β”‚   β”‚   β”œβ”€β”€ tenants/              # Tenant module
β”‚   β”‚   β”‚   β”œβ”€β”€ users/                # User management
β”‚   β”‚   β”‚   β”œβ”€β”€ workflows/            # CRUD + versioning + webhook
β”‚   β”‚   β”‚   β”œβ”€β”€ runs/                 # Trigger, status, history, health
β”‚   β”‚   β”‚   β”œβ”€β”€ execution/            # DAG parser, topo-sort, executor core
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ dag-parser.ts     # Validasi DAG (cycle, orphan, type)
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ topo-sort.ts      # Kahn's algorithm
β”‚   β”‚   β”‚   β”‚   └── step-executor.ts  # HTTP, script, delay, condition
β”‚   β”‚   β”‚   β”œβ”€β”€ queue/                # BullMQ producer/consumer
β”‚   β”‚   β”‚   β”œβ”€β”€ websocket/            # Socket.IO gateway
β”‚   β”‚   β”‚   β”œβ”€β”€ ai/                   # Natural language workflow builder
β”‚   β”‚   β”‚   β”œβ”€β”€ common/               # Pipes, filters, interceptors, guards
β”‚   β”‚   β”‚   └── main.ts
β”‚   β”‚   β”œβ”€β”€ prisma/
β”‚   β”‚   β”‚   β”œβ”€β”€ schema.prisma         # Skema lengkap (6 model)
β”‚   β”‚   β”‚   └── migrations/           # Riwayat migrasi otomatis
β”‚   β”‚   β”œβ”€β”€ test/                     # Integration & E2E tests
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.e2e-spec.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ workflows.e2e-spec.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ tenant-isolation.e2e-spec.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ trigger.e2e-spec.ts
β”‚   β”‚   β”‚   └── rate-limit.e2e-spec.ts
β”‚   β”‚   β”œβ”€β”€ .env.example              # Template variabel environment
β”‚   β”‚   └── Dockerfile                # Multi-stage build
β”‚   └── web/                          # React Frontend
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ components/           # DAG canvas, run history, health panel
β”‚       β”‚   β”œβ”€β”€ pages/                # Login, Dashboard, Workflows, Runs, AI
β”‚       β”‚   β”œβ”€β”€ hooks/                # useWorkflowSocket, useAuth
β”‚       β”‚   └── lib/                  # API client, Socket client
β”‚       └── Dockerfile
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── ci.yml                    # CI Pipeline
β”œβ”€β”€ audits/                           # Laporan audit berkala perbaikan sistem
β”œβ”€β”€ docs/
β”‚   └── infra-design.md               # Desain infrastruktur AWS
β”œβ”€β”€ docker-compose.yml                # Full stack (6 services)
β”œβ”€β”€ FlowForge_Final_Report.md         # Laporan akhir komprehensif assessment
β”œβ”€β”€ REVIEW.md                         # Code review assessment
β”œβ”€β”€ CHANGELOG-DECISIONS.md            # Log keputusan implementasi
β”œβ”€β”€ 01-PRD-FlowForge.md               # Product Requirements Document
β”œβ”€β”€ 02-INSTALL-GUIDE.md               # Panduan instalasi lengkap
└── 03-AGENT-EXECUTION-GUIDE.md       # Panduan eksekusi agent

βœ… Prasyarat & Instalasi

πŸ“– Panduan instalasi lebih lengkap tersedia di 02-INSTALL-GUIDE.md

πŸ”§ Prasyarat Sistem (klik untuk expand)
Tool Versi Minimum Keterangan
Node.js 22.x Digunakan untuk API & frontend
npm 11.x Bundled dengan Node 22
Git 2.x Version control
PostgreSQL 16 Database relasional utama
MongoDB 7 Store log eksekusi
Redis 7 Queue & rate limiting
Docker 24.x (opsional) Untuk mode production
Docker Compose v2.x (opsional) Full stack dengan satu perintah

Windows: Direkomendasikan menggunakan Laragon yang sudah membundel PostgreSQL, MongoDB, dan Redis. Atau gunakan WSL2 + Docker Desktop.

πŸ“₯ Langkah 1: Clone Repository
git clone https://github.com/Himdeunn/flowforge.git
cd flowforge
πŸ“¦ Langkah 2: Install Dependensi

Backend (NestJS API):

cd apps/api
npm install

Frontend (React + Vite):

cd apps/web
npm install
πŸ—„οΈ Langkah 3: Setup Database (Prisma Migration)

Pastikan PostgreSQL berjalan dan file .env sudah dikonfigurasi (lihat bagian Konfigurasi Environment), lalu jalankan:

cd apps/api

# Jalankan migrasi database
npx prisma migrate dev

# Generate Prisma Client
npx prisma generate

Verifikasi tabel berhasil dibuat:

# PostgreSQL (via Laragon atau Docker)
psql -U flowforge -d flowforge -c "\dt"
# Output yang diharapkan: tenants, users, workflow_definitions, workflow_versions, workflow_runs, step_runs

βš™οΈ Konfigurasi Environment

Buat file .env di dalam apps/api/ berdasarkan template yang tersedia:

cp apps/api/.env.example apps/api/.env
πŸ“‹ Daftar Lengkap Variabel Environment (klik untuk expand)

Edit apps/api/.env dan isi nilai yang sesuai:

# ─── Database (PostgreSQL via Prisma) ─────────────────────────────────────────
DATABASE_URL="postgresql://flowforge:flowforge@localhost:5432/flowforge"

# ─── MongoDB (Execution Log Store) ────────────────────────────────────────────
MONGODB_URI="mongodb://localhost:27017/flowforge"

# ─── Redis (BullMQ Queue + Rate Limiting) ─────────────────────────────────────
REDIS_HOST="localhost"
REDIS_PORT=6379

# ─── JWT Authentication ────────────────────────────────────────────────────────
JWT_ACCESS_SECRET="ganti-dengan-secret-panjang-dan-acak"
JWT_REFRESH_SECRET="ganti-dengan-secret-lain-yang-berbeda"
JWT_ACCESS_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"

# ─── Google Gemini AI ─────────────────────────────────────────────────────────
# Wajib diisi untuk menggunakan fitur AI Natural Language Builder
# Dukung rotasi hingga 5 key untuk menghindari rate limit
GEMINI_API_KEY="masukkan-api-key-anda-disini"
GEMINI_API_KEY_2="api-key-cadangan-2-opsional"
GEMINI_API_KEY_3="api-key-cadangan-3-opsional"
GEMINI_API_KEY_4="api-key-cadangan-4-opsional"
GEMINI_API_KEY_5="api-key-cadangan-5-opsional"

# ─── Aplikasi ─────────────────────────────────────────────────────────────────
PORT=3000
NODE_ENV="development"

# ─── Worker Control ───────────────────────────────────────────────────────────
# Set true untuk menonaktifkan BullMQ worker (berguna di environment test)
# Di docker-compose: service 'api' β†’ DISABLE_WORKER=true, service 'worker' β†’ DISABLE_WORKER=false
DISABLE_WORKER="false"

⚠️ PENTING: Jangan pernah commit file .env ke Git. File ini sudah terdaftar di .gitignore.

πŸ”‘ Cara Mendapatkan Gemini API Key
  1. Buka Google AI Studio
  2. Login dengan akun Google
  3. Klik "Create API Key"
  4. Pilih project Google Cloud yang sudah ada atau buat baru
  5. Salin API key dan tempelkan ke variabel GEMINI_API_KEY di file .env

Untuk menghindari rate limit, bisa menyediakan hingga 5 API key dari akun berbeda. Sistem akan melakukan rotasi otomatis saat satu key terkena limit.


πŸš€ Menjalankan Secara Lokal

▢️ Langkah 1: Jalankan Backend API (NestJS)
cd apps/api
npm run start:dev

Server akan berjalan di: http://localhost:3000

Endpoint yang tersedia setelah server berjalan:

  • API Base URL: http://localhost:3000/api/v1
  • Swagger UI: http://localhost:3000/api/docs
  • Health Check: http://localhost:3000/api/v1/health
▢️ Langkah 2: Jalankan Frontend Dashboard (React + Vite)

Buka terminal baru, lalu:

cd apps/web
npm run dev

Dashboard akan berjalan di: http://localhost:5173

πŸ”΄ Pastikan Service Pendukung Berjalan

Sebelum menjalankan API, pastikan ketiga service ini aktif:

PostgreSQL (Laragon atau Docker):

# Via Docker
docker run --name flowforge-postgres \
  -e POSTGRES_USER=flowforge \
  -e POSTGRES_PASSWORD=flowforge \
  -e POSTGRES_DB=flowforge \
  -p 5432:5432 -d postgres:16-alpine

Redis (Laragon atau Docker):

# Via Docker
docker run --name flowforge-redis -p 6379:6379 -d redis:7-alpine

MongoDB (Laragon atau Docker):

# Via Docker
docker run --name flowforge-mongo -p 27017:27017 -d mongo:7

Jika menggunakan Laragon di Windows, cukup klik Start All di Laragon Panel dan pastikan PostgreSQL, MongoDB, dan Redis sudah aktif.


🐳 Docker Compose (Production)

Untuk menjalankan seluruh stack (API + Worker + Frontend + Database) dengan satu perintah:

# Dari root repository
docker compose up --build
πŸ“‹ Detail Service Docker Compose (klik untuk expand)
Service Image Port Deskripsi
api flowforge/api (multi-stage) 3000 NestJS API (DISABLE_WORKER=true)
worker flowforge/api (multi-stage) β€” BullMQ Worker (DISABLE_WORKER=false)
web flowforge/web (Nginx) 5173 React frontend
postgres postgres:16-alpine 5432 Database relasional
mongodb mongo:7 27017 Store log eksekusi
redis redis:7-alpine 6379 Queue & rate limiting
πŸ” Verifikasi Docker Compose Berjalan
# Cek semua container berjalan
docker compose ps

# Cek health status
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# Cek log API
docker compose logs api -f

# Test endpoint health
curl http://localhost:3000/api/v1/health
πŸ›‘ Menghentikan & Membersihkan
# Hentikan semua container
docker compose down

# Hentikan dan hapus volume (DATA AKAN HILANG)
docker compose down -v

# Rebuild image
docker compose up --build --force-recreate

πŸ“‹ Dokumentasi API

Swagger UI Interaktif: http://localhost:3000/api/docs

πŸ” Auth Endpoints
Method Endpoint Akses Deskripsi
POST /api/v1/auth/register Publik Registrasi tenant baru + user Admin
POST /api/v1/auth/login Publik Login, return accessToken + refreshToken
POST /api/v1/auth/refresh Publik (refresh token) Perbarui access token
POST /api/v1/auth/logout Semua role Invalidasi refresh token

Contoh Register:

curl -X POST http://localhost:3000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "tenantName": "Acme Corp",
    "tenantSlug": "acme-corp",
    "email": "admin@acme.com",
    "password": "Password123!"
  }'

Contoh Login:

curl -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@acme.com", "password": "Password123!"}'
# Response: { "accessToken": "eyJ...", "refreshToken": "eyJ..." }
πŸ“‘ Workflow Endpoints
Method Endpoint Role Deskripsi
GET /api/v1/workflows Semua List workflow (cursor pagination)
POST /api/v1/workflows Admin, Editor Buat workflow baru dengan definisi DAG
GET /api/v1/workflows/:id Semua Detail workflow + versi aktif
PUT /api/v1/workflows/:id Admin, Editor Update β†’ membuat versi baru otomatis
DELETE /api/v1/workflows/:id Admin Hapus workflow (soft-delete)
GET /api/v1/workflows/:id/versions Semua Riwayat semua versi
POST /api/v1/workflows/:id/versions/:vId/rollback Admin, Editor Rollback ke versi tertentu
POST /api/v1/workflows/:id/trigger Admin, Editor Trigger manual eksekusi
POST /api/v1/webhooks/:token/trigger Publik (token) Trigger via webhook URL

Contoh Create Workflow:

curl -X POST http://localhost:3000/api/v1/workflows \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order Processing Pipeline",
    "description": "Validate β†’ Process β†’ Notify",
    "definitionJson": {
      "nodes": [
        { "id": "validate", "type": "http", "config": { "url": "https://api.example.com/validate", "method": "POST" } },
        { "id": "process",  "type": "delay", "config": { "durationMs": 2000 } },
        { "id": "notify",   "type": "script", "config": { "script": "output.message = \"Order processed: \" + steps.validate.output.orderId;" } }
      ],
      "edges": [
        { "from": "validate", "to": "process" },
        { "from": "process",  "to": "notify" }
      ]
    }
  }'
πŸƒ Run Endpoints
Method Endpoint Role Deskripsi
GET /api/v1/runs Semua List run (filter: ?status=failed&createdAfter=...)
GET /api/v1/runs/:id Semua Detail run + status setiap step
GET /api/v1/runs/:id/logs Semua Log eksekusi per step dari MongoDB
GET /api/v1/runs/health-summary Semua Agregat 24 jam (active, success rate, avg duration)
πŸ€– AI Endpoint
Method Endpoint Role Deskripsi
POST /api/v1/ai/generate-workflow Admin, Editor Deskripsi natural language β†’ DAG JSON

Contoh Request:

curl -X POST http://localhost:3000/api/v1/ai/generate-workflow \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description": "Tunggu 3 detik, kemudian ambil data dari https://httpbin.org/get, lalu cek apakah status responsenya 200"}'

Contoh Response:

{
  "nodes": [
    { "id": "wait", "type": "delay", "config": { "durationMs": 3000 } },
    { "id": "fetch", "type": "http", "config": { "url": "https://httpbin.org/get", "method": "GET" } },
    { "id": "check", "type": "script", "config": { "script": "output.ok = steps.fetch.status === 200;" } }
  ],
  "edges": [
    { "from": "wait", "to": "fetch" },
    { "from": "fetch", "to": "check" }
  ]
}
πŸ“Š Header Rate Limit

Setiap response dari endpoint yang terproteksi menyertakan header berikut:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1720000000

Jika melebihi limit (100 request/menit per tenant):

HTTP 429 Too Many Requests
{"statusCode": 429, "message": "Rate limit exceeded. Try again in X seconds."}

πŸ§ͺ Panduan Testing Manual (Browser)

Panduan ini memandu Anda melalui seluruh fitur FlowForge secara end-to-end melalui browser, tanpa perlu alat tambahan.

Tahap 1: Registrasi & Login Akun Tenant
  1. Buka browser dan navigasi ke http://localhost:5173
  2. Pilih tab "Create Account" (Register)
  3. Isi formulir:
    • Organization Name: Acme Corp
    • Slug (ID unik): acme-corp
    • Email: admin@acme.com
    • Password: Password123!
  4. Klik "Create Account"
  5. βœ… Berhasil jika: diredirect ke halaman Dashboard dan muncul nama tenant di sidebar
Tahap 2: Melihat System Health Dashboard
  1. Setelah login, Anda berada di halaman Dashboard
  2. Perhatikan 4 kartu statistik agregat 24 jam:
    • Active Runs β€” jumlah run yang sedang berjalan
    • Success Rate β€” persentase run berhasil
    • Avg Duration β€” rata-rata durasi eksekusi (ms)
    • Total Runs β€” total run dalam 24 jam terakhir
  3. Di bagian bawah, terdapat daftar Recent Runs (awalnya kosong)
  4. βœ… Data di-cache selama 30 detik, refresh otomatis setiap 30 detik
Tahap 3: Membuat Workflow via AI Natural Language Builder
  1. Klik "AI Builder" di sidebar kiri
  2. Di kolom "Describe Your Workflow", ketik prompt dalam bahasa alami:

    "Tunggu 2 detik, kemudian ambil data dari https://httpbin.org/get, dan gunakan script untuk mengecek apakah status responsenya 200"

  3. Klik tombol "✨ Generate DAG"
  4. Tunggu beberapa detik β€” sistem akan:
    • Mengirim prompt ke Gemini API
    • Menerima DAG JSON terstruktur
    • Memvalidasi DAG (cycle detection, node type check)
    • Menampilkan hasil di panel kanan
  5. Review nodes yang dihasilkan di panel kanan
  6. Klik "πŸ’Ύ Save as Workflow", masukkan nama: Order Check Pipeline
  7. βœ… Berhasil jika: muncul notifikasi sukses dan workflow tersimpan
Tahap 4: Mengelola & Men-trigger Workflow
  1. Klik "Workflows" di sidebar
  2. Anda akan melihat kartu workflow yang baru dibuat dengan info:
    • Nama workflow
    • Versi aktif (v1)
    • Jumlah step
  3. Klik tombol "β–Ά Trigger" pada kartu workflow
  4. βœ… Berhasil jika: muncul notifikasi "Run started!" dan status berubah menjadi queued
Tahap 5: Memantau Eksekusi Real-Time
  1. Klik "Run History" di sidebar
  2. Pilih run terbaru dari daftar di panel kiri
  3. Di panel tengah, grafik DAG akan dirender menggunakan React Flow
  4. Amati perubahan warna node secara real-time (via WebSocket):
    • Abu-abu = pending (belum berjalan)
    • Kuning/Amber = running (sedang berjalan)
    • Hijau = success (berhasil)
    • Merah = failed (gagal)
  5. Di bagian bawah, panel "Execution Logs" menampilkan log real-time dari MongoDB:
    • Timestamp setiap event
    • Durasi per step
    • Detail error jika ada
  6. βœ… Berhasil jika: node berubah warna tanpa perlu refresh halaman
Tahap 6: Membuat Versi Baru & Rollback
  1. Kembali ke halaman "Workflows"
  2. Klik "Edit" pada workflow Anda
  3. Tambahkan step baru atau ubah konfigurasi step yang ada
  4. Klik "Save" β€” sistem otomatis membuat versi baru (v2)
  5. Klik "Versions" untuk melihat riwayat versi
  6. Klik "Rollback" pada v1 untuk kembali ke versi sebelumnya
  7. βœ… Berhasil jika: current_version_id kembali ke versi 1
Tahap 7: Uji Isolasi Multi-Tenant
  1. Buka tab browser baru / mode incognito
  2. Navigasi ke http://localhost:5173 dan buat akun tenant baru:
    • Organization Name: Beta Company
    • Slug: beta-company
    • Email: admin@beta.com
    • Password: Password123!
  3. Login sebagai admin@beta.com
  4. βœ… Berhasil jika: Anda tidak melihat workflow milik Acme Corp di daftar workflow tenant Beta β€” membuktikan isolasi tenant bekerja
Tahap 8: Uji Rate Limiting

Buka terminal dan jalankan perintah berikut (pastikan ACCESS_TOKEN sudah diisi):

# Kirim 105 request berturut-turut
for i in {1..105}; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    http://localhost:3000/api/v1/workflows)
  echo "Request $i: $STATUS"
done

βœ… Berhasil jika: request ke-101 dan seterusnya mengembalikan HTTP 429 Too Many Requests


πŸ”„ CI/CD Pipeline

Pipeline CI berjalan otomatis setiap push ke branch master, main, atau develop, dan setiap Pull Request ke master/main.

Push / PR
   β”‚
   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  1. Lint  │───▢│  2. Unit & E2E Tests  │───▢│  3. TS Build   │───▢│  4. Docker Build+Push β”‚
β”‚  ESLint   β”‚    β”‚  Jest + Prisma migrateβ”‚    β”‚  nest build    β”‚    β”‚  ghcr.io/api:sha     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    (PostgreSQL + MongoDB
                     + Redis via Services)
πŸ“„ Detail Setiap Job CI (klik untuk expand)

Job 1: Lint

  • Node.js 22, npm ci
  • Menjalankan ESLint pada seluruh source TypeScript

Job 2: Unit & Integration Tests

  • Spin up PostgreSQL 16, MongoDB 7, Redis 7 sebagai GitHub Actions Service
  • Jalankan Prisma migrations: npx prisma migrate deploy
  • Unit tests: npm run test
  • E2E tests: npm run test:e2e (Auth, Workflows, Tenant Isolation, Trigger, Rate Limit)

Job 3: TypeScript Build

  • npm run build β†’ compile TypeScript ke dist/
  • Memastikan tidak ada type error

Job 4: Docker Build & Push (hanya di master/main)

  • Build multi-stage Docker image
  • Push ke GitHub Container Registry: ghcr.io/himdeunn/flowforge/api:latest dan ghcr.io/himdeunn/flowforge/api:{sha}

πŸ—„οΈ Database Design

PostgreSQL Schema β€” 6 Model Relasional (klik untuk expand)
tenants ───────────────────────────────────────────────────
β”œβ”€β”€ id              uuid (PK)
β”œβ”€β”€ name            varchar(255) NOT NULL
β”œβ”€β”€ slug            varchar(100) UNIQUE NOT NULL
└── created_at      timestamptz DEFAULT now()

users ──────────────────────────────────────────────────────
β”œβ”€β”€ id              uuid (PK)
β”œβ”€β”€ tenant_id       uuid (FK β†’ tenants.id)
β”œβ”€β”€ email           varchar(255) NOT NULL
β”œβ”€β”€ password_hash   varchar(255) NOT NULL
β”œβ”€β”€ role            enum(admin, editor, viewer)
β”œβ”€β”€ created_at      timestamptz DEFAULT now()
└── UNIQUE(tenant_id, email)

workflow_definitions ───────────────────────────────────────
β”œβ”€β”€ id                  uuid (PK)
β”œβ”€β”€ tenant_id           uuid (FK β†’ tenants.id)
β”œβ”€β”€ name                varchar(255) NOT NULL
β”œβ”€β”€ description         text
β”œβ”€β”€ current_version_id  uuid (FK β†’ workflow_versions.id) nullable
β”œβ”€β”€ webhook_token       varchar(64) UNIQUE nullable
β”œβ”€β”€ cron_expression     varchar(100) nullable
β”œβ”€β”€ is_active           boolean DEFAULT true
β”œβ”€β”€ created_by          uuid (FK β†’ users.id)
β”œβ”€β”€ created_at          timestamptz
└── updated_at          timestamptz

workflow_versions ──────────────────────────────────────────
β”œβ”€β”€ id              uuid (PK)
β”œβ”€β”€ workflow_id     uuid (FK β†’ workflow_definitions.id)
β”œβ”€β”€ version_number  integer NOT NULL
β”œβ”€β”€ definition_json jsonb NOT NULL        ← DAG: nodes[] + edges[]
β”œβ”€β”€ created_by      uuid (FK β†’ users.id)
β”œβ”€β”€ created_at      timestamptz
└── UNIQUE(workflow_id, version_number)

workflow_runs ──────────────────────────────────────────────
β”œβ”€β”€ id              uuid (PK)
β”œβ”€β”€ workflow_id     uuid (FK β†’ workflow_definitions.id)
β”œβ”€β”€ version_id      uuid (FK β†’ workflow_versions.id)
β”œβ”€β”€ tenant_id       uuid (FK β†’ tenants.id)
β”œβ”€β”€ status          enum(queued, running, completed, failed, timed_out)
β”œβ”€β”€ triggered_by    enum(manual, cron, webhook)
β”œβ”€β”€ started_at      timestamptz
β”œβ”€β”€ completed_at    timestamptz
└── INDEX(tenant_id, created_at DESC)     ← optimasi run history query

step_runs ──────────────────────────────────────────────────
β”œβ”€β”€ id          uuid (PK)
β”œβ”€β”€ run_id      uuid (FK β†’ workflow_runs.id)
β”œβ”€β”€ step_id     varchar(100) NOT NULL     ← node.id dari DAG
β”œβ”€β”€ status      enum(pending, running, success, failed, timed_out, skipped)
β”œβ”€β”€ attempts    integer DEFAULT 0
β”œβ”€β”€ started_at  timestamptz
└── completed_at timestamptz
MongoDB Collection β€” Execution Logs (klik untuk expand)
Collection: execution_logs
Indexes: { runId: 1, stepId: 1, timestamp: -1 }

Document Schema:
{
  "_id":       ObjectId,
  "runId":     string,      // FK ke workflow_runs.id
  "stepId":    string,      // node.id dari DAG
  "level":     "info" | "warn" | "error",
  "message":   string,
  "data":      object,      // Output/error detail, fleksibel per step type
  "attempt":   number,      // Attempt ke-berapa (untuk retry)
  "timestamp": Date
}

Alasan MongoDB untuk log: Volume tinggi, write-heavy, skema output per step-type berbeda-beda (HTTP step punya statusCode/headers, script step punya output, delay step punya durationMs). Tidak ada JOIN kompleks yang diperlukan. Lebih efisien disimpan sebagai dokumen append-only.


πŸ€– Fitur AI β€” Natural Language Builder

Cara Kerja Internal (klik untuk expand)
User Input (deskripsi teks)
       β”‚
       β–Ό
POST /api/v1/ai/generate-workflow
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AI Service                             β”‚
β”‚  1. Format prompt dengan template       β”‚
β”‚     yang menyertakan:                   β”‚
β”‚     - Jenis step yang valid             β”‚
β”‚     - Format JSON yang diharapkan       β”‚
β”‚     - Contoh DAG valid                  β”‚
β”‚  2. Kirim ke Gemini 2.5 Flash           β”‚
β”‚     (dengan key rotation otomatis)      β”‚
β”‚  3. Parse response JSON                 β”‚
β”‚  4. Validasi dengan DAG Parser          β”‚
β”‚     (cycle detection, type check)       β”‚
β”‚  5. Jika invalid β†’ retry hingga 2x      β”‚
β”‚  6. Jika masih invalid β†’ return 422     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
Response: DAG JSON valid

Tipe Step yang Didukung:

Tipe Konfigurasi
http url, method, headers, body
script script (JavaScript sandboxed)
delay durationMs
condition expression (evaluasi steps.stepId.output.field)

Rotasi API Key Otomatis: Sistem mengelola array dari GEMINI_API_KEY hingga GEMINI_API_KEY_5. Jika satu key terkena rate limit (HTTP 429 dari Gemini API), sistem otomatis beralih ke key berikutnya, tanpa interupsi bagi pengguna.


πŸ“‘ WebSocket Real-Time Events

Koneksi WebSocket menggunakan Socket.IO di endpoint /ws.

Detail Event & Room Structure (klik untuk expand)

Join Room:

// Client join room untuk memantau run tertentu
socket.emit('join:run', { runId: 'uuid-run-id' });

Events yang Dikirim Server:

Event Payload Keterangan
run:started { runId, workflowId, startedAt } Run dimulai, status berubah ke running
step:status_changed { runId, stepId, status, attempt } Step berubah status
run:completed { runId, completedAt, duration } Seluruh run berhasil
run:error { runId, stepId, error, failedAt } Run gagal pada step tertentu

Room Naming Convention:

tenant:{tenantId}:run:{runId}

Contoh: tenant:550e8400-e29b-41d4-a716-446655440000:run:6ba7b810-9dad-11d1-80b4-00c04fd430c8

Contoh Koneksi (JavaScript):

import { io } from 'socket.io-client';

const socket = io('http://localhost:3000', {
  auth: { token: 'Bearer eyJ...' }
});

socket.emit('join:run', { runId: 'your-run-id' });

socket.on('step:status_changed', (data) => {
  console.log(`Step ${data.stepId} β†’ ${data.status}`);
  // Update node color di React Flow
});

βš–οΈ Trade-offs & Rencana Perbaikan

Area Keputusan Saat Ini Trade-off Rencana Perbaikan
Script Sandboxing Node.js vm module Ringan, tapi tidak mencegah sandbox breakout sepenuhnya Migrate ke isolated-vm untuk production security
WebSocket Auth Join room via runId query param Mudah diimplementasikan, kurang aman Validasi Bearer token di WebSocket handshake
Refresh Token Storage Redis (bukan httpOnly cookie) Lebih mudah di-test, tapi lebih rentan XSS jika client tidak hati-hati Migrate ke httpOnly cookie + CSRF token
Rate Limiting Manual Redis sliding window Full kontrol per-tenant, tapi lebih banyak kode Pertimbangkan @nestjs/throttler dengan custom storage adapter
Worker Process In-process di development Mudah deploy, tapi tidak bisa scale worker terpisah Di production: container worker terpisah (sudah ada di docker-compose)
Cron Scheduling Disimpan sebagai cron_expression di DB Cron hanya trigger jika API/worker berjalan Pertimbangkan BullMQ repeatable jobs atau dedicated scheduler
Frontend Styling TailwindCSS + Vanilla CSS Sangat fleksibel, build step terintegrasi di Vite Selesai (Diimplementasikan estetika Minimalist Editorial premium)

πŸ“ Keputusan Implementasi

Setiap keputusan yang menyimpang dari PRD atau menambahkan detail implementasi yang tidak eksplisit disebutkan di PRD didokumentasikan di CHANGELOG-DECISIONS.md.

Ringkasan Keputusan Utama (klik untuk expand)
Task Keputusan Alasan Singkat
Task 1.1 Node.js 22 (bukan 20 sesuai PRD) Node 22 tersedia di mesin dev, CI diselaraskan ke versi yang sama
Task 1.1 Laragon untuk DB dev (bukan Docker) Docker WSL2 lambat untuk hot-reload
Task 1.2 JSONB (bukan JSON biasa) Mendukung indexing dan query lebih efisien
Task 2.1 Refresh token di Redis Lebih mudah di-invalidate & di-test dibanding httpOnly cookie
Task 2.2 vm module untuk script step isolated-vm butuh native binding yang sulit di semua deployment
Task 2.4 BullMQ via host/port config Menghindari TypeScript conflict dua versi ioredis
Task 2.5 Prisma Proxy (bukan middleware) Lebih transparan, tidak perlu register per-model
Task 2.6 Redis sliding window manual @nestjs/throttler tidak support custom key per-tenant
Task 3.2 TailwindCSS + Vanilla CSS Awalnya menggunakan Vanilla CSS, namun kemudian dimigrasi ke TailwindCSS + Shadcn-inspired Minimalist Editorial untuk visual premium tingkat lanjut
Task 3.6 CI NODE_VERSION dari 20 ke 22 npm v11 (Node 22) menghasilkan lockfile format berbeda dengan npm v10 (Node 20)

Lihat CHANGELOG-DECISIONS.md untuk detail penuh setiap keputusan.


FlowForge β€” Dibangun dengan ❀️ untuk Technical Assessment Sevima

NestJS Β· React Β· PostgreSQL Β· MongoDB Β· Redis Β· BullMQ Β· Socket.IO Β· Gemini AI

About

SEVIMA Internship Assessment ( I must Win!! )

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages