윤찬
|
8af75a0a1e
|
feat: V2-7 Admin 콘솔 리디자인 + 3단계 권한 + SaaS 전환 + 코드 정리
- Admin CRM: D3RO Console 스타일 전체 적용 (panelSx/tableSx/filterBtnSx)
- 3단계 권한: manager/admin/super_admin (DB + Edge Functions + Frontend)
- 랜딩 페이지: 1회 결제 → 월간/연간 구독 SaaS 모델 (10개 언어)
- SSE 스트리밍: VoiceConversation Premium LLM 라우팅 + fallback
- Supabase 클라이언트: packages/api-client 공통 추출 (browser+server)
- RPC 함수 타입: 9개 정의 (admin_usage_by_feature 등)
- callAdminApi 401 버그 수정 (getUser() 선행 토큰 갱신)
|
2026-04-13 01:28:10 +09:00 |
|
윤찬
|
6e52c18e5b
|
feat(desktop+server): Phase 3.2 Premium LLM — Anthropic Claude 프리미엄 파이프라인 + 모델별 쿼터 + SaaS UI
빅뱅 8/8 마지막 성공 기준 달성. Supabase Edge Function(llm-proxy)을 통해
Anthropic Claude를 호출하는 PremiumLLMService 신규 구현. 사용자가 Settings에서
Local/Premium 백엔드를 선택하면 VoiceModeService가 자동 분기하고, Premium 실패 시
Local로 silent fallback + 상단 중앙 배너 알림.
실측: Claude Haiku refine 1.6~3.2초 (이전 qwen3 42.9초 → 13~27배 빠름).
주요 변경:
- PremiumLLMService 신규 (싱글톤+EventEmitter, processText/chatStream,
Supabase functions.invoke 기반, _ensureAuth 가드)
- llm-prompts.ts: SYSTEM_PROMPTS를 Local/Premium 공유 모듈로 추출
(resolveSystemPrompt 헬퍼)
- VoiceModeService: _getLLMProcessor → _runProcessorWithFallback 라우터 +
premium-llm-fallback 이벤트
- CloudSyncService: getAccessToken(async), getAnonKey, invokeFunction(auth
헤더 자동 처리, 에러 body 파싱)
- IPC: LLM.PREMIUM_* 채널 6개 + preload API + llm-handlers 이벤트 전달
(safeSendToRenderer 헬퍼)
- AppConfig.llmBackend: 'local' | 'premium' (기본 'local')
- Settings UI: Backend 드롭다운 + Premium 선택 시 Ollama UI 숨김 + 라이선스
모달 자동 오픈
- AppLayout: 상단 중앙 Snackbar fallback 배너 (8초, warning filled)
- LicenseModal: 라이선스 키 입력 제거 → SaaS 구독 관리 UI 전환
(Free/Pro/Pro+ 업그레이드 버튼, Payple 준비 중 스텁)
- 등급 비교 표: featureLabel i18n 번역 수정
서버 (Supabase Edge Functions):
- quota.ts: 모델별 쿼터 구조 (llm_haiku/sonnet/opus × free/pro/pro_plus),
주간/일간 기간 분리, modelToQuotaKey 매핑, consumeQuota baseLimit 파라미터화
- llm-proxy: 모델별 쿼터 체크 + 소비 (checkQuota → consumeQuota 원자적),
verify_jwt=false (2026 sb_publishable_ 키 호환)
- config.toml: llm-proxy verify_jwt = false
- migration 20260412000001: tier team→pro_plus 통일, subscriptions.overage_credits
컬럼, consume_quota RPC (원자적 base→overage fallback)
Tier/쿼터:
- free: Haiku 250/주간, Sonnet/Opus 불가
- pro ₩9,900: Haiku 1500/일, Sonnet 300/일, Opus 50/일
- pro_plus ₩29,900: Haiku 무제한, Sonnet 1500/일, Opus 300/일
- api-client SubscriptionTier: team→pro_plus, overage_credits 필드 추가
|
2026-04-12 18:28:02 +09:00 |
|
yunchan8804
|
1fa24ce3c9
|
feat(V2-4차): Database 제네릭 복원 + /chat + /knowledge + Resend + Push + 문서 생성
묶음 K — Database 제네릭 완전 주입:
- @supabase/ssr 0.5→0.10, @supabase/supabase-js 2.45→2.103 bump
- 버전 정합으로 @supabase/ssr의 Database 제약이 정상 동작
- packages/api-client/src/types.ts:
- TypedTable<Row, Insert, Update>로 Row & Record<string, unknown> 교차 유지
- 각 테이블 Insert를 Partial<Row> & Pick<필수필드>로 교체
(optional 컬럼이 required로 추론되던 문제 해결)
- push_tokens / team_invites 테이블 타입도 inline 추가
- index.ts: types + client + auth + meetings + history + usage 전체 re-export 복원
- apps/web supabase-browser/server에 <Database> 제네릭 주입
묶음 L — /chat 페이지 (Voice Conversation 이식):
- apps/web/src/components/chat/chat-panel.tsx (client)
- 메시지 state, user/assistant 말풍선, MetalCard 래핑
- llm-proxy POST (non-streaming JSON), Anthropic 응답 형식 파싱
- Enter 전송, Shift+Enter 줄바꿈, 초기화 버튼
- apps/web/src/app/(app)/chat/page.tsx
- Sidebar에 Chat 메뉴 추가 (ChatIcon)
- ko.json nav.chat 키
묶음 M — /knowledge 페이지 (RAG 이식):
- migrations/20260410000002_knowledge_documents.sql
- knowledge_documents + knowledge_chunks 테이블
- tsvector 자동 생성 컬럼 + GIN 인덱스 (전문 검색)
- RLS: 개인/팀 분기 (team_id NULL 가능)
- pgvector는 주석 처리 (V2-M+1에서 활성화)
- apps/web/src/app/(app)/knowledge/page.tsx — 카드 리스트 + 상태 배지
- components/knowledge/add-knowledge-form.tsx — 제목/타입/본문 입력,
800자 단위 청킹 후 documents+chunks INSERT
- api-client types.ts에 KnowledgeDocument/Chunk 추가 + Database 등록
- Sidebar Knowledge 메뉴 (LibraryBooksIcon) + ko.json 키
묶음 N — team-invite Resend 이메일:
- functions/team-invite/index.ts에 Resend API 호출 로직 추가
- RESEND_API_KEY 설정 시 HTML 이메일 발송 (팀 이름/초대자/버튼/만료)
- 응답에 email_sent / email_error 포함
- API 키 없으면 기존대로 URL만 반환
묶음 O — Expo Push notification:
- migrations/20260410000003_push_tokens.sql (user_id, token unique, platform)
- functions/send-push/index.ts: 호출자 인증 + 본인/팀 멤버 권한 체크,
대상 사용자 push_tokens 조회, Expo Push API 배치 호출
- config.toml에 send-push 함수 등록
- apps/mobile/package.json에 expo-device + expo-notifications 추가
- apps/mobile/lib/push.ts: registerPushToken (권한 요청, projectId,
Android 채널, Supabase upsert)
- auth-context.tsx에서 로그인 직후 자동 등록
- app.json plugins에 expo-notifications
묶음 P — meetings/[id] 문서 생성:
- apps/web/components/meetings/generate-document-button.tsx (client)
- 4개 템플릿 메뉴 (minutes/report/idea-note/mindmap)
- 각 템플릿별 systemPrompt 지정
- llm-proxy 호출 → meeting_documents INSERT
- latency 측정, prompt_used 기록
- meetings/[id]/page.tsx DOCUMENTS 섹션에 버튼 노출
(transcript는 edited_transcript ?? raw_transcript 우선)
검증:
- desktop typecheck + build OK
- web typecheck + build OK (14 라우트: 기존 12 + chat + knowledge)
- api-client test 19 passed
통계:
- 총 Edge Functions 10개: stt/llm-proxy, stripe-checkout/portal/webhook,
team-invite/accept, send-push
- 총 SQL 마이그레이션 7개
- 웹 라우트 14개, 모바일 화면 5개
- 테스트 19개 passed
|
2026-04-10 08:51:53 +09:00 |
|
yunchan8804
|
c167737198
|
feat(V2-3차): pull 확장 + invite flow + Realtime + 테스트 + mobile UI 교체
묶음 E — V2-4b pull 확장:
- CloudSyncService.pullAll()에 meetings/meeting_memos/meeting_documents 추가
- meetings: LWW, 모든 컬럼 매핑 (minutes_json JSON 직렬화)
- meeting_memos: immutable INSERT-only 전략
- meeting_documents: LWW UPDATE
묶음 F — api-client types:
- interface -> type alias 전환 (11개)
- Database 타입에 TypedTable<Row, Insert, Update> 유틸 도입
(Row & Record<string, unknown> 교차로 GenericTable 제약 만족)
- @supabase/ssr 2.102는 supabase-js 버전 불일치로 제네릭 주입 불가 -
다음 사이클로 이월, api-client index.ts는 types만 재수출
묶음 G — apps/mobile UI 교체:
- login.tsx: MetalCard + PhosphorText(hero/label) + PhysicalButton
- meetings.tsx: MetalCard + Led(status) + PhosphorText(body/meta)
- record.tsx: Led + PhosphorText + PhysicalButton
- profile.tsx: MetalCard + PhysicalButton(danger) + d3roNativePalette
묶음 H — V2-7b 이메일 invite flow:
- migrations/20260410000001_team_invites.sql
- team_invites 테이블 (token, email, role, expires_at 7일)
- RLS: 같은 팀 멤버 + 초대 이메일 소유자 SELECT,
owner/admin만 INSERT/DELETE
- generate_invite_token() SECURITY DEFINER RPC (service_role)
- functions/team-invite: 권한 체크 -> 토큰 생성 -> 초대 URL 반환
- functions/team-accept: 토큰 검증 -> expires_at/accepted_at/이메일 일치 ->
team_members upsert -> 초대 accepted 표시
- config.toml에 team-invite/team-accept 함수 등록
- InviteMemberForm 재작성: 이메일 입력 + 역할 선택 -> URL 복사 UI
- /accept-invite 페이지 신규 (Suspense 내 useSearchParams + token 수락)
묶음 I — Realtime transcripts:
- apps/web/components/meetings/live-transcript-list.tsx (client)
- supabase.channel('transcripts:meeting:${id}').on('postgres_changes')
- INSERT -> 세그먼트 추가, UPDATE -> row 교체
- 중복 방지 segment_index 기준
- edited 배지 표시
- meetings/[id]/page.tsx의 transcript 섹션을 LiveTranscriptList로 교체
묶음 J — 테스트:
- packages/api-client/__tests__/client.test.ts (9 tests)
- createD3roSupabaseClient, isClientConfigured 팩토리 검증
- packages/api-client/__tests__/types.test.ts (10 tests)
- 모든 Row 타입 + 리터럴 union + Database keyof
- vitest.config.ts 신규
- apps/web/playwright.config.ts 신규 (baseURL, webServer dev 서버)
- apps/web/e2e/smoke.spec.ts 신규 (6 스모크 테스트)
- apps/web tsconfig exclude에 e2e/playwright.config.ts 추가
- package.json scripts: test, test:e2e, test:e2e:ui
검증:
- desktop typecheck OK
- web typecheck OK
- web next build OK (12 라우트, /accept-invite Suspense 적용)
- desktop build OK
- api-client test 19 passed
|
2026-04-10 08:34:52 +09:00 |
|
yunchan8804
|
d0c33ca259
|
feat(V2-3): Web App MVP — Next.js 15 + @d3ro/api-client
packages/api-client (@d3ro/api-client) 신규:
- types.ts: 12개 테이블 Row/Insert/Update 타입 + Database 형식
- client.ts: createD3roSupabaseClient 팩토리 (placeholder fallback)
- auth.ts: signInWithOAuth/signOut/getSession/onAuthStateChange
- meetings.ts: 회의 + 메모 + 문서 + 전사 CRUD
- history.ts: 음성 입력 이력 CRUD
- usage.ts: 일일 쿼터 + 구독 조회
- 루트 barrel은 types만 re-export, 함수는 subpath import 전용
apps/web (@d3ro/web) 신규 — Next.js 15 App Router:
- transpilePackages: @d3ro/core, ui, i18n, api-client
- ThemeProvider (MUI + AppRouterCacheProvider)
- I18nProvider (localStorage 어댑터)
- AuthProvider (Supabase session Context)
- 라우트 9개:
- / (auth 상태 기반 리다이렉트)
- /login (Google/GitHub OAuth, 미설정 경고)
- /auth/callback (code -> session 교환)
- /dashboard (요약 카드 + 최근 회의)
- /meetings (카드 그리드 리스트)
- /meetings/[id] (transcripts/memos/documents)
- /record (getUserMedia + MediaRecorder + stt-proxy)
- Sidebar, 인증 가드, 9바 웨이브폼, 레벨 미터
packages/ui 확장:
- MetalCard가 BoxProps 상속 (sx/onClick 등 전달)
- theme.ts에 typoSx(key) 헬퍼 추가 (d3roTypo -> MUI sx 변환)
- d3roPalette.tag.blue 추가 (M5 정리 포함)
- DS 컴포넌트 9개에 'use client' directive
- MetalDial 미사용 import 제거
packages/i18n 확장:
- ko.json에 7개 새 키 (nav.meetings/record/logout, login.*)
설계 결정:
- Database 제네릭 현재는 기본 타입 (V2-4에서 supabase gen types로 자동화)
- api-client barrel은 타입만 노출해서 컴파일 전파 차단
- DS 컴포넌트 client 경계 명시
- env 없어도 Next.js 빌드 성공 (placeholder URL/key)
검증:
- web typecheck OK
- desktop typecheck OK (회귀 없음)
- web next build OK (9 라우트 정적/동적 생성)
- desktop build OK (회귀 없음)
|
2026-04-09 02:52:31 +09:00 |
|