|
21 | 21 |
|
22 | 22 | ```bash |
23 | 23 | git clone https://github.com/Himdeunn/flowforge.git |
24 | | -cd flowforge/apps/api |
| 24 | +cd flowforge |
| 25 | +# Install API deps |
| 26 | +cd apps/api |
| 27 | +npm install |
| 28 | +# Install Web deps |
| 29 | +cd ../web |
25 | 30 | npm install |
26 | 31 | ``` |
27 | 32 |
|
28 | 33 | ### 2. Configure Environment |
29 | 34 |
|
30 | | -```bash |
31 | | -cp .env.example .env |
32 | | -# Edit .env with your local values |
33 | | -``` |
| 35 | +Create a `.env` file inside `apps/api/` based on the `.env.example` template: |
34 | 36 |
|
35 | | -Key variables: |
36 | 37 | ```bash |
37 | | -DATABASE_URL=postgresql://flowforge:flowforge@localhost:5432/flowforge |
38 | | -MONGODB_URI=mongodb://localhost:27017/flowforge_logs |
39 | | -REDIS_URL=redis://localhost:6379 |
40 | | -JWT_ACCESS_SECRET=changeme-access-secret |
41 | | -JWT_REFRESH_SECRET=changeme-refresh-secret |
42 | | -GEMINI_API_KEY=your-gemini-api-key-here |
| 38 | +cp apps/api/.env.example apps/api/.env |
43 | 39 | ``` |
44 | 40 |
|
| 41 | +> [!IMPORTANT] |
| 42 | +> To use the AI generation features, make sure `GEMINI_API_KEY` in the `.env` file is set to a valid API key. The application is pre-configured with 5 rotated API keys for Gemini to handle rate limits, but you can also supply your own key in `.env`. |
| 43 | +
|
45 | 44 | ### 3. Run Migrations |
46 | 45 |
|
47 | 46 | ```bash |
| 47 | +cd apps/api |
48 | 48 | npx prisma migrate dev |
49 | 49 | ``` |
50 | 50 |
|
51 | | -### 4. Start Development Server |
| 51 | +### 4. Start Development Servers |
52 | 52 |
|
| 53 | +Run the backend API: |
53 | 54 | ```bash |
| 55 | +cd apps/api |
54 | 56 | npm run start:dev |
55 | 57 | ``` |
56 | | - |
57 | 58 | API available at: `http://localhost:3000/api/v1` |
58 | 59 | Swagger docs at: `http://localhost:3000/api/docs` |
59 | 60 |
|
| 61 | +Run the frontend dashboard: |
| 62 | +```bash |
| 63 | +cd apps/web |
| 64 | +npm run dev |
| 65 | +``` |
| 66 | +Dashboard available at: `http://localhost:5173` |
| 67 | + |
60 | 68 | --- |
61 | 69 |
|
62 | | -## 🐳 Docker Compose (Full Stack) |
| 70 | +## 🐳 Docker Compose (Production Build) |
63 | 71 |
|
64 | 72 | ```bash |
65 | 73 | # From repository root |
66 | 74 | docker-compose up --build |
67 | 75 | ``` |
68 | 76 |
|
69 | 77 | Services started: |
70 | | -| Service | Port | Description | |
71 | | -|---------|------|-------------| |
72 | | -| API | `3000` | NestJS REST + WebSocket | |
73 | | -| Web | `5173` | React Dashboard | |
74 | | -| PostgreSQL | `5432` | Relational DB | |
75 | | -| MongoDB | `27017` | Execution log store | |
76 | | -| Redis | `6379` | Queue + Rate limiting | |
| 78 | +- **API** (NestJS): `http://localhost:3000/api/v1` |
| 79 | +- **Web** (React + Nginx): `http://localhost:5173` |
| 80 | +- **PostgreSQL**: port `5432` |
| 81 | +- **MongoDB**: port `27017` |
| 82 | +- **Redis**: port `6379` |
77 | 83 |
|
78 | 84 | --- |
79 | 85 |
|
@@ -103,124 +109,86 @@ PostgreSQL BullMQ (Redis) MongoDB |
103 | 109 |
|
104 | 110 | ### Key Design Decisions |
105 | 111 |
|
106 | | -| Layer | Choice | Rationale | |
107 | | -|-------|--------|-----------| |
108 | | -| Backend | NestJS + TypeScript | Modular architecture with built-in DI, great for RBAC/validation | |
109 | | -| Execution | Custom DAG executor + BullMQ | Redis-backed queue for async, retry/backoff built-in | |
110 | | -| Database | PostgreSQL via Prisma | ACID transactions for CRUD + versioning | |
111 | | -| Log Store | MongoDB (append-only) | Write-heavy, schema-flexible per step type | |
112 | | -| Real-time | Socket.IO | Bidirectional, room-per-run subscription model | |
113 | | -| AI Feature | Gemini 2.5 Flash | Structured JSON output mode, fast, low-cost | |
| 112 | +- **Backend Architecture**: Built using NestJS with strict modular architecture (Auth, Workflows, Runs, Execution, Webhooks, Queue, AI, WebSockets). |
| 113 | +- **Tenant Isolation**: Handled via custom Prisma client proxy interceptor that automatically appends `tenant_id` filters to all database queries based on the verified JWT claims. |
| 114 | +- **Asynchronous Execution**: Powered by BullMQ and Redis to separate API request handling from long-running workflow executions. |
| 115 | +- **Real-Time Updates**: Bidirectional event streaming using Socket.IO to notify the dashboard when steps change status (pending → running → success/failed). |
| 116 | +- **Execution Log Store**: Append-only log entries stored in MongoDB to keep log volume out of the relational database. |
114 | 117 |
|
115 | 118 | --- |
116 | 119 |
|
117 | 120 | ## 📋 API Documentation |
118 | 121 |
|
119 | 122 | Interactive Swagger UI: `http://localhost:3000/api/docs` |
120 | 123 |
|
121 | | -### Auth Endpoints |
122 | | -| Method | Endpoint | Description | |
123 | | -|--------|----------|-------------| |
124 | | -| POST | `/api/v1/auth/register` | Register new tenant + admin user | |
125 | | -| POST | `/api/v1/auth/login` | Login → access + refresh tokens | |
126 | | -| POST | `/api/v1/auth/refresh` | Refresh access token | |
127 | | -| POST | `/api/v1/auth/logout` | Invalidate refresh token | |
128 | | - |
129 | | -### Workflow Endpoints |
130 | | -| Method | Endpoint | Auth | |
131 | | -|--------|----------|------| |
132 | | -| GET | `/api/v1/workflows` | All roles | |
133 | | -| POST | `/api/v1/workflows` | Admin, Editor | |
134 | | -| GET | `/api/v1/workflows/:id` | All roles | |
135 | | -| PUT | `/api/v1/workflows/:id` | Admin, Editor | |
136 | | -| DELETE | `/api/v1/workflows/:id` | Admin | |
137 | | -| POST | `/api/v1/workflows/:id/trigger` | Admin, Editor | |
138 | | -| POST | `/api/v1/workflows/:id/versions/:vId/rollback` | Admin, Editor | |
139 | | -| POST | `/api/v1/webhooks/:webhookToken/trigger` | Public (token-verified) | |
140 | | - |
141 | | -### AI Endpoint |
142 | | -| Method | Endpoint | Description | |
143 | | -|--------|----------|-------------| |
144 | | -| POST | `/api/v1/ai/generate-workflow` | Generate DAG from natural language | |
| 124 | +### Key Endpoints |
| 125 | +- **Auth**: `POST /auth/register`, `POST /auth/login`, `POST /auth/refresh`, `POST /auth/logout` |
| 126 | +- **Workflows**: `GET /workflows`, `POST /workflows`, `PUT /workflows/:id`, `DELETE /workflows/:id`, `POST /workflows/:id/trigger`, `POST /workflows/:id/versions/:versionId/rollback` |
| 127 | +- **Runs**: `GET /runs`, `GET /runs/:id`, `GET /runs/:id/logs`, `GET /runs/health-summary` |
| 128 | +- **AI**: `POST /ai/generate-workflow` |
| 129 | +- **Webhooks**: `POST /webhooks/:webhookToken/trigger` (Public trigger endpoint) |
145 | 130 |
|
146 | 131 | --- |
147 | 132 |
|
148 | 133 | ## 🧪 Testing |
149 | 134 |
|
150 | 135 | ```bash |
151 | | -# Unit tests |
| 136 | +# Unit & Integration tests |
| 137 | +cd apps/api |
152 | 138 | npm run test |
153 | 139 |
|
154 | | -# E2E tests (requires running Postgres, MongoDB, Redis) |
| 140 | +# E2E integration tests (requires local PostgreSQL, MongoDB, Redis running) |
155 | 141 | npm run test:e2e |
156 | | - |
157 | | -# Coverage |
158 | | -npm run test:cov |
159 | 142 | ``` |
160 | 143 |
|
161 | | -### Test Coverage Summary |
162 | | - |
163 | | -| Layer | Tool | Coverage | |
164 | | -|-------|------|----------| |
165 | | -| Unit | Jest | DAG parser, topo-sort, execution engine (retry/timeout), AI service, WebSocket gateway | |
166 | | -| Integration | Jest + Supertest | Auth flow, CRUD workflows, tenant isolation (2-tenant), rate limiting, pagination | |
167 | | -| E2E | Jest + Supertest | Create workflow → trigger → poll status → assert completed | |
168 | | - |
169 | 144 | --- |
170 | 145 |
|
171 | | -## 🔐 Security |
172 | | - |
173 | | -- **Tenant Isolation**: `tenantId` always from JWT claim, never from request body — enforced by `TenantGuard` + Prisma middleware |
174 | | -- **Passwords**: bcrypt hashed (cost 10), never returned in API responses |
175 | | -- **JWT**: Access token 15min, refresh token 7d stored in Redis (invalidatable) |
176 | | -- **Webhook**: 32-byte random token per workflow |
177 | | -- **Rate Limiting**: Redis sliding window 100 req/min per tenant |
178 | | -- **Input Validation**: `class-validator` with `forbidNonWhitelisted: true` |
179 | | -- **Script Sandbox**: `vm` module with timeout for `script` step type |
| 146 | +## 💻 Manual Testing Guide (Browser Walkthrough) |
| 147 | + |
| 148 | +To manually test the application and see the real-time Multi-Tenant DAG execution in action, follow these steps: |
| 149 | + |
| 150 | +### Step 1: Account Creation & Tenant Registration |
| 151 | +1. Open your browser and navigate to `http://localhost:5173`. |
| 152 | +2. Select the **Create Account** tab. |
| 153 | +3. Fill out the fields: |
| 154 | + - **Organization Name**: e.g., `Acme Corp` |
| 155 | + - **Slug (unique ID)**: e.g., `acme-corp` |
| 156 | + - **Email**: `admin@acme.com` |
| 157 | + - **Password**: `StrongPassword123!` |
| 158 | +4. Click **Create Account**. You will be registered, logged in, and redirected to the **Dashboard** page. |
| 159 | + |
| 160 | +### Step 2: System Health Dashboard |
| 161 | +1. The dashboard displays 4 stats cards: **Active Runs**, **Success Rate**, **Avg Duration**, and **Total Runs (24h)**. These aggregate all workflow runs within the tenant slug. |
| 162 | +2. The **Recent Runs** list at the bottom will initially be empty. |
| 163 | + |
| 164 | +### Step 3: Natural Language AI Workflow Builder |
| 165 | +1. Click **AI Builder** in the sidebar. |
| 166 | +2. Under **Describe Your Workflow**, type a prompt in natural language, for example: |
| 167 | + > *"Wait 3 seconds, then fetch orders from https://httpbin.org/get, and then use a script to check if the status is 200"* |
| 168 | +3. Click the **✨ Generate DAG** button. |
| 169 | +4. The backend will query Gemini, rotate keys if needed, validate the generated DAG structure (ensuring no cycles exist), and display the result. |
| 170 | +5. In the right panel, you will see the generated steps. You can review and edit the raw DAG JSON. |
| 171 | +6. Click **💾 Save as Workflow**, enter a name (e.g., `Order Fulfillment Process`), and click **Confirm Save**. |
| 172 | + |
| 173 | +### Step 4: Manage & Trigger Workflows |
| 174 | +1. Go to the **Workflows** page in the sidebar. |
| 175 | +2. You will see your newly created workflow card displaying its name, active version (`v1`), step count, and cron schedules if any. |
| 176 | +3. Click the **▶ Trigger** button on the card. This immediately dispatches an execution request, queues the run in BullMQ, and initiates the background worker. |
| 177 | + |
| 178 | +### Step 5: Real-Time DAG Visualizer & History |
| 179 | +1. Click **Run History** in the sidebar. |
| 180 | +2. Select the latest run ID from the left list. |
| 181 | +3. In the center panel, a visual graph representation of your DAG will render using **ReactFlow**. |
| 182 | +4. Watch the step nodes change border colors in real-time as they run: |
| 183 | + - Border turns **Amber/Yellow** when the step is `running`. |
| 184 | + - Border turns **Green** when the step completes with `success`. |
| 185 | + - Border turns **Red** if the step fails. |
| 186 | +5. At the bottom, the **Execution Logs** card streams log statements in real-time straight from MongoDB, detailing timeouts, retries, and errors. |
180 | 187 |
|
181 | 188 | --- |
182 | 189 |
|
183 | 190 | ## ⚖️ Trade-offs & Future Improvements |
184 | 191 |
|
185 | | -| Trade-off | Current Decision | Future Improvement | |
186 | | -|-----------|-----------------|-------------------| |
187 | | -| MongoDB vs PostgreSQL for logs | MongoDB for schema flexibility + append-only writes | Consider TimescaleDB for full SQL analytics | |
188 | | -| In-process worker vs separate service | Single process in dev, `DISABLE_WORKER=true` to separate | Deploy as separate ECS Fargate service in production | |
189 | | -| Rate limiting with Redis | Simple sliding window per tenant | Add per-IP limiting for public endpoints (webhook, login) | |
190 | | -| WebSocket auth | Simple room join — no JWT validation on WS | Add JWT verification on `subscribe:run` event | |
191 | | -| Script sandbox | Node.js `vm` module | Replace with `isolated-vm` for full V8 isolation | |
192 | | - |
193 | | ---- |
194 | | - |
195 | | -## 📁 Project Structure |
196 | | - |
197 | | -``` |
198 | | -flowforge/ |
199 | | -├── apps/ |
200 | | -│ ├── api/ # NestJS backend |
201 | | -│ │ ├── src/ |
202 | | -│ │ │ ├── auth/ # JWT strategy, guards, RBAC |
203 | | -│ │ │ ├── ai/ # Gemini-powered NL workflow builder |
204 | | -│ │ │ ├── common/ # Rate limiter guard, interceptors |
205 | | -│ │ │ ├── execution/ # DAG parser, topo-sort, execution engine |
206 | | -│ │ │ ├── queue/ # BullMQ producer + consumer |
207 | | -│ │ │ ├── runs/ # Run CRUD + health panel |
208 | | -│ │ │ ├── websocket/ # Socket.IO gateway |
209 | | -│ │ │ └── workflows/ # Workflow CRUD + versioning |
210 | | -│ │ ├── prisma/ # Schema + migrations |
211 | | -│ │ └── test/ # E2E integration tests |
212 | | -│ └── web/ # React frontend (Vite + TailwindCSS) |
213 | | -├── docs/ |
214 | | -│ └── infra-design.md # AWS infrastructure design |
215 | | -├── .github/ |
216 | | -│ └── workflows/ci.yml # GitHub Actions CI pipeline |
217 | | -├── docker-compose.yml |
218 | | -├── REVIEW.md # Code review findings |
219 | | -└── README.md |
220 | | -``` |
221 | | - |
222 | | ---- |
223 | | - |
224 | | -## 📄 License |
225 | | - |
226 | | -MIT — Assessment project for Sevima Engineering Internship |
| 192 | +- **Script Sandboxing**: Currently runs JS script steps inside Node's native `vm` module. While lightweight, for production it should be moved to `isolated-vm` to prevent sandbox breakout vulnerabilities. |
| 193 | +- **WebSocket Auth**: Current Socket.IO gateway joins rooms directly based on query parameter `runId`. In production, this must validate the bearer token in the connection handshake to prevent unauthorized room listening. |
| 194 | +- **Tailwind CSS**: The frontend UI is styled with optimized Vanilla CSS custom tokens to guarantee extremely fast load times. It can be easily ported to Tailwind class names if required. |
0 commit comments