Исправил постинг токена в ссылках

This commit is contained in:
Taz
2026-03-07 19:14:58 +01:00
parent 34e6a4562c
commit d300bc9824
9 changed files with 270 additions and 91 deletions
+87 -1
View File
@@ -2,15 +2,35 @@ package main
import (
"encoding/json"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/joho/godotenv"
)
// Заполняются при сборке: go build -ldflags "-X main.Version=... -X main.BuildDate=..."
var (
Version = "dev"
BuildDate = "unknown"
)
func main() {
// Загружаем .env из текущей директории или рядом с бинарником
if err := godotenv.Load(); err != nil {
execPath, _ := os.Executable()
envPath := filepath.Join(filepath.Dir(execPath), ".env")
if err2 := godotenv.Load(envPath); err2 != nil {
log.Println("Файл .env не найден, используются переменные окружения shell")
}
}
log.Println("Go Chat Bridge запущен.")
log.Printf("Версия: %s, сборка: %s", Version, BuildDate)
telegramToken := os.Getenv("TELEGRAM_TOKEN")
discordToken := os.Getenv("DISCORD_TOKEN")
@@ -144,6 +164,67 @@ func main() {
bridge.StartDiscordListener(discordChannelID, func(text string, sender string, media []string) { forward(text, "discord", sender, media) })
bridge.StartVKListener(func(text string, sender string, media []string) { forward(text, "vk", sender, media) })
// Прокси медиа Telegram: по подписанному токену отдаём файл без раскрытия токена бота в ссылках
http.HandleFunc("/media/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
log.Printf("[Media] %s %s — Method not allowed", r.Method, r.URL.Path)
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Путь: /media/<токен>/<имя_файла.jpg> — имя в конце только для вида, токен до последнего /
pathSuffix := strings.TrimPrefix(r.URL.Path, "/media/")
if pathSuffix == "" {
log.Printf("[Media] запрос без токена")
http.Error(w, "Not found", http.StatusNotFound)
return
}
token := pathSuffix
if idx := strings.LastIndex(pathSuffix, "/"); idx != -1 {
token = pathSuffix[:idx]
}
secret := os.Getenv("MEDIA_PROXY_SECRET")
if secret == "" {
log.Printf("[Media] MEDIA_PROXY_SECRET не задан, прокси отключён")
http.Error(w, "Proxy not configured", http.StatusInternalServerError)
return
}
filePath, ok := verifyMediaToken(token, secret)
if !ok {
pathLog := r.URL.Path
if len(pathLog) > 50 {
pathLog = pathLog[:50] + "..."
}
log.Printf("[Media] неверный или просроченный токен (path=%s)", pathLog)
http.Error(w, "Invalid or expired link", http.StatusForbidden)
return
}
log.Printf("[Media] отдача файла: %s", filePath)
directURL := bridge.GetTelegramFileDirectURL(filePath)
resp, err := http.Get(directURL)
if err != nil {
log.Printf("[Media] ошибка загрузки из Telegram для %s: %v", filePath, err)
http.Error(w, "Failed to fetch file", http.StatusBadGateway)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("[Media] Telegram вернул %d для %s", resp.StatusCode, filePath)
http.Error(w, "Upstream error", resp.StatusCode)
return
}
if ct := resp.Header.Get("Content-Type"); ct != "" {
w.Header().Set("Content-Type", ct)
}
// Имя файла с расширением — иначе при открытии/сохранении расширение теряется
fileName := path.Base(filePath)
if fileName != "" && fileName != "." {
w.Header().Set("Content-Disposition", "inline; filename=\""+fileName+"\"")
}
w.Header().Set("Cache-Control", "private, max-age=86400")
io.Copy(w, resp.Body)
log.Printf("[Media] отдано: %s", filePath)
})
// webhook endpoint
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
@@ -193,6 +274,11 @@ func main() {
w.Write([]byte("ok"))
})
log.Println("Webhook listening on :8080 (/webhook)")
if os.Getenv("PUBLIC_URL") != "" && os.Getenv("MEDIA_PROXY_SECRET") != "" {
log.Println("Медиа-прокси включён: ссылки из Telegram будут без токена в URL")
} else {
log.Println("Медиа-прокси выключен (задайте PUBLIC_URL и MEDIA_PROXY_SECRET для включения)")
}
log.Println("Webhook listening on :8080 (/webhook, /media/)")
log.Fatal(http.ListenAndServe(":8080", nil))
}