dev-portfolio-deploy & editor mode
cron이 index.html 덮어쓰니까 커스텀 파일 따로 관리할 것.
dev.ericfromkorea.com 배포 구조
dev-portfolio-fe 도커 컨테이너로 서빙함. (python server → host 60022 → nginx)
Source: /home/son/prj/dev_portfolio/fe/
⚠️ Gotcha: 매일 11:00에 cron 돌면서 generate-hub.py 실행됨. 이때 fe/index.html이랑 fe/hub.html 덮어써버림(clobber). 커스텀 내용은 무조건 다른 파일명(예: portfolio.html)으로 만들어야 살아남음.
Docker run 옵션 (generate-hub.py 내부):
--user 1000:1000 -v [FE_DIR]:/app --env-file [SECRET_FILE_PATH]
- -v : 호스트랑 볼륨 마운트해서 에디터로 수정하면 바로 반영되게 함.
Editor mode (Notion-style)
- login:
.editor-secret에 있는 비번으로POST /api/login해서 24h 토큰 받음. - block editor:
js/editor/blockedit/에 구현됨. contenteditable 기반. /슬래시 메뉴, 마크다운 단축키, 인라인 툴바 지원.- 데이터는 JSON으로 관리 (
js/data/profile.json등). - 이미지 업로드는
assets/img/uploads/로. - server code 수정 시:
docker restart dev-portfolio-feJS/CSS는 마운트되어 있어서 바로 반영되는데, Python 서버 코드는 프로세스 재시작해야 함.
PermissionError 관련:
gen_learn_hub() 실행 시 /var/www/learn.ericfromkorea.com/index.html 권한 문제로 터지는데, 포트폴리오 빌드 자체는 이미 성공함. 그냥 무시해도 됨.
여기서 배울 것
- cron으로 파일 덮어쓰기 설정할 때 주의할 것
- 도커에서 python 코드 수정하면 컨테이너 재시작 필수
- Notion 스타일 에디터는 vanilla JS로 구현 가능
원본 파일 보기 (.claude/projects/-home-son-prj-notion/memory/dev-portfolio-deploy.md)
---
name: dev-portfolio-deploy
description: How dev.ericfromkorea.com is deployed and the index.html auto-clobber gotcha
metadata:
node_type: memory
type: project
originSessionId: 066633f9-0c35-4e25-a5a5-084b3eb783e5
---
`dev.ericfromkorea.com` is served by the `dev-portfolio-fe` Docker container (`python -m http.server 8000` → host 60022 → nginx). Source: `/home/son/prj/dev_portfolio/fe/`.
**Gotcha:** a daily 11:00 cron runs `/home/son/prj/dev_portfolio/generate-hub.py`, which **overwrites `fe/index.html` and `fe/hub.html`** with the auto-generated link hub and rebuilds the container. So custom content must live in *other* filenames (e.g. `portfolio.html`, `projects.html`) + `css/`, `js/`, `assets/` — those survive the cron.
The Dockerfile does `COPY . .` and `CMD ["python","server/server.py"]` (a custom static+API server, not `http.server`). `rebuild_docker()` in generate-hub.py runs the container with `--user 1000:1000 -v {FE_DIR}:/app --env-file /home/son/prj/dev_portfolio/.editor-secret` so editor writes persist to the host source and survive rebuilds. To deploy: edit files in `fe/`, then run `python3 generate-hub.py`. The hub card link for a subdomain comes from `# hub-url:` comment in its nginx conf at `/etc/nginx/sites-available/<sub>`.
**Editor mode** (`server/server.py` + `js/editor/*`): `/login.html` → `POST /api/login` with the password in `.editor-secret` (`EDITOR_PASSWORD`; `EDITOR_SECRET` signs the HMAC token) → 24h bearer token in localStorage. Authed users get a floating edit bar on portfolio/projects/project pages. Profile + DB rows use form editors; **project detail uses a Notion-style block editor** (`js/editor/blockedit/`): contenteditable blocks, a `/` slash menu (createSlash), markdown shortcuts (`#`,`-`,`1.`,`>`,```` ``` ````,`---` via input.js), inline toolbar (B/I/S/code/link), block handle menu (delete/dupe/move), image upload. Serialization between Notion rich-text spans and editable HTML is in `blockedit/model.js`; complex blocks (columns/tables) are preserved read-only. Test tooling: `extract/cdp_*.js` drive headless Chrome via CDP to verify slash/markdown. Backend PUTs overwrite `js/data/profile.json` / `projects.json` / `details/<slug>.json`; uploads go to `assets/img/uploads/`. `POST /api/page` (store.create_page) atomically creates a new project page (appends a row + writes an empty `details/<slug>.json`, returns it) — exposed as a "+ 새 페이지" button on projects.html and the `/page` slash command in the block editor (inserts a link to the new page). **Editing the Python server code (`server/*.py`) requires `docker restart dev-portfolio-fe`** to reload (static JS/CSS update live via the mount, but the running process holds the Python code). Data is now JSON (not JS modules), loaded async via `js/data/store.js`. The editor password is in `/home/son/prj/dev_portfolio/.editor-secret` (mode 600, not in git/image); change it there and re-run generate-hub.py (or `docker restart`).
`gen_learn_hub()` at the end throws PermissionError on `/var/www/learn.ericfromkorea.com/index.html` (root-owned) — pre-existing, unrelated to the dev portfolio; the hub+container rebuild already succeeded before it.
The custom Notion reproduction is vanilla HTML/CSS/JS, fully modular (ES modules under `js/`, tokens/components under `css/`, data in `js/data/`). Pages: 프로필 = `portfolio.html`, All Projects DB = `projects.html`, per-project detail = `project.html?id=<slug>` (renders `js/data/details/<slug>.json` via `js/notion/`). Design is Apple-style (frosted-glass sticky nav, soft shadows, large radii, SF/Pretendard). Project images live under `assets/img/projects/` (~72MB; baked into the image by `COPY . .`). See [[notion-source-ids]].