Files
document-server/frontend/setup.html
Hyungi Ahn 6e01dbdeb3 Fix: 업로드 및 API 연결 문제 해결
- FastAPI 라우터에서 슬래시 문제로 인한 307 리다이렉트 수정
- Nginx 프록시 설정에서 경로 중복 문제 해결
- 계정 관리 시스템 구현 (로그인, 사용자 관리, 권한 설정)
- 노트북 연결 기능 수정 (notebook_id 필드 추가)
- 메모 트리 UI 개선 (수평 레이아웃, 드래그 기능 제거)
- 헤더 UI 개선 및 고정 위치 설정
- 백업/복원 스크립트 추가
- PDF 미리보기 토큰 인증 지원
2025-09-03 15:58:10 +09:00

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>