340 lines
9.1 KiB
Go
340 lines
9.1 KiB
Go
package handlers
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"pocketbaseSigner/config"
|
||
"pocketbaseSigner/models"
|
||
"time"
|
||
)
|
||
|
||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodGet {
|
||
http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// Получаем токен из cookie
|
||
cookie, err := r.Cookie("pb_auth")
|
||
if err != nil {
|
||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||
return
|
||
}
|
||
|
||
// Получаем ID пользователя из cookie
|
||
userIdCookie, err := r.Cookie("user_id")
|
||
if err != nil {
|
||
log.Printf("Ошибка при получении ID пользователя: %v", err)
|
||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||
return
|
||
}
|
||
|
||
// Получаем данные пользователя
|
||
url := config.PocketBaseURL + "/api/collections/users/records/" + userIdCookie.Value
|
||
req, err := http.NewRequest("GET", url, nil)
|
||
if err != nil {
|
||
log.Printf("Ошибка при создании запроса: %v", err)
|
||
http.Error(w, "Внутренняя ошибка сервера", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
req.Header.Set("Authorization", cookie.Value)
|
||
client := &http.Client{}
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
log.Printf("Ошибка при получении данных пользователя: %v", err)
|
||
http.Error(w, "Ошибка при получении данных", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
log.Printf("Ошибка при чтении ответа: %v", err)
|
||
http.Error(w, "Ошибка при обработке данных", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Проверяем статус ответа
|
||
if resp.StatusCode != http.StatusOK {
|
||
log.Printf("Ошибка при получении данных пользователя: %s", string(body))
|
||
http.Error(w, "Ошибка при получении данных пользователя", resp.StatusCode)
|
||
return
|
||
}
|
||
|
||
var userData models.UserData
|
||
if err := json.Unmarshal(body, &userData); err != nil {
|
||
log.Printf("Ошибка при разборе JSON: %v", err)
|
||
http.Error(w, "Ошибка при обработке данных", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Отображаем dashboard
|
||
w.Header().Set("Content-Type", "text/html")
|
||
dashboardHTML := `
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Личный кабинет</title>
|
||
<style>
|
||
body {
|
||
font-family: Arial, sans-serif;
|
||
background: #f4f4f4;
|
||
margin: 0;
|
||
padding: 20px;
|
||
}
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
.user-info {
|
||
margin-bottom: 20px;
|
||
}
|
||
.logout-btn {
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
}
|
||
.logout-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
.files-section {
|
||
margin-top: 30px;
|
||
}
|
||
.files-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
margin-top: 20px;
|
||
}
|
||
.file-card {
|
||
background: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
cursor: pointer;
|
||
transition: transform 0.2s;
|
||
}
|
||
.file-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||
}
|
||
.file-name {
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
word-break: break-word;
|
||
}
|
||
.file-date {
|
||
color: #6c757d;
|
||
font-size: 0.9em;
|
||
}
|
||
.pdf-viewer {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0,0,0,0.8);
|
||
display: none;
|
||
z-index: 1000;
|
||
}
|
||
.pdf-viewer iframe {
|
||
width: 90%;
|
||
height: 90%;
|
||
margin: 2% auto;
|
||
display: block;
|
||
background: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
}
|
||
.close-viewer {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.upload-section {
|
||
margin-top: 20px;
|
||
padding: 20px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
}
|
||
.upload-btn {
|
||
background: #28a745;
|
||
color: white;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
}
|
||
.upload-btn:hover {
|
||
background: #218838;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>Личный кабинет</h1>
|
||
<form action="/logout" method="POST">
|
||
<button type="submit" class="logout-btn">Выйти</button>
|
||
</form>
|
||
</div>
|
||
<div class="user-info">
|
||
<h2>Информация о пользователе</h2>
|
||
<p><strong>Email:</strong> ` + userData.Email + `</p>
|
||
<p><strong>Имя:</strong> ` + userData.FirstName + `</p>
|
||
<p><strong>Фамилия:</strong> ` + userData.LastName + `</p>
|
||
<p><strong>Телефон:</strong> ` + userData.Phone + `</p>
|
||
</div>
|
||
|
||
<div class="files-section">
|
||
<h2>Мои документы</h2>
|
||
<div class="upload-section">
|
||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||
<input type="file" name="files" accept=".pdf" required>
|
||
<button type="submit" class="upload-btn">Загрузить PDF</button>
|
||
</form>
|
||
</div>
|
||
<div class="files-grid">
|
||
` + generateFilesGridFromNames(userData.Files, userIdCookie.Value, cookie.Value) + `
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pdf-viewer" id="pdfViewer">
|
||
<button class="close-viewer" onclick="closePdfViewer()">×</button>
|
||
<iframe id="pdfFrame" src=""></iframe>
|
||
</div>
|
||
|
||
<script>
|
||
function openPdfViewer(url) {
|
||
document.getElementById('pdfFrame').src = url;
|
||
document.getElementById('pdfViewer').style.display = 'block';
|
||
}
|
||
|
||
function closePdfViewer() {
|
||
document.getElementById('pdfViewer').style.display = 'none';
|
||
document.getElementById('pdfFrame').src = '';
|
||
}
|
||
|
||
// Закрытие по клику вне iframe
|
||
document.getElementById('pdfViewer').addEventListener('click', function(e) {
|
||
if (e.target === this) {
|
||
closePdfViewer();
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|
||
`
|
||
w.Write([]byte(dashboardHTML))
|
||
}
|
||
|
||
func getFileToken(cookie string) (string, error) {
|
||
url := config.PocketBaseURL + "/api/files/token"
|
||
req, err := http.NewRequest("POST", url, nil)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
req.Header.Set("Authorization", cookie)
|
||
client := &http.Client{}
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", fmt.Errorf("ошибка получения токена: %d", resp.StatusCode)
|
||
}
|
||
|
||
var result map[string]string
|
||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return result["token"], nil
|
||
}
|
||
|
||
func generateFilesGridFromNames(files []string, userId string, authCookie string) string {
|
||
if len(files) == 0 {
|
||
return `<div class="no-files">У вас пока нет загруженных документов</div>`
|
||
}
|
||
|
||
// Получаем токен для доступа к файлам
|
||
token, err := getFileToken(authCookie)
|
||
if err != nil {
|
||
log.Printf("Ошибка при получении токена файла: %v", err)
|
||
return `<div class="error">Ошибка при получении доступа к файлам</div>`
|
||
}
|
||
|
||
var html string
|
||
for _, fileName := range files {
|
||
// Формируем URL для просмотра файла с токеном
|
||
fileUrl := config.PocketBaseURL + "/api/files/_pb_users_auth_/" + userId + "/" + fileName + "?token=" + token
|
||
html += `
|
||
<div class="file-card" onclick="openPdfViewer('` + fileUrl + `')">
|
||
<div class="file-icon">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||
<polyline points="14 2 14 8 20 8"></polyline>
|
||
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||
<polyline points="10 9 9 9 8 9"></polyline>
|
||
</svg>
|
||
</div>
|
||
<div class="file-info">
|
||
<div class="file-name">` + fileName + `</div>
|
||
</div>
|
||
</div>
|
||
`
|
||
}
|
||
return html
|
||
}
|
||
|
||
func formatDate(dateStr string) string {
|
||
// Парсим дату из формата ISO 8601
|
||
t, err := time.Parse(time.RFC3339, dateStr)
|
||
if err != nil {
|
||
return dateStr
|
||
}
|
||
// Форматируем в удобный для чтения формат
|
||
return t.Format("02.01.2006 15:04")
|
||
}
|
||
|
||
func formatVerified(verified bool) string {
|
||
if verified {
|
||
return "Подтвержден"
|
||
}
|
||
return "Не подтвержден"
|
||
}
|