# Jimi Gallery Contemporary art gallery website prototype — public site + admin panel. Design language inspired by Jack Shainman, Crossing Art, and Derek Eller. ## Stack - **Frontend**: Vanilla HTML/CSS/JS (no build step). i18n in EN/KO/JA. Exhibition slider. Per-field image upload with client-side downscale. - **Backend**: FastAPI + Motor (async MongoDB driver) + PyJWT. - **Database**: MongoDB 7. - **Deploy**: Docker Compose — single-command bring-up. Brand mark: purple circle with serif `JM` monogram (inline SVG in `assets/app.js` and `admin/admin.js`), paired with a `JIMI GALLERY` wordmark in Cinzel. ## Run it ```bash cd /Users/jungwoochoi/Desktop/prototype/gallery docker compose up --build ``` Then visit: - **Public site** → http://localhost:5891 - **Admin** → http://localhost:5891/admin/ (password: `admin`) Ports (deliberately uncommon to avoid local collisions): | Service | Host port | Container port | |---------|-----------|----------------| | API + static | **5891** | 8000 | | MongoDB | **47017** | 27017 | First boot auto-seeds the demo data. Subsequent boots persist to the `mongo_data` volume. ### Environment Override via shell env or a `.env` at the repo root: ``` ADMIN_PASSWORD=your-password JWT_SECRET=change-me-to-a-long-random-string ``` `backend/.env.example` lists all knobs. ## Structure ``` gallery/ ├── index.html, artists.html, ... public site pages ├── assets/ │ ├── data.js Store (API-backed, cached) + Auth (JWT) │ ├── i18n.js EN/KO/JA dictionary, detection, switcher helpers │ ├── app.js nav + footer + slider helpers │ └── styles.css ├── admin/ │ ├── index.html login │ ├── dashboard.html, artists.html, exhibitions.html, news.html, settings.html │ ├── admin.js sidebar, image field, multilingual field, export/import │ └── admin.css ├── backend/ │ ├── main.py FastAPI routes + static mount │ ├── db.py Motor client │ ├── auth.py JWT issue/verify │ ├── seed.py demo seed │ ├── requirements.txt │ ├── Dockerfile │ └── .env.example └── docker-compose.yml ``` ## API surface All writes require `Authorization: Bearer `; reads are public. ``` POST /api/auth/login → {token} GET /api/auth/me → {ok: true} GET /api/settings PUT /api/settings GET /api/artists GET /api/artists/{id} PUT /api/artists/{id} DELETE /api/artists/{id} GET /api/exhibitions GET /api/exhibitions/{id} PUT /api/exhibitions/{id} DELETE /api/exhibitions/{id} GET /api/news GET /api/news/{id} PUT /api/news/{id} DELETE /api/news/{id} POST /api/admin/seed reset all collections to demo seed GET /api/admin/export full DB snapshot POST /api/admin/import replace full DB with uploaded snapshot ``` Interactive docs (Swagger UI) at http://localhost:5891/docs. ## Frontend data access pattern ```js // On every page — inline script pattern: (async () => { await Store.load(); // fetches /api/settings + /api/artists + /api/exhibitions + /api/news, caches renderChrome("home"); // ... accessors below are sync reads from cache ... Store.artists(); Store.exhibition(id); })(); // Admin mutations are async: await Store.upsert("artists", artist); await Store.remove("news", id); await Store.updateSettings({...}); await Store.reset(); await Store.importAll(json); await Store.exportAll(); ``` Auth: ```js await Auth.login(password); // posts to /api/auth/login, stores JWT in localStorage Auth.isAuthed(); // sync — presence check; API returns 401 if expired Auth.logout(); // clears token ``` ## Dev (no Docker) If you want to iterate on the backend without rebuilding the image: ```bash # Mongo (container only — still uncommon port) docker run -d -p 47017:27017 --name jimi-mongo mongo:7 # Backend cd backend python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt MONGO_URL=mongodb://localhost:47017 STATIC_DIR=.. \ uvicorn main:app --reload --port 5891 ``` Hot-reload on Python changes, frontend edits land on browser refresh.