feat: initial Jimi Gallery prototype

- Public site (Home/Artists/Exhibitions/News/About/Contact) with EN/KO/JA i18n
- Admin panel with login, CRUD, image upload, multilingual editing
- Exhibition slider/lightbox view
- FastAPI + MongoDB backend, JWT auth
- Docker Compose deployment, behind nginx at jimi.yakenator.io
This commit is contained in:
2026-04-25 12:47:36 +09:00
commit 098b55e3b0
32 changed files with 5334 additions and 0 deletions

152
README.md Normal file
View File

@ -0,0 +1,152 @@
# 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 <jwt>`; 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.