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.