- FastAPI 라우터에서 슬래시 문제로 인한 307 리다이렉트 수정 - Nginx 프록시 설정에서 경로 중복 문제 해결 - 계정 관리 시스템 구현 (로그인, 사용자 관리, 권한 설정) - 노트북 연결 기능 수정 (notebook_id 필드 추가) - 메모 트리 UI 개선 (수평 레이아웃, 드래그 기능 제거) - 헤더 UI 개선 및 고정 위치 설정 - 백업/복원 스크립트 추가 - PDF 미리보기 토큰 인증 지원
275 lines
14 KiB
HTML
275 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>시스템 초기 설정 - Document Server</title>
|
|
|
|
<!-- Tailwind CSS -->
|
|
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
|
|
|
|
<!-- Font Awesome -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
|
|
<!-- Alpine.js -->
|
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
|
|
<!-- 공통 스타일 -->
|
|
<link rel="stylesheet" href="static/css/common.css">
|
|
</head>
|
|
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen" x-data="setupApp()">
|
|
<!-- 메인 컨테이너 -->
|
|
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
|
<div class="max-w-md w-full space-y-8">
|
|
<!-- 로고 및 제목 -->
|
|
<div class="text-center">
|
|
<div class="mx-auto h-20 w-20 bg-blue-600 rounded-full flex items-center justify-center mb-6">
|
|
<i class="fas fa-book text-white text-3xl"></i>
|
|
</div>
|
|
<h2 class="text-3xl font-bold text-gray-900 mb-2">Document Server</h2>
|
|
<p class="text-gray-600">시스템 초기 설정</p>
|
|
</div>
|
|
|
|
<!-- 설정 상태 확인 중 -->
|
|
<div x-show="loading" class="text-center">
|
|
<div class="inline-flex items-center px-4 py-2 font-semibold leading-6 text-sm shadow rounded-md text-blue-600 bg-white">
|
|
<i class="fas fa-spinner fa-spin mr-2"></i>
|
|
시스템 상태 확인 중...
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 이미 설정된 시스템 -->
|
|
<div x-show="!loading && !setupRequired" class="bg-white rounded-lg shadow-md p-8 text-center">
|
|
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i class="fas fa-check-circle text-green-600 text-2xl"></i>
|
|
</div>
|
|
<h3 class="text-xl font-semibold text-gray-900 mb-2">시스템이 이미 설정되었습니다</h3>
|
|
<p class="text-gray-600 mb-6">Document Server가 정상적으로 구성되어 있습니다.</p>
|
|
<a href="index.html" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
<i class="fas fa-home mr-2"></i>메인 페이지로 이동
|
|
</a>
|
|
</div>
|
|
|
|
<!-- 초기 설정 폼 -->
|
|
<div x-show="!loading && setupRequired" class="bg-white rounded-lg shadow-md p-8">
|
|
<div class="mb-6">
|
|
<h3 class="text-xl font-semibold text-gray-900 mb-2">관리자 계정 생성</h3>
|
|
<p class="text-gray-600">시스템 관리자(Root) 계정을 생성해주세요.</p>
|
|
</div>
|
|
|
|
<!-- 알림 메시지 -->
|
|
<div x-show="notification.show" x-transition class="mb-6 p-4 rounded-lg" :class="notification.type === 'success' ? 'bg-green-50 text-green-800 border border-green-200' : 'bg-red-50 text-red-800 border border-red-200'">
|
|
<div class="flex items-center">
|
|
<i :class="notification.type === 'success' ? 'fas fa-check-circle text-green-500' : 'fas fa-exclamation-circle text-red-500'" class="mr-2"></i>
|
|
<span x-text="notification.message"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<form @submit.prevent="initializeSystem()" class="space-y-6">
|
|
<div>
|
|
<label for="admin_email" class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-envelope mr-1"></i>관리자 이메일
|
|
</label>
|
|
<input type="email" id="admin_email" x-model="setupForm.admin_email" required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="admin@example.com">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="admin_password" class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-lock mr-1"></i>관리자 비밀번호
|
|
</label>
|
|
<input type="password" id="admin_password" x-model="setupForm.admin_password" required minlength="6"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="최소 6자 이상">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="confirm_password" class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-lock mr-1"></i>비밀번호 확인
|
|
</label>
|
|
<input type="password" id="confirm_password" x-model="confirmPassword" required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="비밀번호를 다시 입력하세요">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="admin_full_name" class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-user mr-1"></i>관리자 이름 (선택사항)
|
|
</label>
|
|
<input type="text" id="admin_full_name" x-model="setupForm.admin_full_name"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="시스템 관리자">
|
|
</div>
|
|
|
|
<!-- 주의사항 -->
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
|
<div class="flex">
|
|
<i class="fas fa-exclamation-triangle text-yellow-600 mt-0.5 mr-2"></i>
|
|
<div class="text-sm text-yellow-800">
|
|
<p class="font-medium mb-1">주의사항:</p>
|
|
<ul class="list-disc list-inside space-y-1">
|
|
<li>이 계정은 시스템의 최고 관리자 권한을 가집니다.</li>
|
|
<li>안전한 비밀번호를 사용하고 잘 보관해주세요.</li>
|
|
<li>설정 완료 후에는 이 페이지에 다시 접근할 수 없습니다.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" :disabled="setupLoading || setupForm.admin_password !== confirmPassword"
|
|
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed">
|
|
<i class="fas fa-spinner fa-spin mr-2" x-show="setupLoading"></i>
|
|
<i class="fas fa-rocket mr-2" x-show="!setupLoading"></i>
|
|
<span x-text="setupLoading ? '설정 중...' : '시스템 초기화'"></span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 설정 완료 -->
|
|
<div x-show="setupComplete" class="bg-white rounded-lg shadow-md p-8 text-center">
|
|
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i class="fas fa-check-circle text-green-600 text-2xl"></i>
|
|
</div>
|
|
<h3 class="text-xl font-semibold text-gray-900 mb-2">설정이 완료되었습니다!</h3>
|
|
<p class="text-gray-600 mb-6">Document Server가 성공적으로 초기화되었습니다.</p>
|
|
|
|
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
|
<div class="text-sm text-blue-800">
|
|
<p class="font-medium mb-2">생성된 관리자 계정:</p>
|
|
<p><strong>이메일:</strong> <span x-text="createdAdmin.email"></span></p>
|
|
<p><strong>이름:</strong> <span x-text="createdAdmin.full_name"></span></p>
|
|
<p><strong>역할:</strong> 시스템 관리자</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<a href="index.html" class="w-full inline-flex justify-center items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
<i class="fas fa-home mr-2"></i>메인 페이지로 이동
|
|
</a>
|
|
<button @click="goToLogin()" class="w-full inline-flex justify-center items-center px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
|
|
<i class="fas fa-sign-in-alt mr-2"></i>로그인하기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API 스크립트 -->
|
|
<script>
|
|
// 간단한 API 클라이언트
|
|
const setupApi = {
|
|
async get(endpoint) {
|
|
const response = await fetch(`/api${endpoint}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
return await response.json();
|
|
},
|
|
|
|
async post(endpoint, data) {
|
|
const response = await fetch(`/api${endpoint}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return await response.json();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<!-- 설정 앱 스크립트 -->
|
|
<script>
|
|
function setupApp() {
|
|
return {
|
|
loading: true,
|
|
setupRequired: false,
|
|
setupComplete: false,
|
|
setupLoading: false,
|
|
confirmPassword: '',
|
|
createdAdmin: {},
|
|
|
|
setupForm: {
|
|
admin_email: '',
|
|
admin_password: '',
|
|
admin_full_name: ''
|
|
},
|
|
|
|
notification: {
|
|
show: false,
|
|
type: 'success',
|
|
message: ''
|
|
},
|
|
|
|
async init() {
|
|
console.log('🔧 설정 앱 초기화');
|
|
await this.checkSetupStatus();
|
|
},
|
|
|
|
async checkSetupStatus() {
|
|
try {
|
|
const status = await setupApi.get('/setup/status');
|
|
this.setupRequired = status.is_setup_required;
|
|
|
|
console.log('✅ 설정 상태 확인 완료:', status);
|
|
} catch (error) {
|
|
console.error('❌ 설정 상태 확인 실패:', error);
|
|
this.showNotification('시스템 상태를 확인할 수 없습니다.', 'error');
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async initializeSystem() {
|
|
if (this.setupForm.admin_password !== this.confirmPassword) {
|
|
this.showNotification('비밀번호가 일치하지 않습니다.', 'error');
|
|
return;
|
|
}
|
|
|
|
this.setupLoading = true;
|
|
try {
|
|
const result = await setupApi.post('/setup/initialize', this.setupForm);
|
|
|
|
this.createdAdmin = result.admin_user;
|
|
this.setupComplete = true;
|
|
this.setupRequired = false;
|
|
|
|
console.log('✅ 시스템 초기화 완료:', result);
|
|
} catch (error) {
|
|
console.error('❌ 시스템 초기화 실패:', error);
|
|
this.showNotification(error.message || '시스템 초기화에 실패했습니다.', 'error');
|
|
} finally {
|
|
this.setupLoading = false;
|
|
}
|
|
},
|
|
|
|
goToLogin() {
|
|
// 로그인 모달을 열거나 로그인 페이지로 이동
|
|
window.location.href = 'index.html';
|
|
},
|
|
|
|
showNotification(message, type = 'success') {
|
|
this.notification = {
|
|
show: true,
|
|
type,
|
|
message
|
|
};
|
|
|
|
setTimeout(() => {
|
|
this.notification.show = false;
|
|
}, 5000);
|
|
}
|
|
};
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|