pocketbase-signer/handlers/dashboard.go

340 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 "Не подтвержден"
}