Niken 3D
A Persian-language e-commerce and content platform for 3D printing — shop, blog, wallet, custom print orders and support — built as one Django backend serving three clients: a Next.js storefront, a React admin dashboard, and a Bale/Telegram bot. I own the backend and the production infrastructure.
Overview
A Persian-language e-commerce and content platform for 3D printing — shop, blog, wallet, custom print orders and support — built as one Django backend serving three clients: a Next.js storefront, a React admin dashboard, and a Bale/Telegram bot. I own the backend and the production infrastructure.
Problem
A 3D-printing business needed to sell online in a market where the usual infrastructure does not apply: Persian RTL throughout, domestic payment and SMS providers, and customers who live on Bale and Telegram as much as on the web. Beyond a storefront it needed custom print-order intake, customer wallets, competitor price tracking, and a back-office its staff could actually run — all from one set of business rules rather than three implementations that would inevitably diverge.
Solution
Built the platform as one brain and three faces: a Django + DRF backend that owns every model, rule and permission, and three pure clients — a Next.js storefront, a Vite admin SPA, and a FastAPI chat bot that holds no database of its own. The contract is enforced rather than assumed: one response envelope, a trailing slash on every path, and client field names mirroring backend serializers exactly, with API changes made deliberately in the backend first. On the operations side I built the production posture the platform runs on — a Prometheus observability stack with in-house exporters, an off-site backup pipeline with verified restores, and the hardening that followed a ransomware incident on exposed datastores.
Architecture
The backend is fifteen Django apps — users, product, shop, finance, content, comments, notifications, campaigns, shortlinks, print_orders, tickets, scraper, logs, general and a shared toolkit — holding 53 models behind roughly 191 URL patterns under /api/v1/<area>/, layered views → services/selectors → models. Authentication is cookie-first JWT: the storefront reaches the API through a same-origin Next.js proxy, while the admin SPA calls it cross-origin against a CORS allowlist with SameSite=None cookies. Background work runs on Huey with APScheduler for periodic jobs — SMS and OTP dispatch, notifications, and an hourly competitor-catalogue sweep guarded by a task lock so a long run is skipped rather than queued. Redis serves as both cache and broker; PostgreSQL runs on the host. Integrations are domestic: Zibal for payment, FarazSMS for OTP, Neshan for maps, and the Bale and Telegram Bot APIs. Each of the five repositories deploys itself over SSH from GitHub Actions, building images on the server and gating the deploy on CI. Observability is a separate compose stack — Prometheus, Alertmanager and nine exporters behind a custom FastAPI dashboard — and backups run as a standalone PM2-supervised service that dumps PostgreSQL to object storage with a SHA-256 manifest per run.
Challenges
Recovering from a ransomware incident: production PostgreSQL and Redis had been published on a public interface with default credentials and were encrypted. Remediation meant rebinding every datastore to loopback, moving credentials into environment-driven configuration, auditing every published port across the compose files, and writing the resulting posture down so it could not silently drift back.
Building observability for a server in Iran, where standard components are simply unreachable — cAdvisor image registry is blocked, so I wrote a docker-stats exporter to replace it, alongside a bot-health exporter that interrogates each chat platform Bot API for webhook registration and queued-update backlog rather than only pinging our own process.
Monitoring targets that are not containers: Prometheus runs in Docker while PostgreSQL and the Docker daemon run on the host, which needs per-subnet host rules (pg_hba, listen_addresses, firewall) and a pinned bridge subnet — with a diagnose script that names the missing prerequisite instead of leaving a panel silently reading "no data".
Keeping one API contract honest across three clients in three stacks: the envelope, the trailing-slash rule and serializer field names are load-bearing, so the backend is treated as the contract and no client change is allowed to imply an API change on its own.
Outcomes
A production e-commerce platform with fifteen backend domains, 53 models and roughly 191 endpoints, serving a storefront, an admin dashboard and a chat bot from a single contract.
A backend test suite of 184 files and ~4,700 test functions behind a 93% coverage gate wired into the deploy workflow, so a coverage drop or a missing migration stops the release instead of being noticed after it.
Observability built from nothing: Prometheus, Alertmanager and nine exporters — two of them written in-house — behind a custom dashboard, with alerts routed to the team chat.
An off-site backup pipeline with a documented restore runbook: PostgreSQL dumped to object storage with a SHA-256 manifest per run, retention pruning, and a per-run report delivered to the team, supervised by PM2.
Lessons Learned
Exposure is the whole attack surface. The ransomware exploited no bug — it walked into a database published on a public interface with a default password, and the fix was configuration, not code.
A monitor that cannot reach its target is worse than no monitor, because an alert that can never fire reads exactly like a passing check. Every blind spot has to be recorded as a blind spot.
Infrastructure constraints are engineering constraints: when a standard component is unreachable from your region, writing the replacement is often cheaper than engineering around its absence.
A contract shared by three clients survives only when one side owns it — treating the backend as the specification, and refusing cross-project edits inside a single task, is what kept the three from drifting apart.

