Files
document-server/frontend/static/js/auth.js
Hyungi Ahn 46546da55f feat: 계층구조 뷰 및 완전한 하이라이트/메모 시스템 구현
주요 기능:
- 📚 Book 및 BookCategory 모델 추가 (서적 그룹화)
- 🏗️ 계층구조 뷰 (Book > Category > Document) 구현
- 🎨 완전한 하이라이트 시스템 (생성, 표시, 삭제)
- 📝 통합 메모 관리 (추가, 수정, 삭제)
- 🔄 그리드 뷰와 계층구조 뷰 간 완전 동기화
- 🛡️ 관리자 전용 문서 삭제 기능
- 🔧 모든 CORS 및 500 오류 해결

기술적 개선:
- API 베이스 URL을 Nginx 프록시로 변경 (/api)
- 외래키 제약 조건 해결 (삭제 순서 최적화)
- SQLAlchemy 관계 로딩 최적화 (selectinload)
- 프론트엔드 캐시 무효화 시스템
- Alpine.js 컴포넌트 구조 개선

UI/UX:
- 계층구조 네비게이션 (사이드바 + 트리 구조)
- 하이라이트 모드 토글 스위치
- 완전한 툴팁 기반 메모 관리 인터페이스
- 반응형 하이라이트 메뉴 (색상 선택)
- 스마트 툴팁 위치 조정 (화면 경계 고려)
2025-08-23 14:31:30 +09:00

92 lines
2.9 KiB
JavaScript

/**
* 인증 관련 Alpine.js 컴포넌트
*/
// 인증 모달 컴포넌트
window.authModal = () => ({
showLogin: false,
loginForm: {
email: '',
password: ''
},
loginError: '',
loginLoading: false,
async login() {
this.loginLoading = true;
this.loginError = '';
try {
// 실제 API 호출
const response = await window.api.login(this.loginForm.email, this.loginForm.password);
// 토큰 저장
window.api.setToken(response.access_token);
localStorage.setItem('refresh_token', response.refresh_token);
// 사용자 정보 가져오기
const userResponse = await window.api.getCurrentUser();
// 전역 상태 업데이트
window.dispatchEvent(new CustomEvent('auth-changed', {
detail: { isAuthenticated: true, user: userResponse }
}));
// 모달 닫기 (부모 컴포넌트의 상태 변경)
window.dispatchEvent(new CustomEvent('close-login-modal'));
this.loginForm = { email: '', password: '' };
} catch (error) {
this.loginError = error.message || '로그인에 실패했습니다';
} finally {
this.loginLoading = false;
}
},
async logout() {
try {
await window.api.logout();
} catch (error) {
console.error('Logout error:', error);
} finally {
// 로컬 스토리지 정리
localStorage.removeItem('refresh_token');
// 전역 상태 업데이트
window.dispatchEvent(new CustomEvent('auth-changed', {
detail: { isAuthenticated: false, user: null }
}));
}
}
});
// 자동 토큰 갱신
async function refreshTokenIfNeeded() {
const refreshToken = localStorage.getItem('refresh_token');
if (!refreshToken || !api.token) return;
try {
// 토큰 만료 확인 (JWT 디코딩)
const tokenPayload = JSON.parse(atob(api.token.split('.')[1]));
const now = Date.now() / 1000;
// 토큰이 5분 내에 만료되면 갱신
if (tokenPayload.exp - now < 300) {
const response = await api.refreshToken(refreshToken);
api.setToken(response.access_token);
localStorage.setItem('refresh_token', response.refresh_token);
}
} catch (error) {
console.error('Token refresh failed:', error);
// 갱신 실패시 로그아웃
window.api.setToken(null);
localStorage.removeItem('refresh_token');
window.dispatchEvent(new CustomEvent('auth-changed', {
detail: { isAuthenticated: false, user: null }
}));
}
}
// 5분마다 토큰 갱신 체크
setInterval(refreshTokenIfNeeded, 5 * 60 * 1000);