Files
wgServer/internal/server/handlers.go
2025-10-16 16:27:36 +07:00

549 lines
14 KiB
Go
Raw Permalink 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 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)
}