feat: AI판정/OCR/알림톡/소셜로그인/푸시/CODEF 전체 구현 + CI SSH 전환
Deploy via SSH / remote-deploy (push) Failing after 6s

Backend (server/src):
- services/anthropic.ts — Claude API 래퍼 (키 없으면 룰베이스 fallback)
- services/ocr.ts — Naver Clova + Google Vision 듀얼 연동 + 영수증 필드 파서
- services/solapi.ts — 카카오 알림톡 HMAC 서명 + 드라이런
- services/expoPush.ts — Expo Push API 전송
- services/codef.ts — 보험 통합조회 mock + 실연동 포인트
- routes/ai.ts, ocr.ts, devices.ts, social.ts (naver/apple), alimtalk.ts, codef.ts
- Prisma: PushDevice 모델 + binaryTargets linux-musl-openssl-3.0.x
- Dockerfile: apk add openssl (Prisma schema engine 정상화)
- api-secrets에 9개 외부 API 키 슬롯 추가 (optional)

Frontend:
- api/endpoints.ts: aiApi, ocrApi, deviceApi, socialApi, codefApi
- services/kakao.ts — Kakao JS SDK 동적 로드 + Auth.login
- services/push.ts — expo-notifications 권한/토큰 등록 + 서버 전송
- LoginScreen — 카카오/네이버/애플 버튼 (웹은 토큰 입력 fallback)
- AIJudgeScreen — 실제 /ai/claim-judge 호출, source(llm/rules) 표시
- ClaimScreen — 영수증 촬영 시 자동 OCR → 병원/날짜/제목 자동 기입
- useAuthStore hydrate 시 푸시 토큰 등록

Infra:
- eas.json (development/preview/production 빌드 프로필)
- API_KEYS.md — 9개 외부 서비스 발급/등록 가이드
- scripts/deploy-remote.sh 개선 (sudo 정확히, traefik cp 버그 수정, API fail 시 로그 출력)
- deploy/k8s/api.yaml — 외부 API 키 환경변수 매핑 (optional=true)

CI/CD:
- .gitea/workflows/deploy.yml → SSH 기반으로 전환
  (appleboy/ssh-action으로 서버 접속 → deploy-remote.sh 실행)
- 필요 Secrets: SSH_HOST, SSH_USER, SSH_PASSWORD

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-04-23 00:56:06 +09:00
parent ff18784983
commit cfd550bed8
26 changed files with 1048 additions and 141 deletions
+20 -112
View File
@@ -1,122 +1,30 @@
name: Build & Deploy
name: Deploy via SSH
on:
push:
branches: [master, main]
workflow_dispatch:
env:
REGISTRY: git.junggomoa.com
WEB_IMAGE: chpark/insurance
API_IMAGE: chpark/insurance-api
API_BASE_URL: https://api.insurance.junggomoa.com
jobs:
build-and-deploy:
remote-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set short SHA
run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Gitea Container Registry
uses: docker/login-action@v3
- name: Trigger remote deploy on server
uses: appleboy/ssh-action@v1.0.3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build & push WEB image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
build-args: |
EXPO_PUBLIC_API_BASE=${{ env.API_BASE_URL }}
push: true
tags: |
${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:latest
${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:${{ env.SHORT_SHA }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:buildcache,mode=max
- name: Build & push API image
uses: docker/build-push-action@v5
with:
context: ./server
file: ./server/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.API_IMAGE }}:latest
${{ env.REGISTRY }}/${{ env.API_IMAGE }}:${{ env.SHORT_SHA }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:buildcache,mode=max
- name: Set up kubectl
uses: azure/setup-kubectl@v4
with:
version: "v1.29.0"
- name: Configure kubeconfig
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config
- name: Ensure namespace, registry & DB secrets
run: |
kubectl apply -f deploy/k8s/namespace.yaml
kubectl -n insurance create secret docker-registry gitea-registry \
--docker-server=${{ env.REGISTRY }} \
--docker-username=${{ secrets.REGISTRY_USER }} \
--docker-password=${{ secrets.REGISTRY_TOKEN }} \
--dry-run=client -o yaml | kubectl apply -f -
kubectl -n insurance create secret generic postgres-credentials \
--from-literal=username=insurance \
--from-literal=password='${{ secrets.POSTGRES_PASSWORD }}' \
--dry-run=client -o yaml | kubectl apply -f -
kubectl -n insurance create secret generic api-secrets \
--from-literal=jwtSecret='${{ secrets.JWT_SECRET }}' \
--from-literal=databaseUrl="postgresql://insurance:${{ secrets.POSTGRES_PASSWORD }}@postgres:5432/insurance?schema=public" \
--dry-run=client -o yaml | kubectl apply -f -
- name: Deploy Postgres
run: kubectl apply -f deploy/k8s/postgres.yaml
- name: Wait for Postgres
run: kubectl -n insurance rollout status statefulset/postgres --timeout=180s
- name: Deploy API
run: |
kubectl apply -f deploy/k8s/api.yaml
kubectl -n insurance set image deployment/insurance-api \
api=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:${{ env.SHORT_SHA }}
kubectl -n insurance rollout status deployment/insurance-api --timeout=240s
- name: Deploy Web
run: |
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml
if [ "${{ secrets.INGRESS_MODE }}" = "ingressroute" ]; then
kubectl apply -f deploy/k8s/ingressroute-traefik.yaml
else
kubectl apply -f deploy/k8s/ingress.yaml
fi
kubectl -n insurance set image deployment/insurance-web \
web=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:${{ env.SHORT_SHA }}
kubectl -n insurance rollout status deployment/insurance-web --timeout=180s
- name: Show deployment info
run: |
kubectl -n insurance get deployment,statefulset,svc,ingress,pvc
echo ""
echo "🚀 Web: https://insurance.junggomoa.com"
echo "🔌 API: https://api.insurance.junggomoa.com"
host: ${{ secrets.SSH_HOST }}
port: ${{ secrets.SSH_PORT || 22 }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.SSH_PASSWORD }}
command_timeout: 20m
script: |
set -e
cd /home/chpark
if [ ! -d insurance/.git ]; then
git clone https://git.junggomoa.com/chpark/insurance.git
fi
cd insurance
git fetch origin
git reset --hard origin/master
chmod +x scripts/deploy-remote.sh
bash scripts/deploy-remote.sh