commit a6474ed52431fe83ee3b306ed7e2b0059f9032a1 Author: sihoo Date: Fri Aug 7 14:17:40 2026 +0900 학교 출석 시스템 설치 패키지 (docker-compose + 원클릭 설치 스크립트) diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f64680c --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# 이 파일을 .env로 복사한 뒤 값을 채우세요: cp .env.example .env + +# 학교 도메인 (DNS A레코드를 이 서버 IP로 미리 연결해두세요. Caddy가 자동으로 HTTPS 인증서를 발급합니다) +DOMAIN=attendance.myschool.ac.kr + +# DB 비밀번호 (임의의 강력한 문자열로 변경하세요) +DB_PASSWORD=change_this_password + +# 교사 회원가입 시 필요한 인증 코드 (학교마다 다르게 설정) +TEACHER_SECRET_CODE=change_this_secret_code + +# /reset-db (출석 DB 초기화) 엔드포인트 보호용 비밀번호 +ADMIN_RESET_PASSWORD=change_this_admin_password diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..cd55233 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,3 @@ +{$DOMAIN} { + reverse_proxy backend:8000 +} diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..74bc677 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,60 @@ +# 다른 학교에 설치하기 + +이 시스템을 새 학교 서버에 설치하는 절차입니다. 백엔드는 도커로 뜨고, 도메인만 지정하면 HTTPS 인증서는 자동으로 발급됩니다. + +## 1. Firebase 프로젝트 만들기 (학교마다 1개씩) + +푸시 알림(FCM)과 웹 대시보드 호스팅에 필요합니다. + +1. https://console.firebase.google.com 에서 새 프로젝트 생성 +2. Flutter 앱 폴더에서 `flutterfire configure` 실행 → 방금 만든 프로젝트 선택 + - `lib/firebase_options.dart`, `android/app/google-services.json`이 새 프로젝트 값으로 교체됩니다 +3. Firebase 콘솔 → 프로젝트 설정 → Cloud Messaging → **웹 푸시 인증서**에서 VAPID 키 발급 (아래 3번에서 사용) +4. Firebase 콘솔 → 프로젝트 설정 → 서비스 계정 → **새 비공개 키 생성** → `firebase-key.json`으로 저장 (서버에 올릴 파일, 4번에서 사용) + +## 2. 안드로이드 앱 ID 변경 (학교마다 다르게) + +- `android/app/build.gradle.kts`의 `applicationId`를 학교별 고유 값으로 변경 (예: `com.myschool.attendance`) +- 릴리즈 키스토어 새로 생성하고 `android/key.properties`에 경로/비밀번호 등록 (기존 백산고 릴리즈와 절대 같은 키스토어 쓰지 않기) + +## 3. 서버에 백엔드 배포 + +가장 쉬운 방법 — 설치 스크립트 한 줄: + +```bash +curl -fsSL https://hootongerungit.mcv.kr/sihoo/school-attendance-deploy/raw/branch/main/install.sh | bash +``` + +Docker가 없으면 자동으로 설치하고, 도메인/교사 인증코드를 물어본 뒤 미리 빌드해둔 이미지(`hootongerungit.mcv.kr/sihoo/school-backend`)를 내려받아 바로 실행합니다. (소스코드는 비공개 레포에 있고, 이 이미지는 그 코드로 미리 빌드해서 공개로 올려둔 것 — 설치할 땐 소스코드 접근이 필요 없습니다.) + +수동으로 하고 싶다면: + +```bash +git clone https://hootongerungit.mcv.kr/sihoo/school-attendance-deploy.git +cd school-attendance-deploy +cp .env.example .env +vi .env # DOMAIN, DB_PASSWORD, TEACHER_SECRET_CODE, ADMIN_RESET_PASSWORD 채우기 +# 1-4번에서 만든 firebase-key.json을 이 폴더에 복사 +docker compose pull +docker compose up -d +``` + +- `DOMAIN`으로 지정한 주소의 DNS A레코드를 이 서버의 공인 IP로 미리 연결해두세요 (포트 80/443 오픈 필요). Caddy가 첫 요청부터 자동으로 Let's Encrypt 인증서를 발급합니다. +- `docker compose logs -f backend`로 정상 기동(DB 연결 완료 로그) 확인. +- 백엔드 코드가 업데이트되면 `docker compose pull && docker compose up -d`로 최신 이미지로 갱신하면 됩니다. + +## 4. Flutter 앱 빌드 (학교별 서버 주소로) + +```bash +flutter build web --release -t lib/main_client_app.dart \ + --dart-define=BASE_URL=https://<2번에서 지정한 DOMAIN> \ + --dart-define=WEB_VAPID_KEY=<1-3번에서 발급받은 VAPID 키> \ + --dart-define=SCHOOL_NAME=<학교 이름> + +flutter build apk --release --split-per-abi -t lib/main_client_app.dart \ + --dart-define=BASE_URL=https:// \ + --dart-define=WEB_VAPID_KEY= \ + --dart-define=SCHOOL_NAME=<학교 이름> +``` + +인자를 안 주면 기존 백산고 서버(backsanhi.mcv.kr) 기본값으로 빌드되니, 새 학교용 빌드에는 반드시 세 값을 다 넘겨야 합니다. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..55774e6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +services: + db: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: school_management + POSTGRES_USER: school_admin + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U school_admin -d school_management"] + interval: 5s + timeout: 5s + retries: 10 + + backend: + image: hootongerungit.mcv.kr/sihoo/school-backend:${IMAGE_TAG:-latest} + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DB_HOST: db + DB_NAME: school_management + DB_USER: school_admin + DB_PASS: ${DB_PASSWORD} + TEACHER_SECRET_CODE: ${TEACHER_SECRET_CODE} + ADMIN_RESET_PASSWORD: ${ADMIN_RESET_PASSWORD} + FIREBASE_KEY_PATH: /run/secrets/firebase-key.json + volumes: + - ./firebase-key.json:/run/secrets/firebase-key.json:ro + + caddy: + image: caddy:2 + restart: unless-stopped + ports: + - "80:80" + - "443:443" + environment: + DOMAIN: ${DOMAIN} + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + depends_on: + - backend + +volumes: + db_data: + caddy_data: + caddy_config: diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..0504f9e --- /dev/null +++ b/install.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# 🏫 학교 출석 시스템 원클릭 설치 스크립트 +# 사용법: curl -fsSL <이 파일의 raw 주소> | bash +set -euo pipefail + +REPO_URL="https://hootongerungit.mcv.kr/sihoo/school-attendance-deploy.git" +INSTALL_DIR="school-attendance" + +echo "🏫 학교 출석 시스템 설치를 시작합니다." +echo "" + +# ── 1. Docker 확인/설치 ────────────────────────────────────── +if ! command -v docker &> /dev/null; then + echo "🐳 Docker가 없어서 자동으로 설치합니다..." + curl -fsSL https://get.docker.com | sh +else + echo "✅ Docker 확인됨" +fi + +if ! docker compose version &> /dev/null; then + echo "❌ 'docker compose' 명령을 찾을 수 없습니다. Docker Compose 플러그인을 설치한 뒤 다시 시도하세요." + exit 1 +fi + +if ! command -v git &> /dev/null; then + echo "❌ git이 설치되어 있지 않습니다. 먼저 설치해주세요 (예: sudo apt install git)." + exit 1 +fi + +# ── 2. 저장소 받기 ──────────────────────────────────────────── +if [ -d "$INSTALL_DIR/.git" ]; then + echo "📦 이미 받아져 있어서 최신 버전으로 갱신합니다." + git -C "$INSTALL_DIR" pull +else + echo "📦 저장소를 내려받는 중..." + git clone "$REPO_URL" "$INSTALL_DIR" +fi +cd "$INSTALL_DIR" + +# ── 3. 설정값 입력받기 (curl | bash로 실행돼도 터미널에서 직접 입력받도록 /dev/tty 사용) ── +if [ -f .env ]; then + echo "⚠️ .env가 이미 있어서 기존 설정을 그대로 사용합니다. (다시 설정하려면 .env를 지우고 재실행)" +else + echo "" + read -rp "🌐 이 서버에서 쓸 도메인 (예: attendance.myschool.ac.kr): " SCHOOL_DOMAIN < /dev/tty + read -rp "🔑 교사 회원가입용 인증 코드 (선생님들께 알려줄 코드): " TEACHER_CODE < /dev/tty + + if command -v openssl &> /dev/null; then + DB_PW=$(openssl rand -hex 16) + ADMIN_PW=$(openssl rand -hex 8) + else + DB_PW=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n') + ADMIN_PW=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') + fi + + cat > .env < firebase-key.json +fi + +# ── 5. 실행 (이미지는 새로 빌드하지 않고 미리 만들어둔 이미지를 내려받아 사용) ── +echo "" +echo "🚀 이미지를 내려받고 컨테이너를 실행합니다..." +docker compose pull +docker compose up -d + +# shellcheck disable=SC1091 +source .env + +echo "" +echo "🎉 설치 완료!" +echo "──────────────────────────────────────────" +echo " 도메인: $DOMAIN" +echo " 교사 인증코드: $TEACHER_SECRET_CODE" +echo " DB 비밀번호: $DB_PASSWORD (꼭 기록해두세요)" +echo " 관리자 초기화 비밀번호: $ADMIN_RESET_PASSWORD" +echo "──────────────────────────────────────────" +echo "DNS A레코드를 이 서버의 공인 IP로 연결하면 몇 분 안에 https://$DOMAIN 으로 접속됩니다." +echo "상태 확인: cd $INSTALL_DIR && docker compose logs -f backend"