묶음 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
90 lines
3.9 KiB
SQL
90 lines
3.9 KiB
SQL
-- ============================================================================
|
|
-- Phase V2-M: Knowledge Base — RAG 기초 (텍스트 청크 + 단순 검색)
|
|
-- V1 RAGService의 Supabase 버전. pgvector 임베딩은 V2-M+1에서 추가 예정.
|
|
-- ============================================================================
|
|
|
|
-- pgvector extension (미래 임베딩 검색용, 현재는 미사용)
|
|
-- CREATE EXTENSION IF NOT EXISTS vector;
|
|
|
|
-- 지식 문서 메타데이터
|
|
CREATE TABLE public.knowledge_documents (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
team_id uuid REFERENCES public.teams(id) ON DELETE SET NULL,
|
|
title text NOT NULL,
|
|
file_name text,
|
|
file_type text CHECK (file_type IN ('txt', 'md', 'pdf', 'docx', 'html', 'url')),
|
|
source_url text,
|
|
storage_key text, -- Supabase Storage의 경로 (업로드된 경우)
|
|
chunk_count integer NOT NULL DEFAULT 0,
|
|
indexed boolean NOT NULL DEFAULT false,
|
|
indexed_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX idx_knowledge_documents_user_id ON public.knowledge_documents(user_id);
|
|
CREATE INDEX idx_knowledge_documents_team_id ON public.knowledge_documents(team_id);
|
|
CREATE INDEX idx_knowledge_documents_created ON public.knowledge_documents(user_id, created_at DESC);
|
|
|
|
-- 청크 — 원문을 분할한 단위. 임베딩은 추후 vector 컬럼 추가.
|
|
CREATE TABLE public.knowledge_chunks (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
document_id uuid NOT NULL REFERENCES public.knowledge_documents(id) ON DELETE CASCADE,
|
|
chunk_index integer NOT NULL,
|
|
content text NOT NULL,
|
|
-- embedding vector(768), -- V2-M+1에서 pgvector 활성화
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX idx_knowledge_chunks_document ON public.knowledge_chunks(document_id);
|
|
CREATE UNIQUE INDEX idx_knowledge_chunks_uniq ON public.knowledge_chunks(document_id, chunk_index);
|
|
|
|
-- 임시: 전문 검색을 위한 tsvector (pgvector 적용 전 대체재)
|
|
ALTER TABLE public.knowledge_chunks
|
|
ADD COLUMN tsv tsvector
|
|
GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED;
|
|
|
|
CREATE INDEX idx_knowledge_chunks_tsv ON public.knowledge_chunks USING gin(tsv);
|
|
|
|
-- RLS ------------------------------------------------------------------------
|
|
ALTER TABLE public.knowledge_documents ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.knowledge_chunks ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "knowledge_documents_read" ON public.knowledge_documents
|
|
FOR SELECT USING (
|
|
user_id = auth.uid()
|
|
OR (team_id IS NOT NULL AND team_id IN (
|
|
SELECT team_id FROM public.team_members WHERE user_id = auth.uid()
|
|
))
|
|
);
|
|
|
|
CREATE POLICY "knowledge_documents_insert" ON public.knowledge_documents
|
|
FOR INSERT WITH CHECK (user_id = auth.uid());
|
|
|
|
CREATE POLICY "knowledge_documents_update" ON public.knowledge_documents
|
|
FOR UPDATE USING (user_id = auth.uid());
|
|
|
|
CREATE POLICY "knowledge_documents_delete" ON public.knowledge_documents
|
|
FOR DELETE USING (user_id = auth.uid());
|
|
|
|
-- 청크는 부모 문서 권한 경유
|
|
CREATE POLICY "knowledge_chunks_read" ON public.knowledge_chunks
|
|
FOR SELECT USING (
|
|
document_id IN (SELECT id FROM public.knowledge_documents)
|
|
);
|
|
|
|
CREATE POLICY "knowledge_chunks_insert" ON public.knowledge_chunks
|
|
FOR INSERT WITH CHECK (
|
|
document_id IN (SELECT id FROM public.knowledge_documents WHERE user_id = auth.uid())
|
|
);
|
|
|
|
CREATE POLICY "knowledge_chunks_delete" ON public.knowledge_chunks
|
|
FOR DELETE USING (
|
|
document_id IN (SELECT id FROM public.knowledge_documents WHERE user_id = auth.uid())
|
|
);
|
|
|
|
-- updated_at trigger
|
|
CREATE TRIGGER set_updated_at_knowledge_documents
|
|
BEFORE UPDATE ON public.knowledge_documents
|
|
FOR EACH ROW EXECUTE FUNCTION public.moddatetime();
|