Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,11 @@ jobs:
SPRING_DATASOURCE_URL=${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME=${{ secrets.SPRING_DATASOURCE_USERNAME }}
SPRING_DATASOURCE_PASSWORD=${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_DATASOURCE_DRIVER_CLASS_NAME=${{ secrets.SPRING_DATASOURCE_DRIVER_CLASS_NAME }}
SPRING_JPA_HIBERNATE_DDL_AUTO=${{ secrets.SPRING_JPA_HIBERNATE_DDL_AUTO }}
SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
SPRING_JPA_HIBERNATE_DDL_AUTO=validate

SPRING_FLYWAY_ENABLED=true
SPRING_FLYWAY_URL=${{ secrets.SPRING_FLYWAY_URL }}
SPRING_FLYWAY_USER=${{ secrets.SPRING_FLYWAY_USER }}
SPRING_FLYWAY_PASSWORD=${{ secrets.SPRING_FLYWAY_PASSWORD }}
SPRING_FLYWAY_BASELINE_ON_MIGRATE=true
SPRING_FLYWAY_BASELINE_ON_MIGRATE=false

JWT_SECRET_KEY=${{ secrets.JWT_SECRETKEY }}
JWT_ACCESS_EXPIRATION=${{ secrets.JWT_ACCESS_EXPIRATION }}
Expand Down Expand Up @@ -139,6 +136,47 @@ jobs:
FIREBASE_CREDENTIALS_BASE64=${{ secrets.FIREBASE_CREDENTIALS_BASE64 }}
EOF

fail_deploy() {
echo "Unsafe production database configuration: $1" >&2
exit 1
}

get_env_value() {
grep -E "^$1=" .env | tail -n 1 | cut -d= -f2-
}

DB_URL="$(get_env_value SPRING_DATASOURCE_URL)"
DB_USERNAME="$(get_env_value SPRING_DATASOURCE_USERNAME)"
DB_PASSWORD="$(get_env_value SPRING_DATASOURCE_PASSWORD)"
DDL_AUTO="$(get_env_value SPRING_JPA_HIBERNATE_DDL_AUTO)"
FLYWAY_BASELINE="$(get_env_value SPRING_FLYWAY_BASELINE_ON_MIGRATE)"
NORMALIZED_DB_URL="$(printf '%s' "$DB_URL" | tr '[:upper:]' '[:lower:]')"
NORMALIZED_DB_USERNAME="$(printf '%s' "$DB_USERNAME" | tr '[:upper:]' '[:lower:]')"

[ -n "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL is required."
[ -n "$DB_USERNAME" ] || fail_deploy "SPRING_DATASOURCE_USERNAME is required."
[ -n "$DB_PASSWORD" ] || fail_deploy "SPRING_DATASOURCE_PASSWORD is required."
[ "$NORMALIZED_DB_USERNAME" != "root" ] || fail_deploy "SPRING_DATASOURCE_USERNAME must not be root."
[ "$DDL_AUTO" = "validate" ] || fail_deploy "SPRING_JPA_HIBERNATE_DDL_AUTO must be validate."
[ "$FLYWAY_BASELINE" = "false" ] || fail_deploy "SPRING_FLYWAY_BASELINE_ON_MIGRATE must be false."

case "$NORMALIZED_DB_URL" in
*allowpublickeyretrieval=true*) fail_deploy "SPRING_DATASOURCE_URL must not enable allowPublicKeyRetrieval." ;;
esac

case "$NORMALIZED_DB_URL" in
*createdatabaseifnotexist=true*) fail_deploy "SPRING_DATASOURCE_URL must not create databases at startup." ;;
esac

case "$NORMALIZED_DB_URL" in
*usessl=false*) fail_deploy "SPRING_DATASOURCE_URL must not disable TLS." ;;
esac

case "$NORMALIZED_DB_URL" in
*sslmode=required*|*sslmode=verify_ca*|*sslmode=verify_identity*) ;;
*) fail_deploy "SPRING_DATASOURCE_URL must declare sslMode=REQUIRED, VERIFY_CA, or VERIFY_IDENTITY." ;;
esac

echo "${{ secrets.GHCR_READ_TOKEN }}" | sudo docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin

if sudo docker compose version >/dev/null 2>&1; then
Expand Down
56 changes: 3 additions & 53 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,62 +62,12 @@ jobs:



# 4. 환경 변수 설정 파일 생성
- name: Create Config Files
run: |
mkdir -p ontime-back/src/main/resources
mkdir -p ontime-back/src/main/resources/key
echo "spring.application.name=${{ secrets.SPRING_APPLICATION_NAME }}" > ontime-back/src/main/resources/application.properties
echo "spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_db?serverTimezone=UTC&useSSL=false" >> ontime-back/src/main/resources/application.properties
echo "spring.datasource.username=test_user" >> ontime-back/src/main/resources/application.properties
echo "spring.datasource.password=test_password" >> ontime-back/src/main/resources/application.properties
echo "spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver" >> ontime-back/src/main/resources/application.properties
echo "spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect" >> ontime-back/src/main/resources/application.properties
echo "spring.jpa.hibernate.ddl-auto=validate" >> ontime-back/src/main/resources/application.properties
echo "spring.sql.init.mode=always" >> ontime-back/src/main/resources/application.properties
echo "jwt.secret.key=${{ secrets.JWT_SECRETKEY }}" >> ontime-back/src/main/resources/application.properties
echo "jwt.access.expiration=${{ secrets.JWT_ACCESS_EXPIRATION }}" >> ontime-back/src/main/resources/application.properties
echo "jwt.refresh.expiration=${{ secrets.JWT_REFRESH_EXPIRATION }}" >> ontime-back/src/main/resources/application.properties
echo "jwt.access.header=${{ secrets.JWT_ACCESS_HEADER }}" >> ontime-back/src/main/resources/application.properties
echo "jwt.refresh.header=${{ secrets.JWT_REFRESH_HEADER }}" >> ontime-back/src/main/resources/application.properties
echo "google.web.client-id = ${{ secrets.GOOGLE_WEB_CLIENT_ID }}" >> ontime-back/src/main/resources/application.properties
echo "google.app.client-id = ${{ secrets.GOOGLE_APP_CLIENT_ID }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.google.client-secret=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.google.scope=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.google.redirect-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.google.authorization-grant-type=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_AUTHORIZATION_GRANT_TYPE }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.google.client-name=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_NAME }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.google.authorization-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_AUTHORIZATION_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.google.token-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_TOKEN_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.google.user-info-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_INFO_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.google.user-name-attribute=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_NAME_ATTRIBUTE }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.kakao.client-id=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.kakao.scope=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.kakao.redirect-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.kakao.authorization-grant-type=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.registration.kakao.client-name=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.kakao.authorization-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_AUTHORIZATION_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.kakao.token-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_TOKEN_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.kakao.user-info-uri=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_INFO_URI }}" >> ontime-back/src/main/resources/application.properties
echo "spring.security.oauth2.client.provider.kakao.user-name-attribute=${{ secrets.SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE }}" >> ontime-back/src/main/resources/application.properties
echo "apple.client.id=${{ secrets.APPLE_CLIENT_ID }}" >> ontime-back/src/main/resources/application.properties
echo "apple.client.secret=${{ secrets.APPLE_CLIENT_SECRET }}" >> ontime-back/src/main/resources/application.properties
echo "apple.login.key=${{ secrets.APPLE_LOGIN_KEY }}" >> ontime-back/src/main/resources/application.properties
echo "apple.team.id=${{ secrets.APPLE_TEAM_ID }}" >> ontime-back/src/main/resources/application.properties
echo "spring.flyway.enabled=true" >> ontime-back/src/main/resources/application.properties
echo "spring.flyway.url=jdbc:mysql://127.0.0.1:3306/test_db?serverTimezone=UTC&useSSL=false" >> ontime-back/src/main/resources/application.properties
echo "spring.flyway.user=test_user" >> ontime-back/src/main/resources/application.properties
echo "spring.flyway.password=test_password" >> ontime-back/src/main/resources/application.properties
echo "spring.flyway.baseline-on-migrate=true" >> ontime-back/src/main/resources/application.properties
echo "management.endpoints.web.exposure.include=health" >> ontime-back/src/main/resources/application.properties
echo "management.endpoint.health.show-details=always" >> ontime-back/src/main/resources/application.properties
echo "${{ secrets.ONTIME_PUSH_FIREBASE_ADMINSDK }}" > ontime-back/src/main/resources/ontime-c63f1-firebase-adminsdk-fbsvc-a043cdc829.json
echo "${{ secrets.AUTHKEY_743M7R5W3W }}" > ontime-back/src/main/resources/key/AuthKey_743M7R5W3W.p8

# 5. Gradle 빌드 & JUnit 테스트 실행
# 4. Gradle 빌드 & JUnit 테스트 실행
- name: Run Tests with Gradle
id: run-tests # 실행 결과를 output으로 저장할 id 추가
continue-on-error: true
env:
SPRING_PROFILES_ACTIVE: test
run: |
cd ontime-back
./gradlew test
Expand Down
16 changes: 16 additions & 0 deletions ontime-back/docs/database-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Database Configuration

Production uses MySQL with environment-provided database credentials.

## Production

- Spring profile: `prod`
- Datasource URL: `SPRING_DATASOURCE_URL`
- Datasource username: `SPRING_DATASOURCE_USERNAME`
- Datasource password: `SPRING_DATASOURCE_PASSWORD`
- Datasource driver: `com.mysql.cj.jdbc.Driver`
- Hibernate DDL mode: `validate`
- SQL logging: disabled
- Formatted SQL logging: disabled
- Flyway: enabled
- Flyway baseline on migrate: disabled
44 changes: 44 additions & 0 deletions ontime-back/src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Database Configuration
spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/ontime_db?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:root}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:ontime1234}
spring.datasource.driver-class-name=${SPRING_DATASOURCE_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}

# JPA / Hibernate
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# Flyway
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=true

# JWT Configuration
jwt.secret.key=${JWT_SECRET_KEY:my_secret_key_for_ontime_back_application_development_environment_1234567890}
jwt.access.expiration=${JWT_ACCESS_EXPIRATION:3600000}
jwt.refresh.expiration=${JWT_REFRESH_EXPIRATION:1209600000}
jwt.access.header=${JWT_ACCESS_HEADER:Authorization}
jwt.refresh.header=${JWT_REFRESH_HEADER:Authorization-refresh}

# Google OAuth
google.web.client-id=${GOOGLE_WEB_CLIENT_ID:599377893328-dljp16andl10374bnm9b1nnfp9uj5pvd.apps.googleusercontent.com}
google.app.client-id=${GOOGLE_APP_CLIENT_ID:456571312261-r35ah9qi0qaq7al007e2db0e0jmjcmb4.apps.googleusercontent.com}

# Apple OAuth
apple.client.id=${APPLE_CLIENT_ID:your_apple_client_id}
apple.team.id=${APPLE_TEAM_ID:your_apple_team_id}
apple.login.key=${APPLE_LOGIN_KEY:your_apple_key_id}
apple.client.secret=${APPLE_CLIENT_SECRET:your_apple_private_key}
apple.private-key.base64=${APPLE_PRIVATE_KEY_BASE64:}

# Firebase
firebase.credentials.base64=${FIREBASE_CREDENTIALS_BASE64:}

# Logging
logging.level.root=INFO
logging.level.devkor.ontime_back=DEBUG

# Feature flags
feature.apple-login.enabled=${FEATURE_APPLE_LOGIN_ENABLED:true}
22 changes: 22 additions & 0 deletions ontime-back/src/main/resources/application-prod.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# Database Configuration
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
spring.datasource.driver-class-name=${SPRING_DATASOURCE_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}

# JPA / Hibernate
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=false

# Flyway
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=false

# Logging
logging.level.root=INFO
logging.level.devkor.ontime_back=INFO

# Actuator
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health
management.health.readinessstate.enabled=true
Expand Down
48 changes: 48 additions & 0 deletions ontime-back/src/main/resources/application-test.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Database Configuration
spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:mysql://127.0.0.1:3306/test_db?serverTimezone=UTC&useSSL=false}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:test_user}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:test_password}
spring.datasource.driver-class-name=${SPRING_DATASOURCE_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}

# JPA / Hibernate
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=false

# Flyway
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=false

# JWT Configuration
jwt.secret.key=${JWT_SECRET_KEY:test_secret_key_for_ontime_back_application_tests_1234567890}
jwt.access.expiration=${JWT_ACCESS_EXPIRATION:3600000}
jwt.refresh.expiration=${JWT_REFRESH_EXPIRATION:1209600000}
jwt.access.header=${JWT_ACCESS_HEADER:Authorization}
jwt.refresh.header=${JWT_REFRESH_HEADER:Authorization-refresh}

# Google OAuth
google.web.client-id=${GOOGLE_WEB_CLIENT_ID:test-google-web-client-id}
google.app.client-id=${GOOGLE_APP_CLIENT_ID:test-google-app-client-id}

# Apple OAuth
apple.client.id=${APPLE_CLIENT_ID:test-apple-client-id}
apple.team.id=${APPLE_TEAM_ID:test-apple-team-id}
apple.login.key=${APPLE_LOGIN_KEY:test-apple-key-id}
apple.client.secret=${APPLE_CLIENT_SECRET:}
apple.private-key.base64=${APPLE_PRIVATE_KEY_BASE64:}

# Firebase
firebase.credentials.base64=${FIREBASE_CREDENTIALS_BASE64:}

# Logging
logging.level.root=INFO
logging.level.devkor.ontime_back=INFO

# Feature flags
feature.apple-login.enabled=${FEATURE_APPLE_LOGIN_ENABLED:false}

# Actuator
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=always
Loading
Loading