🎉 Initial commit: Document Server MVP
✨ Features implemented: - FastAPI backend with JWT authentication - PostgreSQL database with async SQLAlchemy - HTML document viewer with smart highlighting - Note system connected to highlights (1:1 relationship) - Bookmark system for quick navigation - Integrated search (documents + notes) - Tag system for document organization - Docker containerization with Nginx 🔧 Technical stack: - Backend: FastAPI + PostgreSQL + Redis - Frontend: Alpine.js + Tailwind CSS - Authentication: JWT tokens - File handling: HTML + PDF support - Search: Full-text search with relevance scoring 📋 Core functionality: - Text selection → Highlight creation - Highlight → Note attachment - Note management with search/filtering - Bookmark creation at scroll positions - Document upload with metadata - User management (admin creates accounts)
This commit is contained in:
94
backend/src/core/database.py
Normal file
94
backend/src/core/database.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
데이터베이스 설정 및 연결
|
||||
"""
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
from sqlalchemy import MetaData
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from src.core.config import settings
|
||||
|
||||
|
||||
# SQLAlchemy 메타데이터 설정
|
||||
metadata = MetaData(
|
||||
naming_convention={
|
||||
"ix": "ix_%(column_0_label)s",
|
||||
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||
"pk": "pk_%(table_name)s"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""SQLAlchemy Base 클래스"""
|
||||
metadata = metadata
|
||||
|
||||
|
||||
# 비동기 데이터베이스 엔진 생성
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=settings.DEBUG,
|
||||
future=True,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=300,
|
||||
)
|
||||
|
||||
# 비동기 세션 팩토리
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
|
||||
|
||||
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""데이터베이스 세션 의존성"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
yield session
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
|
||||
async def init_db() -> None:
|
||||
"""데이터베이스 초기화"""
|
||||
from src.models import user, document, highlight, note, bookmark, tag
|
||||
|
||||
async with engine.begin() as conn:
|
||||
# 모든 테이블 생성
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
# 관리자 계정 생성
|
||||
await create_admin_user()
|
||||
|
||||
|
||||
async def create_admin_user() -> None:
|
||||
"""관리자 계정 생성 (존재하지 않을 경우)"""
|
||||
from src.models.user import User
|
||||
from src.core.security import get_password_hash
|
||||
from sqlalchemy import select
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
# 관리자 계정 존재 확인
|
||||
result = await session.execute(
|
||||
select(User).where(User.email == settings.ADMIN_EMAIL)
|
||||
)
|
||||
admin_user = result.scalar_one_or_none()
|
||||
|
||||
if not admin_user:
|
||||
# 관리자 계정 생성
|
||||
admin_user = User(
|
||||
email=settings.ADMIN_EMAIL,
|
||||
hashed_password=get_password_hash(settings.ADMIN_PASSWORD),
|
||||
is_active=True,
|
||||
is_admin=True,
|
||||
full_name="Administrator"
|
||||
)
|
||||
session.add(admin_user)
|
||||
await session.commit()
|
||||
print(f"관리자 계정이 생성되었습니다: {settings.ADMIN_EMAIL}")
|
||||
Reference in New Issue
Block a user