Add backend/frontend scaffolding with Oracle ADB wallet config

- Backend: Spring Boot 3 + WebFlux, JWT auth, Oracle ADB wallet,
  8 controllers/services/repositories (Auth~Tag), DTOs, exception handling
- Frontend: Next.js 15, TypeScript, Tailwind CSS, AuthContext,
  7 pages (dashboard, knowledge, chat, study, todos, habits, login)
- DB: V1 migration with 12 tables including VECTOR(1024) + HNSW index
- Ops: PM2 ecosystem config, deploy.sh, start-backend.sh
- CLAUDE.md: DB credentials replaced with env var references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 06:56:26 +00:00
parent 5d8b0fcdb8
commit 3d2aa6cf46
68 changed files with 2235 additions and 17 deletions

View File

@@ -0,0 +1,156 @@
-- ============================================
-- SUNDOL Database Schema - Oracle 23ai
-- ============================================
-- USERS
CREATE TABLE users (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
email VARCHAR2(320) NOT NULL UNIQUE,
display_name VARCHAR2(255),
avatar_url VARCHAR2(1000),
google_sub VARCHAR2(255) UNIQUE,
refresh_token VARCHAR2(1000),
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
-- KNOWLEDGE_ITEMS
CREATE TABLE knowledge_items (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
type VARCHAR2(20) NOT NULL,
title VARCHAR2(500),
source_url VARCHAR2(2000),
raw_text CLOB,
status VARCHAR2(20) DEFAULT 'PENDING' NOT NULL,
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_ki_user_id ON knowledge_items(user_id);
CREATE INDEX idx_ki_status ON knowledge_items(status);
-- KNOWLEDGE_CHUNKS
CREATE TABLE knowledge_chunks (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
knowledge_item_id RAW(16) NOT NULL REFERENCES knowledge_items(id) ON DELETE CASCADE,
chunk_index NUMBER(10) NOT NULL,
content CLOB NOT NULL,
embedding VECTOR(1024, FLOAT32),
token_count NUMBER(10),
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_kc_item_id ON knowledge_chunks(knowledge_item_id);
-- VECTOR index for semantic search
CREATE VECTOR INDEX idx_kc_embedding ON knowledge_chunks(embedding)
ORGANIZATION NEIGHBOR PARTITIONS
DISTANCE COSINE
WITH TARGET ACCURACY 95;
-- TAGS
CREATE TABLE tags (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
name VARCHAR2(100) NOT NULL,
color VARCHAR2(7) DEFAULT '#3b82f6',
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX idx_tags_user_name ON tags(user_id, name);
-- KNOWLEDGE_ITEM_TAGS (many-to-many)
CREATE TABLE knowledge_item_tags (
knowledge_item_id RAW(16) NOT NULL REFERENCES knowledge_items(id) ON DELETE CASCADE,
tag_id RAW(16) NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (knowledge_item_id, tag_id)
);
-- CHAT_SESSIONS
CREATE TABLE chat_sessions (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
title VARCHAR2(500) DEFAULT 'New Chat',
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_cs_user_id ON chat_sessions(user_id);
-- CHAT_MESSAGES
CREATE TABLE chat_messages (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
session_id RAW(16) NOT NULL REFERENCES chat_sessions(id) ON DELETE CASCADE,
role VARCHAR2(20) NOT NULL,
content CLOB NOT NULL,
source_chunks CLOB,
tokens_used NUMBER(10),
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_cm_session_id ON chat_messages(session_id);
-- TODOS
CREATE TABLE todos (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
parent_id RAW(16) REFERENCES todos(id) ON DELETE CASCADE,
title VARCHAR2(500) NOT NULL,
description CLOB,
status VARCHAR2(20) DEFAULT 'PENDING' NOT NULL,
priority VARCHAR2(10) DEFAULT 'MEDIUM' NOT NULL,
due_date DATE,
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_todos_user_id ON todos(user_id);
CREATE INDEX idx_todos_parent_id ON todos(parent_id);
-- HABITS
CREATE TABLE habits (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
name VARCHAR2(200) NOT NULL,
description VARCHAR2(1000),
habit_type VARCHAR2(10) DEFAULT 'BUILD' NOT NULL,
target_days VARCHAR2(50) DEFAULT 'DAILY',
color VARCHAR2(7) DEFAULT '#22c55e',
streak_current NUMBER(10) DEFAULT 0,
streak_best NUMBER(10) DEFAULT 0,
is_active NUMBER(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_habits_user_id ON habits(user_id);
-- HABIT_LOGS
CREATE TABLE habit_logs (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
habit_id RAW(16) NOT NULL REFERENCES habits(id) ON DELETE CASCADE,
log_date DATE NOT NULL,
checked_in NUMBER(1) DEFAULT 1,
note VARCHAR2(500),
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX idx_hl_habit_date ON habit_logs(habit_id, log_date);
-- STUDY_CARDS
CREATE TABLE study_cards (
id RAW(16) DEFAULT SYS_GUID() PRIMARY KEY,
user_id RAW(16) NOT NULL REFERENCES users(id),
knowledge_item_id RAW(16) REFERENCES knowledge_items(id) ON DELETE SET NULL,
front CLOB NOT NULL,
back CLOB NOT NULL,
ease_factor NUMBER(4,2) DEFAULT 2.50,
interval_days NUMBER(10) DEFAULT 0,
repetitions NUMBER(10) DEFAULT 0,
next_review_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE INDEX idx_sc_user_review ON study_cards(user_id, next_review_at);