Дашборд доступно добавление файлов
This commit is contained in:
parent
76a770add8
commit
11431a256f
@ -13,6 +13,7 @@ func main() {
|
||||
|
||||
http.HandleFunc("/dashboard", handlers.AuthMiddleware(handlers.DashboardHandler))
|
||||
|
||||
http.HandleFunc("/upload", handlers.AuthMiddleware(handlers.UploadHandler))
|
||||
// Запуск сервера
|
||||
log.Println("Клиент запущен на :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
|
22
config/config.go
Normal file
22
config/config.go
Normal file
@ -0,0 +1,22 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
// PocketBaseURL URL сервера PocketBase
|
||||
PocketBaseURL string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Получаем URL из переменной окружения или используем значение по умолчанию
|
||||
PocketBaseURL = os.Getenv("POCKETBASE_URL")
|
||||
if PocketBaseURL == "" {
|
||||
PocketBaseURL = "http://localhost:8090"
|
||||
log.Printf("POCKETBASE_URL не установлен, используем значение по умолчанию: %s", PocketBaseURL)
|
||||
} else {
|
||||
log.Printf("Используем POCKETBASE_URL из переменной окружения: %s", PocketBaseURL)
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
@ -14,3 +13,5 @@ services:
|
||||
- server
|
||||
ports:
|
||||
- "8080:8080" # Укажите порт клиента
|
||||
environment:
|
||||
- POCKETBASE_URL=http://server:8090
|
||||
|
@ -2,13 +2,15 @@ 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)
|
||||
@ -31,7 +33,7 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Получаем данные пользователя
|
||||
url := "http://localhost:8090/api/collections/users/records/" + userIdCookie.Value
|
||||
url := config.PocketBaseURL + "/api/collections/users/records/" + userIdCookie.Value
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса: %v", err)
|
||||
@ -87,7 +89,7 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
@ -114,6 +116,87 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
.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>
|
||||
@ -130,17 +213,124 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
<p><strong>Имя:</strong> ` + userData.FirstName + `</p>
|
||||
<p><strong>Фамилия:</strong> ` + userData.LastName + `</p>
|
||||
<p><strong>Телефон:</strong> ` + userData.Phone + `</p>
|
||||
<p><strong>Дата регистрации:</strong> ` + userData.Created + `</p>
|
||||
<p><strong>Последнее обновление:</strong> ` + userData.Updated + `</p>
|
||||
<p><strong>Статус верификации:</strong> ` + formatVerified(userData.Verified) + `</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 "Подтвержден"
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"pocketbaseSigner/config"
|
||||
"pocketbaseSigner/models"
|
||||
)
|
||||
|
||||
@ -13,7 +14,6 @@ import (
|
||||
//2. реализовать логин
|
||||
//3. приступить к миддлвейру
|
||||
|
||||
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
http.ServeFile(w, r, "./web/login_form.html")
|
||||
@ -27,28 +27,35 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.FormValue("email") == "" || r.FormValue("password") == "" {
|
||||
user := models.LoginForm{
|
||||
Email: r.FormValue("email"),
|
||||
Password: r.FormValue("password"),
|
||||
}
|
||||
|
||||
// Проверяем обязательные поля
|
||||
if user.Email == "" || user.Password == "" {
|
||||
http.Error(w, "Email и пароль обязательны!", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Создаем структуру для запроса
|
||||
loginData := map[string]string{
|
||||
"identity": r.FormValue("email"),
|
||||
"password": r.FormValue("password"),
|
||||
// Подготавливаем данные для отправки
|
||||
dataMap := map[string]string{
|
||||
"identity": user.Email,
|
||||
"password": user.Password,
|
||||
}
|
||||
|
||||
// Преобразуем в JSON
|
||||
jsonData, err := json.Marshal(loginData)
|
||||
data, err := json.Marshal(dataMap)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании JSON: %v", err)
|
||||
http.Error(w, "Внутренняя ошибка сервера", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
url := "http://localhost:8090/api/collections/users/auth-with-password"
|
||||
log.Printf("Отправляем данные пользователя: %+v\n", user)
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
||||
url := config.PocketBaseURL + "/api/collections/users/auth-with-password"
|
||||
log.Printf("URL: %v\n", url)
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса: %v", err)
|
||||
http.Error(w, "Внутренняя ошибка сервера", http.StatusInternalServerError)
|
||||
|
@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"pocketbaseSigner/config"
|
||||
)
|
||||
|
||||
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
@ -16,7 +17,8 @@ func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// Проверяем валидность токена через PocketBase
|
||||
req, err := http.NewRequest("POST", "http://localhost:8090/api/collections/users/auth-refresh", nil)
|
||||
url := config.PocketBaseURL + "/api/collections/users/auth-refresh"
|
||||
req, err := http.NewRequest("POST", url, nil)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса: %v", err)
|
||||
http.Error(w, "Внутренняя ошибка сервера", http.StatusInternalServerError)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"pocketbaseSigner/config"
|
||||
"pocketbaseSigner/models"
|
||||
)
|
||||
|
||||
@ -61,7 +62,7 @@ func RegisterFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.Printf("Отправляем данные пользователя: %+v\n", user)
|
||||
|
||||
url := "http://localhost:8090/api/collections/users/records"
|
||||
url := config.PocketBaseURL + "/api/collections/users/records"
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса: %v", err)
|
||||
|
171
handlers/upload.go
Normal file
171
handlers/upload.go
Normal file
@ -0,0 +1,171 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"pocketbaseSigner/config"
|
||||
"pocketbaseSigner/models"
|
||||
)
|
||||
|
||||
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
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
|
||||
}
|
||||
|
||||
var userData models.UserData
|
||||
if err := json.Unmarshal(body, &userData); err != nil {
|
||||
log.Printf("Ошибка при разборе JSON: %v", err)
|
||||
http.Error(w, "Ошибка при обработке данных", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Получаем файл из формы
|
||||
file, header, err := r.FormFile("files")
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при получении файла: %v", err)
|
||||
http.Error(w, "Ошибка при загрузке файла", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Создаем multipart writer для отправки файла
|
||||
requestBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(requestBody)
|
||||
|
||||
// Добавляем файл
|
||||
part, err := writer.CreateFormFile("files", header.Filename)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании формы: %v", err)
|
||||
http.Error(w, "Ошибка при подготовке файла", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Копируем содержимое файла
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при копировании файла: %v", err)
|
||||
http.Error(w, "Ошибка при обработке файла", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Добавляем существующие файлы
|
||||
for _, existingFile := range userData.Files {
|
||||
part, err := writer.CreateFormFile("files", existingFile)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при добавлении существующего файла: %v", err)
|
||||
continue
|
||||
}
|
||||
// Получаем содержимое существующего файла
|
||||
fileUrl := config.PocketBaseURL + "/api/files/_pb_users_auth_/" + userIdCookie.Value + "/" + existingFile
|
||||
fileReq, err := http.NewRequest("GET", fileUrl, nil)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса для существующего файла: %v", err)
|
||||
continue
|
||||
}
|
||||
fileReq.Header.Set("Authorization", cookie.Value)
|
||||
fileResp, err := client.Do(fileReq)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при получении существующего файла: %v", err)
|
||||
continue
|
||||
}
|
||||
_, err = io.Copy(part, fileResp.Body)
|
||||
fileResp.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при копировании существующего файла: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Закрываем writer
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при закрытии writer: %v", err)
|
||||
http.Error(w, "Ошибка при подготовке запроса", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Создаем запрос к PocketBase
|
||||
url = config.PocketBaseURL + "/api/collections/users/records/" + userIdCookie.Value
|
||||
log.Printf("Отправка запроса на URL: %s", url)
|
||||
log.Printf("Content-Type: %s", writer.FormDataContentType())
|
||||
|
||||
req, err = http.NewRequest("PATCH", url, requestBody)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при создании запроса: %v", err)
|
||||
http.Error(w, "Ошибка при отправке файла", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Устанавливаем заголовки
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Authorization", cookie.Value)
|
||||
|
||||
// Отправляем запрос
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка при отправке запроса: %v", err)
|
||||
http.Error(w, "Ошибка при загрузке файла", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Читаем ответ
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
log.Printf("Ответ сервера (статус %d): %s", resp.StatusCode, string(respBody))
|
||||
|
||||
// Проверяем ответ
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("Ошибка при загрузке файла (статус %d): %s", resp.StatusCode, string(respBody))
|
||||
http.Error(w, "Ошибка при загрузке файла", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Перенаправляем обратно на дашборд
|
||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||
}
|
19
models/files.go
Normal file
19
models/files.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
// File представляет информацию о файле
|
||||
type File struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
// FilesList представляет список файлов
|
||||
type FilesList struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
Total int `json:"total"`
|
||||
Items []File `json:"items"`
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package models
|
||||
|
||||
|
||||
type UserResponse struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
@ -8,17 +7,19 @@ type UserResponse struct {
|
||||
LastName string `json:"last_name"`
|
||||
}
|
||||
|
||||
// UserData представляет данные пользователя
|
||||
type UserData struct {
|
||||
CollectionId string `json:"collectionId"`
|
||||
CollectionName string `json:"collectionName"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"FirstName"`
|
||||
LastName string `json:"LastName"`
|
||||
Phone string `json:"Phone"`
|
||||
EmailVisibility bool `json:"emailVisibility"`
|
||||
Verified bool `json:"verified"`
|
||||
Avatar string `json:"avatar"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
}
|
||||
CollectionId string `json:"collectionId"`
|
||||
CollectionName string `json:"collectionName"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
PasswordConfirm string `json:"passwordConfirm"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Phone string `json:"phone"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
Verified bool `json:"verified"`
|
||||
Files []string `json:"files"`
|
||||
}
|
||||
|
Binary file not shown.
BIN
pb_data/data.db
BIN
pb_data/data.db
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"application/pdf","user.metadata":{"original-filename":"design_db_pwfofsm6bc.pdf"},"md5":"EGV9XOunjL+DZzoKvFJqHw=="}
|
@ -57,6 +57,19 @@
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
.register-link {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.register-link a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.register-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -78,6 +91,9 @@
|
||||
|
||||
<button type="submit">войти</button>
|
||||
</form>
|
||||
<div class="register-link">
|
||||
<p>Нет аккаунта? <a href="/register">Зарегистрироваться</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
Loading…
Reference in New Issue
Block a user