fix: UX — start.sh 자동 LAN IP 감지 + topics/vote 레이아웃 조정
- start.sh: 호스트 LAN IP 자동 감지 후 PUBLIC_BASE_URL 세팅, 이제 QR이 172.x 컨테이너 IP 대신 실제 LAN IP를 가리킴 - docker-compose.yml: PUBLIC_BASE_URL 환경변수 pass-through 추가 - app.py: topics min-height 480→360, font-size/line-height 상향, vote counter를 QR 위로 이동, pct 계산 단순화 - README: 실행 섹션 교체 (start.sh 권장, raw/최소 방식 병기) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
16
README.md
16
README.md
@@ -12,7 +12,19 @@
|
|||||||
5. **Stage 3 — 투표** (큰 화면에 QR, 모바일 → `/?mode=vote`)
|
5. **Stage 3 — 투표** (큰 화면에 QR, 모바일 → `/?mode=vote`)
|
||||||
6. **시상** (`/?mode=ceremony&token=mlops2026`)
|
6. **시상** (`/?mode=ceremony&token=mlops2026`)
|
||||||
|
|
||||||
## 실행 — Docker (한 줄)
|
## 실행 — Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start.sh # LAN IP 자동 감지 + PUBLIC_BASE_URL 세팅 + 컨테이너 기동
|
||||||
|
```
|
||||||
|
|
||||||
|
또는 raw 방식 (LAN IP 수동):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PUBLIC_BASE_URL="http://192.168.0.47:8501" docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
또는 최소 (LAN IP 없이, 어드민에서 나중에 override):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
@@ -24,7 +36,7 @@ docker compose up -d --build
|
|||||||
docker compose down # 종료 (데이터 보존)
|
docker compose down # 종료 (데이터 보존)
|
||||||
```
|
```
|
||||||
|
|
||||||
**ADMIN_TOKEN**: `mlops2026` (외우기 쉬운 고정값). 변경하려면 `docker-compose.yml`의 `ADMIN_TOKEN:` 값 직접 수정 후 `docker compose up -d --build`.
|
**ADMIN_TOKEN**: `mlops2026` (외우기 쉬운 고정값). 변경하려면 `docker-compose.yml`의 `ADMIN_TOKEN:` 값 직접 수정 후 재기동.
|
||||||
|
|
||||||
## URL
|
## URL
|
||||||
|
|
||||||
|
|||||||
29
app.py
29
app.py
@@ -58,16 +58,16 @@ SHOW_CSS = """
|
|||||||
.show-cat-card {
|
.show-cat-card {
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
min-height: 480px;
|
min-height: 360px;
|
||||||
}
|
}
|
||||||
.show-cat-T1 { background: linear-gradient(135deg, #ffb84d, #ff8c00); color: #222; }
|
.show-cat-T1 { background: linear-gradient(135deg, #ffb84d, #ff8c00); color: #222; }
|
||||||
.show-cat-T2 { background: linear-gradient(135deg, #4dffd2, #2a8e7e); color: #1a1a1a; }
|
.show-cat-T2 { background: linear-gradient(135deg, #4dffd2, #2a8e7e); color: #1a1a1a; }
|
||||||
.show-cat-T3 { background: linear-gradient(135deg, #ff4d6d, #b83a55); color: white; }
|
.show-cat-T3 { background: linear-gradient(135deg, #ff4d6d, #b83a55); color: white; }
|
||||||
.show-cat-T4 { background: linear-gradient(135deg, #a64dff, #6a2eaf); color: white; }
|
.show-cat-T4 { background: linear-gradient(135deg, #a64dff, #6a2eaf); color: white; }
|
||||||
.show-cat-title { font-size: 32px; font-weight: 800; margin-bottom: 4px; }
|
.show-cat-title { font-size: 36px; font-weight: 800; margin-bottom: 4px; }
|
||||||
.show-cat-tagline { font-size: 16px; font-style: italic; margin-bottom: 4px; }
|
.show-cat-tagline { font-size: 18px; font-style: italic; margin-bottom: 4px; }
|
||||||
.show-cat-tone { font-size: 14px; opacity: 0.85; margin-bottom: 12px; }
|
.show-cat-tone { font-size: 14px; opacity: 0.85; margin-bottom: 12px; }
|
||||||
.show-cat-item { font-size: 17px; line-height: 1.45; padding: 4px 0; }
|
.show-cat-item { font-size: 19px; line-height: 1.55; padding: 4px 0; }
|
||||||
.show-vote-counter { font-size: 96px; text-align: center; font-weight: 900; padding: 16px 0; }
|
.show-vote-counter { font-size: 96px; text-align: center; font-weight: 900; padding: 16px 0; }
|
||||||
.show-vote-caption { font-size: 36px; text-align: center; color: #555; padding: 12px 0; }
|
.show-vote-caption { font-size: 36px; text-align: center; color: #555; padding: 12px 0; }
|
||||||
</style>
|
</style>
|
||||||
@@ -468,6 +468,17 @@ def render_stage_vote(data):
|
|||||||
unsafe_allow_html=True,
|
unsafe_allow_html=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
votes = data.get("votes", [])
|
||||||
|
total = len(data.get("people", []))
|
||||||
|
voted = len(votes)
|
||||||
|
pct = voted / total if total else 0
|
||||||
|
|
||||||
|
st.markdown(
|
||||||
|
f'<div class="show-vote-counter">{voted} / {total}</div>',
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
st.progress(pct)
|
||||||
|
|
||||||
vote_url = compute_vote_url()
|
vote_url = compute_vote_url()
|
||||||
qr_png = make_qr_png(vote_url)
|
qr_png = make_qr_png(vote_url)
|
||||||
|
|
||||||
@@ -479,16 +490,6 @@ def render_stage_vote(data):
|
|||||||
unsafe_allow_html=True,
|
unsafe_allow_html=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
votes = data.get("votes", [])
|
|
||||||
total = len(data.get("people", []))
|
|
||||||
voted = len(votes)
|
|
||||||
pct = int(100 * voted / total) if total else 0
|
|
||||||
st.markdown(
|
|
||||||
f'<div class="show-vote-counter">{voted} / {total}</div>',
|
|
||||||
unsafe_allow_html=True,
|
|
||||||
)
|
|
||||||
st.progress(pct / 100 if total else 0)
|
|
||||||
|
|
||||||
|
|
||||||
def render_voter():
|
def render_voter():
|
||||||
if not can_accept_votes(load_data()):
|
if not can_accept_votes(load_data()):
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${PORT:-8501}:8501"
|
- "${PORT:-8501}:8501"
|
||||||
environment:
|
environment:
|
||||||
ADMIN_TOKEN: ${ADMIN_TOKEN:-change-me}
|
# 외우기 쉬운 고정 token. 변경하려면 여기 값만 수정.
|
||||||
DATA_PATH: /app/hackathon.json
|
ADMIN_TOKEN: mlops2026
|
||||||
|
DATA_PATH: /app/data/hackathon.json
|
||||||
|
PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-}
|
||||||
volumes:
|
volumes:
|
||||||
# 단일 데이터 파일. 호스트 ↔ 컨테이너 read-write mount.
|
# 단일 데이터 디렉터리 마운트.
|
||||||
# 호스트에서 jq/vi 편집 가능, 앱이 votes 추가 시 그대로 반영.
|
# 첫 부팅 시 entrypoint.sh가 assign_teams.py 실행하여 hackathon.json 시드.
|
||||||
- ./hackathon.json:/app/hackathon.json
|
# 이후 부팅은 기존 파일 보존.
|
||||||
|
- ./data:/app/data
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
16
start.sh
Executable file
16
start.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 호스트 LAN IP 자동 감지 → PUBLIC_BASE_URL 세팅 → 컨테이너 기동
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
LAN_IP=$(ipconfig getifaddr en0 2>/dev/null || ip -4 addr show 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '^127\.' | head -1 || echo "")
|
||||||
|
PORT=${PORT:-8501}
|
||||||
|
|
||||||
|
if [[ -n "$LAN_IP" ]]; then
|
||||||
|
export PUBLIC_BASE_URL="http://${LAN_IP}:${PORT}"
|
||||||
|
echo "[start] PUBLIC_BASE_URL=${PUBLIC_BASE_URL} (자동 감지)"
|
||||||
|
else
|
||||||
|
echo "[start] LAN IP 감지 실패. 어드민에서 PUBLIC_BASE_URL 직접 설정 필요."
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec docker compose up -d --build "$@"
|
||||||
Reference in New Issue
Block a user