New
Apps 1,217Makers 1,215This week 1,217
Promote your app−30%
Emelmujiro screenshot 1
0views in
the last 30 days

Emelmujiro

AI education and consulting site, a React and Django monorepo self-hosted with Docker.

Built with Claude Code

by researcherhojin · added Oct 29, 2026
no ratings0 comments

AI education, consulting & development — React 19 + Django 6 monorepo, self-hosted via Docker behind a Cloudflare Tunnel.

Homepage — Light mode Homepage — Dark mode

Tech Stack

Frontend
React TypeScript Vite React Router Tailwind CSS Tailwind Typography i18next react-i18next Axios TipTap DOMPurify

Backend
Django DRF SimpleJWT Gunicorn SQLite

Testing
Vitest Testing Library Playwright Lighthouse

Infra
Node Python Docker Nginx GitHub Actions Cloudflare Umami PostgreSQL Dependabot Trivy

Getting Started

Prerequisites: Node >= 24, Python 3.12, uv

git clone https://github.com/researcherhojin/emelmujiro.git
cd emelmujiro

make install                                              # npm install + uv sync --extra dev
cd backend && uv run python manage.py migrate && cd ..    # First-time DB setup
npm run dev                                               # Frontend :5173 + Backend :8000

Fresh macOS machine? make setup-dev-machine handles the full bootstrap (brew deps, make install, local .env, Django migrations). Re-runnable health check: make verify-setup.

Operational rules, architecture details, and conventions: CLAUDE.md.

Useful Commands

Run from the repo root unless noted otherwise.

make test                  # All tests (frontend + backend)
make lint                  # All linters
make lint-fix              # Auto-fix lint issues
make update-test-counts    # Regenerate README test counts locally (CI auto-syncs on main)
make health                # Docker health diagnostic (containers, resources, endpoints)

# From frontend/: npm run validate (lint + type-check + coverage) and npm run test:e2e (Playwright, 5 profiles).
# Single-test, serve:build, bundle-analysis, route-check and visual-diff commands are listed once, in CLAUDE.md → Commands.

Architecture

graph LR
    subgraph Client["Browser"]
        React["React 19 SPA\nVite + Tailwind"]
    end

    subgraph CF["Cloudflare"]
        Tunnel["Cloudflare Tunnel"]
    end

    subgraph Host["Self-hosted (Docker · 127.0.0.1 only)"]
        Nginx["nginx (alpine)\nStatic + prerendered pages"]
        Gunicorn["Gunicorn 3w 2t\nSecurity MW + file cache"]
        DRF["Django 6 + DRF"]
        DB[(SQLite\nsqlite_data volume)]
        Umami["Umami Analytics"]
        UmamiDB[(PostgreSQL 15)]
    end

    Sentry["Sentry (SaaS)\nwired, DSN unset"]
    GHA["GitHub Actions\nCI + deploy trigger"]

    React -->|emelmujiro.com| Tunnel
    React -->|api.emelmujiro.com| Tunnel
    Tunnel -->|:8080| Nginx
    Tunnel -->|:8000 API| Gunicorn
    Nginx -.->|/api - not used by the SPA| Gunicorn
    Nginx -->|/umami/api/send| Umami
    Gunicorn --> DRF
    DRF --> DB
    Umami --> UmamiDB
    React -.->|errors, when a DSN is set| Sentry
    GHA -.->|deploy webhook| Tunnel
    Tunnel -.->|auto-deploy.sh on the host: build/ + backend image, migrations applied on container start| Host

    style Tunnel fill:#F3E8FF,stroke:#7C3AED
    style Host fill:#ECFDF5,stroke:#059669
    style CF fill:#FEF3C7,stroke:#D97706
    style Sentry fill:#FEF9C3,stroke:#CA8A04

Key Features

  • Bilingual (i18n) — URL-based routing: Korean default (/contact), English /en/contact
  • Teaching History — 46 entries across 5 years (2022–2026), org type filter (4 categories)
  • Insights (Blog) — TipTap rich text editor, slug URLs (/insights/:slug), image upload, IP-based likes, nested comments, optional per-post English translation served at /en/insights/:slug
  • Auth — httpOnly cookie JWT with shared-promise refresh queue, CSRF-protected cookie writes and login/refresh, and password-change token revocation
  • Testimonials — Enterprise + 고용노동부 K-디지털 reviews, dual-row auto-scroll carousel
  • Monitoring — Umami analytics (self-hosted, zero external scripts) + Docker health check (make health; a 5-minute cron appends only failures and warnings, timestamped, to backend/logs/health-check.log). Sentry is wired behind a lazy-loaded shim but inactive: VITE_SENTRY_DSN is unset in production, so initSentry() returns early and no DSN ships in the bundle
  • SEO — Search Console, sitemap, hreflang (blog posts only when translated; untranslated /en posts canonicalize to Korean), JSON-LD structured data, SSG prerendering of 10 routes. The Insights list uses the live API at build time; failed API calls or incomplete sitemap inventories abort the build. Article detail pages render client-side.
  • Performance — Vendor chunk splitting, Lighthouse CI assertions, < 10MB bundle budget
  • Security — DOMPurify HTML sanitization, CI ${{ }} injection prevention, uuid4 uploads with bounded image decoding, trusted-client-IP throttling, draft comment access controls, production CORS restrictions, HSTS on both hosts, Docker log rotation (10 MB × 3 files per container), and nightly encrypted backups with isolated restore verification plus full dependency and running-image audits, checked daily by a watchdog that opens an issue on failure
  • Privacy Policy — 13-section bilingual page compliant with Korean PIPA Article 30
  • Tests — Vitest (1293 tests) + Django unittest (450 tests) + Node sitemap regression tests (12 tests) + Playwright E2E (5 profiles)
  • CI/CD — GitHub Actions: lint, type-check, test, Trivy security scan, bundle size, Lighthouse, Docker builds of the images a PR changes, Codecov, auto-deploy via webhook, plus self-healing guards that redeploy on production drift, open an issue when that redeploy does not land, and alarm when a commit reaches main untested

License

GNU Affero General Public License v3.0

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#education · #consulting · #korean