The session is not sent from the server

then upon authorization, we receive a session and go through authorization. If I use ip 192.168.0.100:8080 both when creating the server and when redirecting, then we receive user data, but we do not send anything in response. I mean something related to /profile. It’s as if it can’t handle IP requests.

main.go

package main

import (
    global "DiscordBotGo/Global"
    "DiscordBotGo/auth" // Добавлен пакет авторизации
    "DiscordBotGo/coins"
    "DiscordBotGo/utils"
    "fmt"
    "log"
    "net/http"

    "github.com/bwmarrin/discordgo"
    "github.com/gorilla/mux"
)

var serverip = "localhost:8080"

func main() {

    // Проверяем инициализацию сессии перед запуском
    if global.BotSession == nil {
        var err error
        global.BotSession, err = discordgo.New("Bot тут мой токен")
        if err != nil {
            log.Fatal("Ошибка при создании сессии Discord:", err)
        }
    }

    // Обработчик для статических файлов
    http.FileServer(http.Dir("./"))                                                               // Указываем корневую директорию проекта
    http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./css"))))           // Разрешаем загрузку файлов из папки /css                                         // Разрешаем загрузку файлов из папки /js
    http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets/")))) // Для изображений
    http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./js/"))))

    // Открываем сессию
    err := global.BotSession.Open()
    if err != nil {
        fmt.Println("Ошибка при подключении к Discord:", err)
        return
    }
    defer global.BotSession.Close()

    coins.StartAutoReward(global.BotSession)

    // Добавляем маршруты для авторизации
    http.HandleFunc("/login", auth.LoginHandler)
    http.HandleFunc("/callback", auth.CallbackHandler)
    http.HandleFunc("/profile", auth.ProfileHandler)
    http.HandleFunc("/logout", auth.LogoutHandler)
    http.HandleFunc("/distribute", utils.DistributeTeams)
    http.HandleFunc("/create-product", utils.CreateProductHandler)
    http.HandleFunc("/api/products/{id}", utils.GetProductByIDHandler)
    http.HandleFunc("/check-auth", auth.CheckAuthHandler)
    http.HandleFunc("/api/products/", utils.GetProductByIDHandler)
    http.HandleFunc("/check-role-for-tournament", checkRoleForTournamentHandler)
    http.HandleFunc("/roulette-images/", utils.GetRouletteImagesHandler)

    // Обрабатываем POST запрос на /send-links
    http.HandleFunc("/send-links", utils.SendMatchLinks)
    http.HandleFunc("/buy-product", utils.BuyProductHandler)
    http.HandleFunc("/api/user-roles", utils.UserRolesHandler)
    http.HandleFunc("/profile-redirect", utils.ProfileRedirectHandler)
    http.HandleFunc("/api/products", utils.GetProductsByCategoryHandler)
    http.HandleFunc("/api/coins", utils.CoinsHandler)
    http.HandleFunc("/save-news", utils.SaveNewsHandler)
    http.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.Dir("images"))))
    http.HandleFunc("/get-news", utils.GetNewsHandler)
    http.HandleFunc("/delete-news", utils.DeleteNewsHandler)

    r := mux.NewRouter()
    r.HandleFunc("/api/products/{id}", utils.GetProductByIDHandler).Methods("GET")
    // Дальше идёт ваш HTTP-сервер
    http.HandleFunc("/", serveHTML)
    http.HandleFunc("/command", utils.HandleCommand)

    fmt.Println("Сервер запущен на", serverip)
    err = http.ListenAndServe(serverip, nil)
    if err != nil {
        fmt.Println("Ошибка при запуске HTTP-сервера:", err)
    }
}

func serveHTML(w http.ResponseWriter, r *http.Request) {
    // Если путь запроса "/" (корень), возвращаем index.html
    if r.URL.Path == "/" {
        http.ServeFile(w, r, "index.html")
        return
    }

    // Если запрашивается другой файл, пытаемся его найти
    http.FileServer(http.Dir("./")).ServeHTTP(w, r)
}

func checkRoleForTournamentHandler(w http.ResponseWriter, r *http.Request) {
    userID := auth.GetUserIdFromSessionOrHeader(r)
    if userID == "" {
        http.Error(w, "Пользователь не аутентифицирован", http.StatusUnauthorized)
        return
    }

    // Получаем роли пользователя из Discord
    userRoles := auth.GetUserRolesFromDiscord(userID)
    if userRoles == nil {
        http.Error(w, "Ошибка при получении ролей пользователя", http.StatusInternalServerError)
        return
    }

    // Определяем роли администраторов
    adminRoles := []string{"1318188643944501289", "1318188870415679508", "1318189013823389711"}

    // Проверяем, есть ли у пользователя одна из ролей админа
    isAdmin := false
    for _, role := range userRoles {
        for _, adminRole := range adminRoles {
            if role == adminRole {
                isAdmin = true
                break
            }
        }
        if isAdmin {
            break
        }
    }

    // Если пользователь является админом, предоставляем доступ
    if isAdmin {
        w.WriteHeader(http.StatusOK)
    } else {
        http.Error(w, "У вас нет прав доступа к этой странице", http.StatusForbidden)
    }
}

auth.go

package auth

import (
    global "DiscordBotGo/Global"
    "DiscordBotGo/db"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"

    "github.com/gorilla/sessions"
    "golang.org/x/oauth2"
)

// Определяем OAuth2 Endpoint для Discord
var discordEndpoint = oauth2.Endpoint{
    AuthURL:  "https://discord.com/api/oauth2/authorize",
    TokenURL: "https://discord.com/api/oauth2/token",
}

// Переменные для OAuth2
var (
    oauthConfig = &oauth2.Config{
        ClientID:     "тут мой клиент id",              // Замените на ваш Client ID
        ClientSecret: "тут секретный ключ", // Замените на ваш Client Secret
        RedirectURL:  "http://localhost/callback",        // URL перенаправления после авторизации
        Scopes:       []string{"identify"},               // Указываем необходимые права
        Endpoint:     discordEndpoint,                    // Используем Discord OAuth2 Endpoint
    }
)

var SessionStore = sessions.NewCookieStore([]byte("a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0")) // Хранилище сессий

// Инициализация базы данных
func init() {
    if err := db.InitDB(); err != nil {
        log.Fatalf("Ошибка при инициализации базы данных: %v", err)
    }
}

// LoginHandler — перенаправляет пользователя на Discord для авторизации
func LoginHandler(w http.ResponseWriter, r *http.Request) {
    url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline) // Генерируем URL для авторизации
    http.Redirect(w, r, url, http.StatusTemporaryRedirect)            // Перенаправляем на Discord
}

// CallbackHandler — обработчик для обработки ответа Discord
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
    code := r.URL.Query().Get("code")
    if code == "" {
        http.Error(w, "Ошибка: нет кода в запросе", http.StatusBadRequest)
        return
    }

    token, err := oauthConfig.Exchange(r.Context(), code)
    if err != nil {
        http.Error(w, "Не удалось получить токен: "+err.Error(), http.StatusInternalServerError)
        return
    }

    client := oauthConfig.Client(r.Context(), token)

    resp, err := client.Get("https://discord.com/api/users/@me")
    if err != nil {
        http.Error(w, "Ошибка получения данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }
    defer resp.Body.Close()

    var userData struct {
        ID            string `json:"id"`
        Username      string `json:"username"`
        Discriminator string `json:"discriminator"`
        Avatar        string `json:"avatar"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&userData); err != nil {
        http.Error(w, "Ошибка декодирования данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Генерация URL аватара
    var avatarURL string
    if userData.Avatar != "" {
        avatarURL = fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.png", userData.ID, userData.Avatar)
    } else {
        discriminator, err := strconv.Atoi(userData.Discriminator)
        if err != nil {
            http.Error(w, "Ошибка обработки данных пользователя: "+err.Error(), http.StatusInternalServerError)
            return
        }
        defaultAvatarIndex := discriminator % 5
        avatarURL = fmt.Sprintf("https://cdn.discordapp.com/embed/avatars/%d.png", defaultAvatarIndex)
    }

    // Логируем URL аватара для отладки
    log.Printf("Avatar URL: %s", avatarURL)

    // Сохраняем данные пользователя и аватар в сессии
    session, _ := SessionStore.Get(r, "discord-session")
    session.Values["userID"] = userData.ID
    session.Values["username"] = userData.Username
    session.Values["avatar"] = avatarURL
    err = session.Save(r, w)
    log.Printf("Session saved with userID: %s", userData.ID)
    if err != nil {
        log.Printf("Ошибка сохранения сессии: %v", err)
        http.Error(w, "Ошибка сохранения сессии", http.StatusInternalServerError)
        return
    }

    // Добавляем пользователя в базу данных
    err = db.AddUser(userData.ID, userData.Username)
    if err != nil {
        http.Error(w, "Ошибка при добавлении пользователя в базу данных: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Перенаправляем на страницу профиля
    http.Redirect(w, r, "/index.html", http.StatusSeeOther)

}

func AddRoleToUser(userID string, roleID int64) error {
    guildID := global.ServerID // Замените на ID вашего сервера

    session := global.BotSession
    if session == nil {
        return fmt.Errorf("сессия бота не инициализирована")
    }

    // Преобразуем roleID в строку, так как GuildMemberRoleAdd ожидает строку
    err := session.GuildMemberRoleAdd(guildID, userID, strconv.FormatInt(roleID, 10))
    if err != nil {
        return fmt.Errorf("не удалось выдать роль: %v", err)
    }

    log.Printf("Роль %d успешно выдана пользователю %s", roleID, userID)
    return nil
}

// ProfileHandler — возвращает данные профиля в формате JSON

func ProfileHandler(w http.ResponseWriter, r *http.Request) {

    session, _ := SessionStore.Get(r, "discord-session")
    userID, ok := session.Values["userID"].(string)
    if userID, ok := session.Values["userID"].(string); ok {
        log.Printf("Session userID: %s", userID)
    } else {
        log.Printf("Session userID: not found")
    }
    if !ok {
        http.Error(w, "Не авторизован", http.StatusUnauthorized)
        return
    }

    // Получаем данные пользователя (nickname, coins, status) из базы
    nickname, coins, status, err := db.GetUser(userID)
    if err != nil {
        http.Error(w, "Ошибка при получении данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Получаем аватар пользователя из сессии
    avatar, ok := session.Values["avatar"].(string)
    if !ok || avatar == "" {
        avatar = "/default-avatar.png" // Если аватар не найден, используем дефолтный
    }

    // Получаем роли пользователя из Discord
    userRoles := GetUserRolesFromDiscord(userID)

    // Проверяем, имеет ли пользователь роль VIP
    vipRole := "1320294095020621844" // ID VIP роли
    isVIP := false
    for _, role := range userRoles {
        if role == vipRole {
            isVIP = true
            break
        }
    }

    // Создаем объект с данными профиля для отправки в JSON
    profileData := map[string]interface{}{
        "username": nickname,
        "avatar":   avatar,
        "coins":    coins,
        "status":   status,
        "roles":    userRoles,
        "isVIP":    isVIP, // Добавляем поле VIP
    }

    // Устанавливаем заголовок ответа для JSON
    w.Header().Set("Content-Type", "application/json")

    // Отправляем профиль в формате JSON
    err = json.NewEncoder(w).Encode(profileData)
    if err != nil {
        http.Error(w, "Ошибка отправки данных профиля: "+err.Error(), http.StatusInternalServerError)
    }
}

func GetUserRolesFromDiscord(userID string) []string {
    guildID := global.ServerID // ID вашего сервера
    session := global.BotSession
    if session == nil {
        return nil
    }

    member, err := session.GuildMember(guildID, userID)
    if err != nil {
        log.Printf("Ошибка при получении ролей пользователя: %v", err)
        return nil
    }

    var roles []string
    for _, roleID := range member.Roles {
        roles = append(roles, roleID)
    }
    return roles
}

// LogoutHandler — удаляет данные сессии пользователя
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
    // Удаляем сессию
    session, _ := SessionStore.Get(r, "discord-session")
    session.Options = &sessions.Options{
        Path:     "/",
        MaxAge:   86400 * 30, // 30 дней
        HttpOnly: true,
        Secure:   true,                 // Включите, если используете HTTPS
        SameSite: http.SameSiteLaxMode, // Рекомендуется для защиты от CSRF атак
    }

    session.Values = make(map[interface{}]interface{}) // Полностью очищаем значения
    session.Save(r, w)

    // Перенаправляем на главную страницу
    http.Redirect(w, r, "/", http.StatusSeeOther)
}

func CheckAuthHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := SessionStore.Get(r, "discord-session")
    if _, ok := session.Values["userID"].(string); ok {
        w.WriteHeader(http.StatusOK)
    } else {
        w.WriteHeader(http.StatusUnauthorized)
    }
}

func GetUserIdFromSessionOrHeader(r *http.Request) string {
    session, _ := SessionStore.Get(r, "discord-session")
    if userID, ok := session.Values["userID"].(string); ok {
        return userID
    }

    userIDFromHeader := r.Header.Get("X-User-ID")
    if userIDFromHeader != "" {
        return userIDFromHeader
    }

    return ""
}

index.js

document.addEventListener('DOMContentLoaded', () => {
    document.getElementById('news-section').classList.remove('loading');

    // Сначала скрываем окно авторизации
    document.getElementById('unauthorized-message').classList.remove('show');

    function preloadPage(url) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.send();
    }

    // Предзагрузка страниц при загрузке текущей страницы
    window.addEventListener('load', function() {
        ['profile.html', 'shop.html'].forEach(url => {
            const link = document.createElement('link');
            link.rel = 'prefetch';
            link.href = url;
            document.head.appendChild(link);
        });
    });

    document.getElementById('news-section').classList.remove('blurred');

    fetch('/profile', {
        method: 'GET',
        credentials: 'include'
    })

    .then(response => {
            if (!response.ok) {
                // Показываем окно только если пользователь не авторизован
                document.getElementById('blur-overlay').classList.add('blurred');
                document.getElementById('nav-links').classList.add('hidden');
                document.getElementById('unauthorized-message').classList.add('show');
                throw new Error('Пользователь не авторизован');
            }
            return response.json();
        })
        .then(data => {
            // Логирование полученных данных пользователя
            console.log('Полученные данные пользователя:', data);

            // Проверка ключей данных
            if (!data.username) {
                console.error('Отсутствует ключ "username" в данных пользователя');
            } else {
                console.log(`Имя пользователя: ${data.username}`);
            }

            if (!data.roles || !Array.isArray(data.roles)) {
                console.error('Отсутствует ключ "roles" или он не является массивом');
            } else {
                console.log(`Роли пользователя: ${data.roles.join(', ')}`);
            }

            if (data.isVIP === undefined) {
                console.error('Отсутствует ключ "isVIP" в данных пользователя');
            } else {
                console.log(`Статус VIP: ${data.isVIP ? 'Да' : 'Нет'}`);
            }

            // Убираем классы, когда пользователь авторизован
            document.getElementById('blur-overlay').classList.remove('blurred');
            document.getElementById('nav-links').classList.remove('hidden');
            document.getElementById('unauthorized-message').classList.remove('show');

            const newsSection = document.getElementById('news-section');
            const userRoles = data.roles || [];
            const adminRoles = ["1318188643944501289", "1318188870415679508", "1318189013823389711"];
            const isAdmin = userRoles.some(role => adminRoles.includes(role));

            document.getElementById('tournaments-link').style.display = isAdmin ? 'inline' : 'none';

            // Функция для обновления новостей
            function updateNews() {
                fetch('/get-news')
                    .then(response => response.json())
                    .then(news => {
                        console.log('Загруженные новости:', news);

                        newsSection.innerHTML = '';
                        if (news.length === 0) {
                            console.log('Новости не загружены или пусты');
                        } else {
                            news.forEach((newsData) => {
                                const newsBlock = document.createElement('div');
                                newsBlock.classList.add('news-block');

                                const deleteButton = document.createElement('span');
                                deleteButton.textContent = '❌';
                                deleteButton.classList.add('delete-btn');

                                if (isAdmin) {
                                    deleteButton.style.display = 'block';
                                    deleteButton.onclick = () => {
                                        fetch(`/delete-news?id=${newsData.id}`, { method: 'DELETE' })
                                            .then(response => {
                                                if (response.ok) {
                                                    newsSection.removeChild(newsBlock);
                                                    console.log('Новость успешно удалена');
                                                } else {
                                                    console.error('Ошибка при удалении новости');
                                                }
                                            })
                                            .catch(err => console.error('Ошибка:', err));
                                    };
                                } else {
                                    deleteButton.style.display = 'none';
                                }

                                newsBlock.innerHTML = `
                  <img src="${newsData.image}" alt="News image" loading="lazy">
                  <h3>${newsData.title}</h3>
                  <p>${newsData.text}</p>
                `;

                                newsBlock.appendChild(deleteButton);
                                newsSection.insertBefore(newsBlock, newsSection.firstChild);
                            });
                        }
                    })
                    .catch(err => console.error('Ошибка загрузки новостей:', err));
            }

            // Первоначальная загрузка новостей
            updateNews();

            // Интервал для повторной загрузки новостей каждые 10 секунд
            setInterval(updateNews, 10000);
        })
        .catch(err => {
            console.error('Ошибка:', err.message);
        });

    // Функция для показа окна авторизации
    const authPrompt = document.getElementById('unauthorized-message');

    function showAuthPrompt() {
        authPrompt.classList.add('show');
    }


});

I used different methods and opened ports. And used CORS settings.