Инициализация проекта

Всем привет :)
This commit is contained in:
2025-10-16 16:27:36 +07:00
commit 0e93af1d8c
23 changed files with 4058 additions and 0 deletions

548
internal/server/handlers.go Normal file
View File

@@ -0,0 +1,548 @@
package server
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
"strconv"
"strings"
"wg-panel/internal/database"
"wg-panel/internal/wireguard"
)
var (
DB *database.Database
Config *database.Config
)
// authMiddleware проверяет авторизацию
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Проверяем cookie
cookie, err := r.Cookie("auth")
if err != nil || cookie.Value != "authenticated" {
if r.URL.Path == "/" {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
// handleLogin обрабатывает страницу входа
func HandleLogin(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
tmplData, err := TemplatesFS.ReadFile("templates/login.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.New("login").Parse(string(tmplData))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl.Execute(w, nil)
return
}
if r.Method == "POST" {
username := r.FormValue("username")
password := r.FormValue("password")
if username == Config.Username && password == Config.Password {
http.SetCookie(w, &http.Cookie{
Name: "auth",
Value: "authenticated",
Path: "/",
MaxAge: 86400, // 24 часа
HttpOnly: true,
})
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/login?error=1", http.StatusSeeOther)
}
}
// handleLogout обрабатывает выход
func HandleLogout(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: "auth",
Value: "",
Path: "/",
MaxAge: -1,
HttpOnly: true,
})
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
// handleIndex обрабатывает главную страницу
func HandleIndex(w http.ResponseWriter, r *http.Request) {
tmplData, err := TemplatesFS.ReadFile("templates/index.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.New("index").Parse(string(tmplData))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl.Execute(w, nil)
}
// === ОБРАБОТЧИКИ СЕРВЕРОВ ===
// HandleServers возвращает список серверов
func HandleServers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if DB.Servers == nil {
json.NewEncoder(w).Encode([]database.Server{})
} else {
json.NewEncoder(w).Encode(DB.Servers)
}
}
// HandleCreateServer создает новый WireGuard сервер
func HandleCreateServer(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
name := r.FormValue("name")
address := r.FormValue("address")
portStr := r.FormValue("port")
dns := r.FormValue("dns")
if name == "" || address == "" || portStr == "" {
http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
// Добавляем /24 если не указано
if !strings.Contains(address, "/") {
address = address + "/24"
}
port, err := strconv.Atoi(portStr)
if err != nil {
http.Error(w, "Invalid port", http.StatusBadRequest)
return
}
// Валидируем конфигурацию
if err := database.ValidateServerConfig(DB, address, port); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
server, err := wireguard.CreateServer(DB, name, address, port, dns)
if err != nil {
http.Error(w, "Failed to create server: "+err.Error(), http.StatusInternalServerError)
return
}
DB.Servers = append(DB.Servers, *server)
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(server)
}
// HandleUpdateServer обновляет настройки сервера
func HandleUpdateServer(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
name := r.FormValue("name")
portStr := r.FormValue("port")
dns := r.FormValue("dns")
for i, server := range DB.Servers {
if server.ID == id {
if name != "" {
DB.Servers[i].Name = name
}
if portStr != "" {
port, err := strconv.Atoi(portStr)
if err == nil {
DB.Servers[i].ListenPort = port
}
}
if dns != "" {
DB.Servers[i].DNS = dns
}
// Обновляем конфиг файл
wireguard.UpdateServerConfig(&DB.Servers[i], DB)
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Servers[i])
return
}
}
http.Error(w, "Server not found", http.StatusNotFound)
}
// HandleDeleteServer удаляет сервер
func HandleDeleteServer(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
for i, server := range DB.Servers {
if server.ID == id {
// Удаляем сервер
if err := wireguard.DeleteServer(&server); err != nil {
http.Error(w, "Failed to delete server", http.StatusInternalServerError)
return
}
// Удаляем всех клиентов этого сервера
var newClients []database.Client
for _, client := range DB.Clients {
if client.ServerID != id {
newClients = append(newClients, client)
}
}
DB.Clients = newClients
// Удаляем сервер из базы
DB.Servers = append(DB.Servers[:i], DB.Servers[i+1:]...)
database.SaveDatabase(DB)
w.WriteHeader(http.StatusOK)
return
}
}
http.Error(w, "Server not found", http.StatusNotFound)
}
// HandleToggleServer включает/выключает сервер
func HandleToggleServer(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
for i, server := range DB.Servers {
if server.ID == id {
if err := wireguard.ToggleServer(&DB.Servers[i]); err != nil {
http.Error(w, "Failed to toggle server: "+err.Error(), http.StatusInternalServerError)
return
}
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Servers[i])
return
}
}
http.Error(w, "Server not found", http.StatusNotFound)
}
// === ОБРАБОТЧИКИ КЛИЕНТОВ ===
// HandleClients возвращает список клиентов
func HandleClients(w http.ResponseWriter, r *http.Request) {
serverID := r.URL.Query().Get("server_id")
var clients []database.Client
for _, client := range DB.Clients {
if serverID == "" || client.ServerID == serverID {
clients = append(clients, client)
}
}
w.Header().Set("Content-Type", "application/json")
if clients == nil {
json.NewEncoder(w).Encode([]database.Client{})
} else {
json.NewEncoder(w).Encode(clients)
}
}
// HandleCreateClient создает новый конфиг клиента
func HandleCreateClient(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
serverID := r.FormValue("server_id")
name := r.FormValue("name")
comment := r.FormValue("comment")
if serverID == "" || name == "" {
http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
client, err := wireguard.CreateClient(DB, serverID, name, comment)
if err != nil {
http.Error(w, "Failed to create client: "+err.Error(), http.StatusInternalServerError)
return
}
DB.Clients = append(DB.Clients, *client)
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(client)
}
// HandleDeleteClient удаляет клиента
func HandleDeleteClient(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
for i, client := range DB.Clients {
if client.ID == id {
// Удаляем клиента
if err := wireguard.DeleteClient(DB, &client); err != nil {
http.Error(w, "Failed to delete client", http.StatusInternalServerError)
return
}
// Удаляем из базы
DB.Clients = append(DB.Clients[:i], DB.Clients[i+1:]...)
database.SaveDatabase(DB)
w.WriteHeader(http.StatusOK)
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleToggleClient включает/выключает клиента
func HandleToggleClient(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
for i, client := range DB.Clients {
if client.ID == id {
if err := wireguard.ToggleClient(DB, &DB.Clients[i]); err != nil {
http.Error(w, "Failed to toggle client", http.StatusInternalServerError)
return
}
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Clients[i])
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleUpdateClient обновляет имя и комментарий клиента
func HandleUpdateClient(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := r.FormValue("id")
name := r.FormValue("name")
comment := r.FormValue("comment")
for i, client := range DB.Clients {
if client.ID == id {
if name != "" {
DB.Clients[i].Name = name
}
DB.Clients[i].Comment = comment
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Clients[i])
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleAddPortForward добавляет проброс порта
func HandleAddPortForward(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
clientID := r.FormValue("client_id")
portStr := r.FormValue("port")
protocol := r.FormValue("protocol")
description := r.FormValue("description")
port, err := strconv.Atoi(portStr)
if err != nil {
http.Error(w, "Invalid port", http.StatusBadRequest)
return
}
if protocol != "tcp" && protocol != "udp" && protocol != "both" {
http.Error(w, "Protocol must be tcp, udp or both", http.StatusBadRequest)
return
}
for i, client := range DB.Clients {
if client.ID == clientID {
if err := wireguard.AddPortForward(DB, &DB.Clients[i], port, protocol, description); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Clients[i])
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleRemovePortForward удаляет проброс порта
func HandleRemovePortForward(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
clientID := r.FormValue("client_id")
portStr := r.FormValue("port")
protocol := r.FormValue("protocol")
port, err := strconv.Atoi(portStr)
if err != nil {
http.Error(w, "Invalid port", http.StatusBadRequest)
return
}
for i, client := range DB.Clients {
if client.ID == clientID {
if err := wireguard.RemovePortForward(&DB.Clients[i], port, protocol); err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
database.SaveDatabase(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Clients[i])
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleDownloadConfig скачивает конфиг клиента
func HandleDownloadConfig(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
for _, client := range DB.Clients {
if client.ID == id {
// Находим сервер
var server *database.Server
for i := range DB.Servers {
if DB.Servers[i].ID == client.ServerID {
server = &DB.Servers[i]
break
}
}
if server == nil {
http.Error(w, "Server not found", http.StatusNotFound)
return
}
config := wireguard.GenerateClientConfig(client, server)
// Создаем безопасное имя файла (без пробелов и спецсимволов)
safeName := database.SanitizeFilename(client.Name)
w.Header().Set("Content-Type", "application/x-wireguard-profile")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.conf", safeName))
w.Write([]byte(config))
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleQRCode генерирует QR код для конфига
func HandleQRCode(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
for _, client := range DB.Clients {
if client.ID == id {
// Находим сервер
var server *database.Server
for i := range DB.Servers {
if DB.Servers[i].ID == client.ServerID {
server = &DB.Servers[i]
break
}
}
if server == nil {
http.Error(w, "Server not found", http.StatusNotFound)
return
}
config := wireguard.GenerateClientConfig(client, server)
// Генерируем QR код
png, err := wireguard.GenerateQRCode(config)
if err != nil {
http.Error(w, "Failed to generate QR code", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "image/png")
w.Write(png)
return
}
}
http.Error(w, "Client not found", http.StatusNotFound)
}
// HandleStats возвращает статистику
func HandleStats(w http.ResponseWriter, r *http.Request) {
wireguard.UpdateStats(DB)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(DB.Clients)
}