Files
insurance/src/store/useAuthStore.ts
T
chpark cfd550bed8
Deploy via SSH / remote-deploy (push) Failing after 6s
feat: AI판정/OCR/알림톡/소셜로그인/푸시/CODEF 전체 구현 + CI SSH 전환
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>
2026-04-23 00:56:06 +09:00

79 lines
2.1 KiB
TypeScript

import { create } from 'zustand';
import { authApi, type User } from '@/api/endpoints';
import { clearToken, loadToken, saveToken } from '@/api/client';
type State = {
user: User | null;
loading: boolean;
error: string | null;
hydrate: () => Promise<void>;
login: (email: string, password: string) => Promise<void>;
register: (body: Parameters<typeof authApi.register>[0]) => Promise<void>;
kakaoLogin: (accessToken: string) => Promise<void>;
logout: () => Promise<void>;
};
export const useAuthStore = create<State>((set) => ({
user: null,
loading: true,
error: null,
hydrate: async () => {
const tok = await loadToken();
if (!tok) {
set({ loading: false });
return;
}
try {
const me = await authApi.me();
set({ user: me, loading: false });
// push registration (non-blocking)
import('@/services/push').then((m) => m.registerForPushAsync()).catch(() => {});
} catch {
await clearToken();
set({ user: null, loading: false });
}
},
login: async (email, password) => {
set({ loading: true, error: null });
try {
const res = await authApi.login(email, password);
await saveToken(res.token);
set({ user: res.user, loading: false });
} catch (e: any) {
set({ loading: false, error: e?.message ?? '로그인 실패' });
throw e;
}
},
register: async (body) => {
set({ loading: true, error: null });
try {
const res = await authApi.register(body);
await saveToken(res.token);
set({ user: res.user, loading: false });
} catch (e: any) {
set({ loading: false, error: e?.message ?? '회원가입 실패' });
throw e;
}
},
kakaoLogin: async (accessToken) => {
set({ loading: true, error: null });
try {
const res = await authApi.kakao(accessToken);
await saveToken(res.token);
set({ user: res.user, loading: false });
} catch (e: any) {
set({ loading: false, error: e?.message ?? '카카오 로그인 실패' });
throw e;
}
},
logout: async () => {
await clearToken();
set({ user: null });
},
}));