Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
.env
.git
.gitignore
__pycache__/
db.sqlite3
.venv/
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["python","backend/manage.py","runserver","0.0.0.0:8000"]
299 changes: 21 additions & 278 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ This README is based on the current codebase, not the older planning documents i
- [What Works Today](#what-works-today)
- [Stack](#stack)
- [Repo Layout](#repo-layout)
- [Local Setup](#local-setup)
- [Background Jobs](#background-jobs)
- [Local Setup (Docker)](#local-setup-docker)
- [Frontend Behavior](#frontend-behavior)
- [API Surface](#api-surface)
- [Testing](#testing)
Expand Down Expand Up @@ -82,260 +81,25 @@ outreach_frontend/ # currently empty
*.md # product and architecture docs from earlier phases
```

## Local Setup
## Local Setup (Docker)
We use Docker to orchestrate the entire LeadOrbit stack locally, eliminating the need for manual virtual environments and background process management.

### 1. Create a virtual environment and install dependencies
*Prerequisites:*

```sh
python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS/Linux
source .venv/bin/activate

pip install -r requirements.txt
```

### 2. Create `backend/.env`

The backend reads environment variables from `backend/.env`.

```env
DEBUG=True
SECRET_KEY=change-me
BACKEND_BASE_URL=http://127.0.0.1:8000
FRONTEND_BASE_URL=http://127.0.0.1:8080

CELERY_TASK_ALWAYS_EAGER=true
CELERY_BROKER_URL=redis://localhost:6379/0
ENABLE_AUTO_REPLY_DETECTION=false
LAUNCH_IMMEDIATE_PASSES=1

OPENROUTER_API_KEY=
OPENROUTER_MODEL=mistralai/mistral-nemo
OPENROUTER_APP_URL=http://127.0.0.1:8080
OPENROUTER_APP_NAME=LeadOrbit Campaign Builder

GEMINI_API_KEY=

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://127.0.0.1:8000/api/v1/auth/google/callback

TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
```

### 3. Run migrations

```sh
python backend/manage.py migrate
```

### 4. Start the backend

```sh
cd backend
python manage.py runserver 8000
```

### 5. Serve the frontend

In a second terminal:

```sh
cd frontend
python -m http.server 8080
```

Open `http://127.0.0.1:8080/login.html`.

## Background Jobs

In local development, campaign processing and CSV import work without Redis because `CELERY_TASK_ALWAYS_EAGER=true` by default when `DEBUG=True`.

If you want true async/background execution:

1. Start Redis.
2. Set `CELERY_TASK_ALWAYS_EAGER=false` in `backend/.env`.
3. Run the Celery worker and beat scheduler:

```sh
cd backend
celery -A backend worker -l info
celery -A backend beat -l info
```

Reply polling only runs when `ENABLE_AUTO_REPLY_DETECTION=true`.

## Frontend Behavior

- The active frontend is the static `frontend/*.html` app. There is no active Node build step for the current UI.
- [`frontend/api.js`](frontend/api.js) defaults to `http://127.0.0.1:8000/api/v1` on localhost and the deployed Render API elsewhere.
- You can override the API base URL in the browser:

```js
localStorage.setItem('api_base_url', 'http://127.0.0.1:8000/api/v1');
```

Main screens:

- `login.html`
- `register.html`
- `dashboard.html`
- `leads.html`
- `campaigns.html`
- `campaign-builder.html`
- `analytics.html`
- `settings.html`

## API Surface

Core endpoints exposed by the current backend:

- `POST /api/v1/auth/register/`
- `GET/PATCH /api/v1/auth/me/`
- `POST /api/v1/token/`
- `POST /api/v1/token/refresh/`
- `GET/POST /api/v1/leads/`
- `POST /api/v1/leads/import_csv/`
- `GET/POST/PATCH/DELETE /api/v1/campaigns/`
- `POST /api/v1/campaigns/{id}/enroll/`
- `POST /api/v1/campaigns/{id}/launch/`
- `POST /api/v1/campaigns/ai-generate/`
- `GET /api/v1/analytics/dashboard/`
- `POST /api/v1/webhooks/email/`
- `GET /api/v1/connected-accounts/`
- `GET /api/v1/auth/google/login`
- `GET /api/v1/auth/google/callback`

## Testing

Run the backend test suite from `backend/`:

```sh
python manage.py test
```

Current repo state: `27` backend tests pass. The suite covers auth/profile updates, lead import, tenant isolation, campaign creation, campaign launch, non-email flow handling, conditional branching, connected-account ownership rules, reply polling, and AI fallback behavior.
- Docker and Docker Compose installed on your machine.

## Current Caveats

- Some older root planning documents still mention the original `Lime` name, but the active README, backend, and frontend runtime paths use `LeadOrbit`.
- The settings page shows a Gemini API key field, but it is not persisted from the UI. AI credentials are read from `backend/.env`.
- The danger-zone buttons in `settings.html` are presentational only right now.
- [`frontend/src`](frontend/src) and [`backend/config`](backend/config) look like leftover scaffold code and are not part of the main runtime path.
- Some endpoints are still MVP-grade and should be hardened before a production multi-tenant deployment. In particular, analytics currently uses unscoped aggregate queries, and a few utility endpoints are intentionally permissive.
- The root markdown docs describe product intent and planning. The codebase is the source of truth for current behavior.

## Integration Notes

- Gmail sending requires valid Google OAuth client credentials and a connected account from the Settings page.
- SMS and call steps require Twilio credentials and leads with phone numbers.
- If no AI credentials are configured, the campaign builder AI composer still returns a deterministic fallback draft instead of failing hard.

# LeadOrbit

Current branding note: the active app is branded as `LeadOrbit`; older planning documents in the repo may still mention the original `Lime` name.

LeadOrbit is a multi-tenant outbound outreach MVP built with Django REST Framework and a static HTML/JavaScript frontend. The implemented code supports organization signup, JWT auth, CSV lead import, campaign building, lead enrollment, Gmail sender connection, AI-assisted email drafting, webhook-based engagement tracking, and analytics pages.

This README is based on the current codebase, not the older planning documents in the repo root.

## πŸ“‘ Table of Contents
- [LeadOrbit](#leadorbit)
- [What Works Today](#what-works-today)
- [Stack](#stack)
- [Repo Layout](#repo-layout)
- [Local Setup](#local-setup)
- [Background Jobs](#background-jobs)
- [Frontend Behavior](#frontend-behavior)
- [API Surface](#api-surface)
- [Testing](#testing)
- [Current Caveats](#current-caveats)
- [Integration Notes](#integration-notes)

## What Works Today

- JWT auth with organization creation during signup
- Tenant-scoped users, leads, campaigns, and connected sender accounts
- CSV lead import with flexible column aliases and background task support
- Campaign builder with sequence editing, lead selection, sender selection, and launch flow
- Email sending through Gmail API when a Google account is connected
- SMS and call execution through Twilio when credentials are configured
- Open/reply/click/bounce tracking through the webhook endpoint
- AI draft generation through OpenRouter, with fallback draft generation when no API key is set
- Per-lead merge tag replacement, with Gemini-based send-time personalization when configured
- Dashboard and analytics pages backed by API data
- Celery tasks for CSV import, campaign processing, and reply polling

Execution status by step type:

- Fully implemented: `EMAIL`, `SMS`, `CALL`, `WAIT`, `CONDITION_OPEN`, `CONDITION_REPLY`, `CONDITION_CLICK`
- Builder-visible but currently placeholder/auto-advance steps: `WHATSAPP`, `LINKEDIN`, `MANUAL`

## Stack

- Backend: Django 5, Django REST Framework, Simple JWT, Celery, `django-cors-headers`
- Frontend: static HTML pages, ES modules, Bootstrap 5, Chart.js
- Database: SQLite by default at `backend/db.sqlite3`
- Integrations: Google OAuth/Gmail API, OpenRouter, Gemini, Twilio
- Python target: `3.11.9` via [`runtime.txt`](runtime.txt)

## Repo Layout

```text
backend/
manage.py
backend/ # active Django project package used by manage.py
campaigns/ # campaign models, serializers, tasks, Gmail/Twilio/AI integrations
leads/ # lead models, CSV import task, lead/tag API
tenants/ # organizations, tenant middleware, security middleware
users/ # custom user model, registration, profile/JWT auth
config/ # older scaffold package; not the active settings module

frontend/
*.html # active static product screens
api.js # API base URL + auth token helpers
main.js # shared auth bootstrapping/logout handling
theme.css # shared dashboard styling
src/ # leftover Vite starter files; not used by the live UI

outreach_frontend/ # currently empty
*.md # product and architecture docs from earlier phases
```

## Local Setup

### 1. Create a virtual environment and install dependencies

### 1. Configure Environment Variables
Create a .env file in the backend/ directory:
```sh
python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS/Linux
source .venv/bin/activate

pip install -r requirements.txt
```

### 2. Create `backend/.env`

The backend reads environment variables from `backend/.env`.

```env
DEBUG=True
SECRET_KEY=change-me
BACKEND_BASE_URL=http://127.0.0.1:8000
FRONTEND_BASE_URL=http://127.0.0.1:8080

CELERY_TASK_ALWAYS_EAGER=true
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/1
CELERY_TASK_ALWAYS_EAGER=false
ENABLE_AUTO_REPLY_DETECTION=false
LAUNCH_IMMEDIATE_PASSES=1

Expand All @@ -354,48 +118,27 @@ TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
```

### 3. Run migrations

### 2. Start the Stack
Run the following command from the root of the repository to build and start the database, backend, frontend, Redis, and Celery workers:
```sh
python backend/manage.py migrate
docker compose up --build
```

### 4. Start the backend

### 3. Apply Migrations
Once the containers are running cleanly, open a new terminal window and apply the database migrations:
```sh
cd backend
python manage.py runserver 8000
docker compose exec backend python backend/manage.py migrate
```
### 4. Access the Application

### 5. Serve the frontend

In a second terminal:

```sh
cd frontend
python -m http.server 8080
```

Open `http://127.0.0.1:8080/login.html`.

## Background Jobs

In local development, campaign processing and CSV import work without Redis because `CELERY_TASK_ALWAYS_EAGER=true` by default when `DEBUG=True`.

If you want true async/background execution:

1. Start Redis.
2. Set `CELERY_TASK_ALWAYS_EAGER=false` in `backend/.env`.
3. Run the Celery worker and beat scheduler:
1. Frontend UI: http://localhost:8080/login.html
2. Backend API: http://localhost:8000

To shut down the stack, use--
```sh
cd backend
celery -A backend worker -l info
celery -A backend beat -l info
docker compose down
```

Reply polling only runs when `ENABLE_AUTO_REPLY_DETECTION=true`.

## Frontend Behavior

Expand Down
Binary file modified backend/celerybeat-schedule
Binary file not shown.
Binary file added backend/celerybeat-schedule-shm
Binary file not shown.
Binary file added backend/celerybeat-schedule-wal
Binary file not shown.
Binary file modified backend/db.sqlite3
Binary file not shown.
Loading