diff --git a/docs/deploy.md b/docs/deploy.md index 947b71d..a429564 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -199,6 +199,7 @@ docker compose --env-file .env.production up -d --build - 로컬 개발 Docker Compose 실행 시 `ENV_FILE=.env.development`와 `--env-file .env.development`를 함께 사용 - 로컬 개발 DB 마이그레이션은 `npm run db:migrate:dev`로 실행 - NAS 운영 DB 마이그레이션은 NAS 호스트에 npm이 없어도 실행할 수 있도록 `sh scripts/migrate-production-db.sh status`로 적용 상태를 확인하고, `sh scripts/migrate-production-db.sh migrate`로 미적용 파일만 실행한다. +- 운영 환경 파일은 프로젝트 루트의 `.env.production`을 우선 사용한다. 없으면 `.env`를 읽고, 둘 다 없으면 실행 중인 `sori-studio-db` 컨테이너의 `POSTGRES_DB`·`POSTGRES_USER`를 사용한다. - `schema_migrations`가 없는 기존 운영 DB에서 `posts` 테이블이 감지되면 `migrate`는 001부터 자동 실행하지 않는다. 현재 코드 기준 최신 DB라면 최초 1회 `sh scripts/migrate-production-db.sh baseline`으로 기존 파일을 적용 완료로 기록한다. 특정 번호까지만 기록하려면 예: `sh scripts/migrate-production-db.sh baseline 031`. - 네비게이션 계층(`parent_id`, `is_folder`)은 `017_navigation_hierarchy.sql` 적용 후 저장 API가 정상 동작한다(미적용 시 `INSERT` 컬럼 불일치). - 회원 마지막 로그인 표시(`previous_last_seen_at`, `previous_last_seen_ip`)는 `021_add_member_previous_login.sql` 적용 후 정상 동작한다. diff --git a/docs/update.md b/docs/update.md index 18abda8..6220c71 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,9 @@ # 업데이트 이력 +## v1.3.8 + +- NAS 마이그레이션: `.env.production`이 없을 때 `.env` 또는 실행 중 DB 컨테이너 환경 변수로 동작하도록 `migrate-production-db.sh` 보정. + ## v1.3.7 - NAS 마이그레이션: npm 없는 NAS 호스트에서도 실행 가능한 `scripts/migrate-production-db.sh` 추가. diff --git a/package-lock.json b/package-lock.json index 4fbfe4e..311b103 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.3.7", + "version": "1.3.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.3.7", + "version": "1.3.8", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 06182a5..54a87af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.3.7", + "version": "1.3.8", "private": true, "type": "module", "imports": { diff --git a/scripts/migrate-production-db.sh b/scripts/migrate-production-db.sh index 4e5cc39..4505a98 100644 --- a/scripts/migrate-production-db.sh +++ b/scripts/migrate-production-db.sh @@ -2,28 +2,79 @@ set -eu ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -ENV_FILE=${ENV_FILE:-.env.production} DB_SERVICE=${DB_SERVICE:-sori-studio-db} MIGRATIONS_DIR="$ROOT_DIR/db/migrations" CONTAINER_MIGRATIONS_DIR=${CONTAINER_MIGRATIONS_DIR:-/docker-entrypoint-initdb.d} SCHEMA_MIGRATIONS_TABLE=${SCHEMA_MIGRATIONS_TABLE:-schema_migrations} MODE=${1:-migrate} BASELINE_TARGET=${2:-} +COMPOSE_ENV_FILE= cd "$ROOT_DIR" -if [ -f "$ENV_FILE" ]; then - set -a - # shellcheck disable=SC1090 - . "$ENV_FILE" - set +a -fi - POSTGRES_DB=${POSTGRES_DB:-sori_studio} POSTGRES_USER=${POSTGRES_USER:-sori_studio} +# 환경 파일(.env.production → .env) 선택 +pick_compose_env_file() { + if [ -n "${ENV_FILE:-}" ] && [ -r "$ENV_FILE" ]; then + COMPOSE_ENV_FILE=$ENV_FILE + return 0 + fi + + if [ -r .env.production ]; then + COMPOSE_ENV_FILE=.env.production + return 0 + fi + + if [ -r .env ]; then + COMPOSE_ENV_FILE=.env + return 0 + fi + + COMPOSE_ENV_FILE= +} + +# 환경 파일 로드(없으면 컨테이너 환경 변수 사용) +load_env_file() { + pick_compose_env_file + + if [ -z "$COMPOSE_ENV_FILE" ]; then + echo "환경 파일(.env.production 또는 .env)이 없습니다. 실행 중인 DB 컨테이너 환경 변수를 사용합니다." >&2 + return 0 + fi + + set -a + # shellcheck disable=SC1090 + . "./$COMPOSE_ENV_FILE" + set +a +} + compose() { - docker compose --env-file "$ENV_FILE" "$@" + if [ -n "$COMPOSE_ENV_FILE" ]; then + docker compose --env-file "$COMPOSE_ENV_FILE" "$@" + return 0 + fi + + docker compose "$@" +} + +# 실행 중인 DB 컨테이너에서 PostgreSQL 계정 정보 읽기 +load_db_env_from_container() { + if ! compose exec -T "$DB_SERVICE" true >/dev/null 2>&1; then + return 0 + fi + + container_db=$(compose exec -T "$DB_SERVICE" printenv POSTGRES_DB 2>/dev/null | tr -d '\r' || true) + container_user=$(compose exec -T "$DB_SERVICE" printenv POSTGRES_USER 2>/dev/null | tr -d '\r' || true) + + if [ -n "$container_db" ]; then + POSTGRES_DB=$container_db + fi + + if [ -n "$container_user" ]; then + POSTGRES_USER=$container_user + fi } psql_exec() { @@ -101,7 +152,6 @@ print_status() { baseline_migrations() { ensure_schema_migrations_table - baseline_count=0 found_target=0 migration_files | while IFS= read -r file_name; do @@ -111,7 +161,6 @@ baseline_migrations() { run_sql "INSERT INTO $SCHEMA_MIGRATIONS_TABLE (file_name) VALUES ('$file_name') ON CONFLICT (file_name) DO NOTHING;" echo "baseline $file_name" - baseline_count=$((baseline_count + 1)) if [ -n "$BASELINE_TARGET" ] && { [ "$file_name" = "$BASELINE_TARGET" ] || echo "$file_name" | grep -q "^${BASELINE_TARGET}_"; }; then found_target=1 @@ -136,7 +185,6 @@ migrate_database() { fi ensure_schema_migrations_table - applied_count=0 migration_files | while IFS= read -r file_name; do if is_applied "$file_name"; then @@ -146,13 +194,15 @@ migrate_database() { echo "apply $file_name" psql_exec -f "$CONTAINER_MIGRATIONS_DIR/$file_name" run_sql "INSERT INTO $SCHEMA_MIGRATIONS_TABLE (file_name) VALUES ('$file_name') ON CONFLICT (file_name) DO NOTHING;" - applied_count=$((applied_count + 1)) done echo "마이그레이션 실행 완료" } +load_env_file +load_db_env_from_container ensure_database_ready +load_db_env_from_container case "$MODE" in status)