A visual block-based programming platform. Users build programs by connecting blocks in the browser — blocks are serialized to AST-JSON, stored in a database, and executed by a custom interpreter on the backend.
Browser (Next.js)
│ REST API (JSON)
▼
FastAPI (backend)
│ enqueue task
▼
Celery Worker ──► AST Interpreter
│
├─► PostgreSQL (projects, modules, runs, schedules)
└─► Redis (Celery broker + response cache)
Celery Beat checks schedules every minute and automatically enqueues runs.
Requirements: Docker Desktop — that's it. No Python, no Node.js, no database setup needed.
# 1. Clone the repository
git clone <repo-url>
cd Codeess-project
# 2. Start everything
docker compose up --buildThat single docker compose up --build command will:
- Start a PostgreSQL database
- Start a Redis instance
- Apply all database migrations automatically
- Start the FastAPI backend on http://localhost:8000
- Start the Celery worker (executes programs in the background)
- Start the Celery beat scheduler (for scheduled runs)
- Start the Next.js frontend on http://localhost:3000
Open http://localhost:3000 in your browser — the app is ready.
To stop everything: docker compose down
To also delete the database data: docker compose down -v
| Technology | Role |
|---|---|
| Python 3.12 | Language |
| FastAPI | HTTP framework |
| SQLAlchemy 2 (async) + asyncpg | ORM + PostgreSQL driver |
| Alembic | Database migrations |
| Celery 5 + Redis | Task queue (worker + beat scheduler) |
| Pydantic v2 + pydantic-settings | Validation, schemas, configuration |
| PyJWT + passlib[bcrypt] | Authentication (JWT in httponly cookie) |
| fastapi-cache + Redis | Response caching |
| Poetry | Dependency management |
| Docker | Containerization |
codeess-backend/
├── src/
│ ├── main.py # FastAPI entry point, routers, CORS middleware
│ ├── config.py # Settings via pydantic-settings (.env)
│ ├── database.py # SQLAlchemy engines and session factory
│ ├── cache.py # Redis cache wrapper
│ ├── exceptions.py # Domain exceptions
│ │
│ ├── api/ # HTTP routers (FastAPI)
│ │ ├── auth.py # /auth — register, login, logout, /me
│ │ ├── projects.py # /projects — CRUD
│ │ ├── modules.py # /projects/{id}/modules — CRUD
│ │ ├── runs.py # /modules/{id}/runs — execute and view runs
│ │ ├── schedules.py # /schedules — scheduled runs
│ │ ├── dependencies.py # Dependency injection: DB session, UserID, validation
│ │ └── exceptions.py # HTTP exceptions
│ │
│ ├── services/ # Business logic layer
│ ├── repositories/ # Database access layer (Repository + Unit of Work pattern)
│ │ └── mappers/ # ORM model → domain entity mapping
│ ├── models/ # SQLAlchemy ORM models
│ ├── schemas/ # Pydantic schemas (requests / responses)
│ │
│ ├── logic/ # Interpreter core
│ │ ├── ast/
│ │ │ ├── base.py # Abstract classes: ASTNode, Expression, Statement
│ │ │ ├── expressions.py # Literals, variables, operators, function calls, ...
│ │ │ ├── statements.py # Assign, If, While, For, FunctionDef, ClassDef, Import, ...
│ │ │ ├── program.py # Program — root node, creates and runs Runtime
│ │ │ └── exceptions.py # BreakSignal, ContinueSignal, ReturnSignal
│ │ ├── parser/
│ │ │ └── factory.py # AST-JSON → object tree deserialization
│ │ ├── runtime/
│ │ │ └── runtime.py # Execution environment: scope stack, built-ins, I/O
│ │ └── interpreter/
│ │ └── runner.py # run_from_json() — interpreter entry point
│ │
│ └── tasks/ # Celery tasks
│ ├── celery_app.py # Celery instance, beat schedule
│ ├── runs.py # run_module_task: execute a module by run_id
│ └── schedules.py # check_and_fire: trigger due scheduled runs
│
├── migrations/ # Alembic migrations
├── docker-compose.yml # Backend-only compose (legacy)
├── Dockerfile
└── pyproject.toml
User
└── Project (1..*)
└── Module (1..*) — stores ast_json (the program as AST-JSON)
├── Run (1..*) — execution record (pending → running → done/failed)
└── ScheduledRun (0..*) — automatic run schedule
Run fields:
input_json— inputs passed at run timeoutput_json— output lines{ "output": [...] }errors— error message on failurestatus—pending | running | done | failedcreated_at,finished_at
ScheduledRun supports types: hourly, daily, weekly with configurable hour, minute, and weekday.
| Method | Path | Description |
|---|---|---|
| POST | /auth/register |
Register a new user |
| POST | /auth/login |
Login (sets httponly JWT cookie) |
| POST | /auth/logout |
Logout |
| GET | /auth/me |
Current user |
| GET | /projects |
List user's projects |
| POST | /projects |
Create a project |
| PUT / PATCH / DELETE | /projects/{id} |
Update / delete a project |
| GET | /projects/{id}/modules |
List modules in a project |
| POST | /projects/{id}/modules |
Create a module |
| PUT / PATCH / DELETE | /modules/{id} |
Update / delete a module |
| GET | /modules/{id}/runs |
Run history for a module |
| POST | /modules/{id}/runs |
Execute a module |
| GET | /modules/{id}/runs/{run_id} |
Run status / result |
| POST | /schedules |
Create a schedule |
| GET | /schedules/module/{module_id} |
Schedules for a module |
| PATCH / DELETE | /schedules/{id} |
Update / delete a schedule |
Interactive API docs (Swagger UI): http://localhost:8000/docs
The core feature of the project — a custom interpreter that executes programs represented as AST-JSON.
Full path from block to output:
1. User assembles a program from blocks in the frontend
2. Frontend serializes blocks to AST-JSON and saves to module (PATCH /modules/{id})
3. POST /modules/{id}/runs { inputs: [...] }
→ service creates a Run record with status "pending"
→ calls run_module_task.delay(run_id) # async Celery task
4. Celery worker:
a) Loads ast_json from DB (Redis cache, TTL 2 min)
b) Loads ast_json of all other modules in the project (for inter-module imports)
c) Sets status = "running"
d) factory.build_statement(node) for each node → AST object tree
e) Program(body).run(inputs, project_modules) → Runtime
f) Saves output to run.output_json, status = "done"
On exception → status = "failed", run.errors = str(e)
5. GET /modules/{id}/runs/{run_id} returns result (Redis cache, TTL 24 h)
Runtime (src/logic/runtime/runtime.py):
- Scope stack (
push_scope/pop_scope) — variable isolation inside blocks, functions, loops - Input queue
_inputs— simulatesinput()from a pre-supplied list - Output buffer
output: list[str]— collectsprint()results - Built-in functions:
len,range,str,int,float,sum,min,max,abs,input,print - System module imports (whitelist:
math,random,statistics,itertools, etc.) - Inter-module imports within a project — sibling module runs in an isolated sub-Runtime, its global names are exported
- Infinite loop protection:
10 000iteration limit per loop,1 000 000global step limit
Supported language constructs:
| Category | Constructs |
|---|---|
| Expressions | Literals, variables, arithmetic (+−×÷%), comparisons, and/or/not, ternary, function calls, lambda, attribute access, indexing |
| Collections | list, dict, set, tuple literals; index access, index assignment |
| Statements | assign, print, if/elif/else, while, for range, for each, repeat N times, break, continue, return, raise |
| Functions | Definition (def), recursion, instance methods (self) |
| Classes | class, instances (new), attributes, methods |
| Error handling | try/except with exception type matching |
| Imports | Whitelisted system modules, project module imports |
| Utilities | json_parse, json_stringify, csv_parse, http_get, http_post (external URLs only) |
Three independent services:
- codeess_back — FastAPI, handles HTTP requests
- codeess_celery_worker — consumes tasks from the queue (module execution)
- codeess_celery_beat — scheduler, calls
schedules.check_and_fireevery minute, which finds due schedules, creates Run records, and enqueues tasks
Redis uses two databases:
| DB | Purpose | TTL |
|---|---|---|
REDIS_CELERY_DB (0) |
Celery broker and backend | — |
REDIS_CACHE_DB (1) |
API response cache | 10 min (/auth/me), 2 min (module AST), 24 h (completed Run) |
Settings are read from the .env file at the project root (copied from .env.example):
MODE=LOCAL # TEST | LOCAL | DEV | PROD
DB_HOST=postgres # use "localhost" when running without Docker
DB_PORT=5432
DB_USER=codeess
DB_PASS=codeess
DB_NAME=codeess
REDIS_HOST=redis # use "localhost" when running without Docker
REDIS_PORT=6379
REDIS_CELERY_DB=0
REDIS_CACHE_DB=1
JWT_SECRET_KEY=change-me-to-a-long-random-string
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
ALLOWED_ORIGINS=["http://localhost:3000"]When
MODE=PRODthe JWT cookie is set with theSecureflag (HTTPS only).
codeess-frontend — Next.js 16 / React 19 / TypeScript / TailwindCSS. A visual block code editor with a drag-and-drop interface. Blocks are serialized to AST-JSON and sent to the backend. Supports authentication, project and module management, run history, and schedule configuration.
If Docker is not available, each service can be run manually.
cd codeess-backend
# Install dependencies
poetry install
codeess-backend codeess-frontend docker-compose.yml README.md venv
(codeess-backend-py3.12) kiripupsik5@kiripupsik5-ROG-Strix-G531GU:~/Projects/Programming/Python/Codeess/Codeess-project$ docker compose up --build
env file /home/kiripupsik5/Projects/Programming/Python/Codeess/Codeess-project/.env not found: stat /home/kiripupsik5/Projects/Programming/Python/Codeess/Codeess-project/.env: no such file or directory
(codeess-backend-py3.12) kiripupsik5@kiripupsik5-ROG-Strix-G531GU:~/Projects/Programming/Python/Codeess/Codeess-project$ ls
codeess-backend codeess-frontend docker-compose.yml README.md venv
(codeess-backend-py3.12) kiripupsik5@kiripupsik5-ROG-Strix-G531GU:~/Projects/Programming/Python/Codeess/Codeess-project$
# Copy and edit the env file
cp ../.env.example .env-local
# Edit .env-local: set DB_HOST=localhost, REDIS_HOST=localhost
# Apply database migrations
poetry run alembic upgrade head
# Start FastAPI
poetry run uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload
# In a separate terminal — Celery worker
poetry run celery --app=src.tasks.celery_app:celery_instance worker -l INFO
# In a separate terminal — Celery beat (for schedules)
poetry run celery --app=src.tasks.celery_app:celery_instance beat -l INFO --scheduler celery.beat:PersistentSchedulerPostgreSQL and Redis must be running locally before starting the backend.
cd codeess-frontend
npm install
npm run dev
# Open http://localhost:3000cd codeess-backend
# Apply all pending migrations
poetry run alembic upgrade head
# Create a new migration after changing models
poetry run alembic revision --autogenerate -m "description"
# Roll back the last migration
poetry run alembic downgrade -1