feat: 移除Telegram功能支持

移除了与Telegram相关的所有功能,包括:
- 从README文档中删除Telegram配置选项说明
- 从客户端界面中移除Telegram按钮和相关HTML元素
- 删除Telegram相关的环境变量配置
- 移除Telegram发送配置的功能代码
- 从客户端模型中删除Telegram用户ID字段
- 清理相关的依赖包引用
This commit is contained in:
devcxl 2026-01-11 21:16:01 +08:00
parent f1383a9043
commit 0f26cf6bf7
14 changed files with 31 additions and 569 deletions

View File

@ -9,7 +9,7 @@ A web user interface to manage your WireGuard setup.
- Friendly UI
- Authentication
- Manage extra client information (name, email, etc.)
- Retrieve client config using QR code / file / email / Telegram
- Retrieve client config using QR code / file / email
![wireguard-ui 0.3.7](https://user-images.githubusercontent.com/37958026/177041280-e3e7ca16-d4cf-4e95-9920-68af15e780dd.png)
@ -71,10 +71,6 @@ docker-compose up
| `SMTP_AUTH_TYPE` | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE` | `NONE` |
| `SMTP_ENCRYPTION` | The encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS` | `STARTTLS` |
| `SMTP_HELO` | Hostname to use for the HELO message. smtp-relay.gmail.com needs this set to anything but `localhost` | `localhost` |
| `TELEGRAM_TOKEN` | Telegram bot token for distributing configs to clients | N/A |
| `TELEGRAM_ALLOW_CONF_REQUEST` | Allow users to get configs from the bot by sending a message | `false` |
| `TELEGRAM_FLOOD_WAIT` | Time in minutes before the next conf request is processed | `60` |
### Defaults for server configuration
These environment variables are used to control the default server settings used when initializing the database.

View File

@ -9,7 +9,7 @@
- 友好的图形界面
- 登录认证
- 维护额外的客户端信息(姓名、邮箱等)
- 通过二维码 / 文件 / 邮件 / Telegram 获取客户端配置
- 通过二维码 / 文件 / 邮件获取客户端配置
![wireguard-ui 0.3.7](https://user-images.githubusercontent.com/37958026/177041280-e3e7ca16-d4cf-4e95-9920-68af15e780dd.png)
@ -71,10 +71,6 @@ docker-compose up
| `SMTP_AUTH_TYPE` | SMTP 认证方式,可选:`PLAIN`、`LOGIN`、`NONE` | `NONE` |
| `SMTP_ENCRYPTION` | 加密方式,可选:`NONE`、`SSL`、`SSLTLS`、`TLS`、`STARTTLS` | `STARTTLS` |
| `SMTP_HELO` | HELO 消息中使用的主机名。`smtp-relay.gmail.com` 需要设置为非 `localhost` 的值 | `localhost` |
| `TELEGRAM_TOKEN` | 向客户端分发配置的 Telegram Bot Token | N/A |
| `TELEGRAM_ALLOW_CONF_REQUEST` | 允许用户通过向 Bot 发送消息获取配置 | `false` |
| `TELEGRAM_FLOOD_WAIT` | 处理下一次配置请求前的等待分钟数 | `60` |
### 服务器配置默认值
这些环境变量控制初始化数据库时的服务器默认设置。

View File

@ -1,19 +1,5 @@
function renderClientList(data) {
$.each(data, function(index, obj) {
// render telegram button
let telegramButton = ''
if (obj.Client.telegram_userid) {
telegramButton = `<div class="btn-group">
<button type="button" class="btn btn-outline-primary btn-sm" data-toggle="modal"
data-target="#modal_telegram_client" data-clientid="${obj.Client.id}"
data-clientname="${obj.Client.name}">Telegram</button>
</div>`
}
let telegramHtml = "";
if (obj.Client.telegram_userid && obj.Client.telegram_userid.length > 0) {
telegramHtml = `<span class="info-box-text" style="display: none"><i class="fas fa-tguserid"></i>${obj.Client.telegram_userid}</span>`
}
// render client status css tag style
let clientStatusHtml = '>'
@ -63,7 +49,6 @@ function renderClientList(data) {
data-target="#modal_email_client" data-clientid="${obj.Client.id}"
data-clientname="${obj.Client.name}">Email</button>
</div>
${telegramButton}
<div class="btn-group">
<button type="button" class="btn btn-outline-danger btn-sm">More</button>
<button type="button" class="btn btn-outline-danger btn-sm dropdown-toggle dropdown-icon"
@ -85,7 +70,6 @@ function renderClientList(data) {
<span class="info-box-text"><i class="fas fa-user"></i> ${obj.Client.name}</span>
<span class="info-box-text" style="display: none"><i class="fas fa-key"></i> ${obj.Client.public_key}</span>
<span class="info-box-text" style="display: none"><i class="fas fa-subnetrange"></i>${subnetRangesString}</span>
${telegramHtml}
${additionalNotesHtml}
<span class="info-box-text"><i class="fas fa-envelope"></i> ${obj.Client.email}</span>
<span class="info-box-text"><i class="fas fa-clock"></i>

2
go.mod
View File

@ -3,8 +3,8 @@ module github.com/ngoduykhanh/wireguard-ui
go 1.21
require (
github.com/NicoNex/echotron/v3 v3.27.0
github.com/glendc/go-external-ip v0.1.0
github.com/gorilla/sessions v1.2.2
github.com/labstack/echo-contrib v0.15.0
github.com/labstack/echo/v4 v4.11.4

View File

@ -10,7 +10,6 @@ import (
"os"
"regexp"
"sort"
"strconv"
"strings"
"time"
@ -19,14 +18,12 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/rs/xid"
"github.com/skip2/go-qrcode"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/ngoduykhanh/wireguard-ui/emailer"
"github.com/ngoduykhanh/wireguard-ui/model"
"github.com/ngoduykhanh/wireguard-ui/store"
"github.com/ngoduykhanh/wireguard-ui/telegram"
"github.com/ngoduykhanh/wireguard-ui/util"
)
@ -414,14 +411,6 @@ func NewClient(db store.IStore) echo.HandlerFunc {
var client model.Client
c.Bind(&client)
// Validate Telegram userid if provided
if client.TgUserid != "" {
idNum, err := strconv.ParseInt(client.TgUserid, 10, 64)
if err != nil || idNum == 0 {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Telegram userid must be a non-zero number"})
}
}
// read server information
server, err := db.GetServer()
if err != nil {
@ -575,51 +564,6 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
}
}
// SendTelegramClient handler to send the configuration via Telegram
func SendTelegramClient(db store.IStore) echo.HandlerFunc {
type clientIdUseridPayload struct {
ID string `json:"id"`
Userid string `json:"userid"`
}
return func(c echo.Context) error {
var payload clientIdUseridPayload
c.Bind(&payload)
clientData, err := db.GetClientByID(payload.ID, model.QRCodeSettings{Enabled: false})
if err != nil {
log.Errorf("Cannot generate client id %s config file for downloading: %v", payload.ID, err)
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
}
// build config
server, _ := db.GetServer()
globalSettings, _ := db.GetGlobalSettings()
config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
configData := []byte(config)
var qrData []byte
if clientData.Client.PrivateKey != "" {
qrData, err = qrcode.Encode(config, qrcode.Medium, 512)
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "qr gen: " + err.Error()})
}
}
userid, err := strconv.ParseInt(clientData.Client.TgUserid, 10, 64)
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "userid: " + err.Error()})
}
err = telegram.SendConfig(userid, clientData.Client.Name, configData, qrData, false)
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
}
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Telegram message sent successfully"})
}
}
// UpdateClient handler to update client information
func UpdateClient(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {
@ -636,14 +580,6 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
}
// Validate Telegram userid if provided
if _client.TgUserid != "" {
idNum, err := strconv.ParseInt(_client.TgUserid, 10, 64)
if err != nil || idNum == 0 {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Telegram userid must be a non-zero number"})
}
}
server, err := db.GetServer()
if err != nil {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{
@ -651,8 +587,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
})
}
client := *clientData.Client
// validate the input Allocation IPs
allocatedIPs, err := util.GetAllocatedIPs(client.ID)
check, err := util.ValidateIPAllocation(server.Interface.Addresses, allocatedIPs, _client.AllocatedIPs)
if !check {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
@ -710,8 +645,8 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
// map new data
client.Name = _client.Name
client.Email = _client.Email
client.TgUserid = _client.TgUserid
client.Enabled = _client.Enabled
client.UseServerDNS = _client.UseServerDNS
client.AllocatedIPs = _client.AllocatedIPs
client.AllowedIPs = _client.AllowedIPs

69
main.go
View File

@ -15,12 +15,11 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/ngoduykhanh/wireguard-ui/store"
"github.com/ngoduykhanh/wireguard-ui/telegram"
"github.com/ngoduykhanh/wireguard-ui/emailer"
"github.com/ngoduykhanh/wireguard-ui/handler"
"github.com/ngoduykhanh/wireguard-ui/router"
"github.com/ngoduykhanh/wireguard-ui/store"
"github.com/ngoduykhanh/wireguard-ui/store/jsondb"
"github.com/ngoduykhanh/wireguard-ui/util"
)
@ -32,27 +31,24 @@ var (
gitRef = "N/A"
buildTime = fmt.Sprintf(time.Now().UTC().Format("01-02-2006 15:04:05"))
// configuration variables
flagDisableLogin = false
flagBindAddress = "0.0.0.0:5000"
flagSmtpHostname = "127.0.0.1"
flagSmtpPort = 25
flagSmtpUsername string
flagSmtpPassword string
flagSmtpAuthType = "NONE"
flagSmtpNoTLSCheck = false
flagSmtpEncryption = "STARTTLS"
flagSmtpHelo = "localhost"
flagSendgridApiKey string
flagEmailFrom string
flagEmailFromName = "WireGuard UI"
flagTelegramToken string
flagTelegramAllowConfRequest = false
flagTelegramFloodWait = 60
flagSessionSecret = util.RandomString(32)
flagSessionMaxDuration = 90
flagWgConfTemplate string
flagBasePath string
flagSubnetRanges string
flagDisableLogin = false
flagBindAddress = "0.0.0.0:5000"
flagSmtpHostname = "127.0.0.1"
flagSmtpPort = 25
flagSmtpUsername string
flagSmtpPassword string
flagSmtpAuthType = "NONE"
flagSmtpNoTLSCheck = false
flagSmtpEncryption = "STARTTLS"
flagSmtpHelo = "localhost"
flagSendgridApiKey string
flagEmailFrom string
flagEmailFromName = "WireGuard UI"
flagSessionSecret = util.RandomString(32)
flagSessionMaxDuration = 90
flagWgConfTemplate string
flagBasePath string
flagSubnetRanges string
)
const (
@ -87,9 +83,6 @@ func init() {
flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : PLAIN, LOGIN or NONE.")
flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.")
flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")
flag.StringVar(&flagTelegramToken, "telegram-token", util.LookupEnvOrString("TELEGRAM_TOKEN", flagTelegramToken), "Telegram bot token for distributing configs to clients.")
flag.BoolVar(&flagTelegramAllowConfRequest, "telegram-allow-conf-request", util.LookupEnvOrBool("TELEGRAM_ALLOW_CONF_REQUEST", flagTelegramAllowConfRequest), "Allow users to get configs from the bot by sending a message.")
flag.IntVar(&flagTelegramFloodWait, "telegram-flood-wait", util.LookupEnvOrInt("TELEGRAM_FLOOD_WAIT", flagTelegramFloodWait), "Time in minutes before the next conf request is processed.")
flag.StringVar(&flagWgConfTemplate, "wg-conf-template", util.LookupEnvOrString("WG_CONF_TEMPLATE", flagWgConfTemplate), "Path to custom wg.conf template.")
flag.StringVar(&flagBasePath, "base-path", util.LookupEnvOrString("BASE_PATH", flagBasePath), "The base path of the URL")
flag.StringVar(&flagSubnetRanges, "subnet-ranges", util.LookupEnvOrString("SUBNET_RANGES", flagSubnetRanges), "IP ranges to choose from when assigning an IP for a client.")
@ -146,11 +139,6 @@ func init() {
lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO"))
telegram.Token = flagTelegramToken
telegram.AllowConfRequest = flagTelegramAllowConfRequest
telegram.FloodWait = flagTelegramFloodWait
telegram.LogLevel = lvl
// print only if log level is INFO or lower
if lvl <= log.INFO {
// print app information
@ -241,7 +229,6 @@ func main() {
app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/send-telegram-client", handler.SendTelegramClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/remove-client", handler.RemoveClient(db), handler.ValidSession, handler.ContentTypeJson)
app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession)
@ -269,13 +256,6 @@ func main() {
// serves other static files
app.GET(util.BasePath+"/static/*", echo.WrapHandler(http.StripPrefix(util.BasePath+"/static/", assetHandler)))
initDeps := telegram.TgBotInitDependencies{
DB: db,
SendRequestedConfigsToTelegram: util.SendRequestedConfigsToTelegram,
}
initTelegram(initDeps)
if strings.HasPrefix(util.BindAddress, "unix://") {
// Listen on unix domain socket.
// https://github.com/labstack/echo/issues/830
@ -327,14 +307,3 @@ func initServerConfig(db store.IStore, tmplDir fs.FS) {
log.Fatalf("Cannot create server config: %v", err)
}
}
func initTelegram(initDeps telegram.TgBotInitDependencies) {
go func() {
for {
err := telegram.Start(initDeps)
if err == nil {
break
}
}
}()
}

View File

@ -11,7 +11,6 @@ type Client struct {
PublicKey string `json:"public_key"`
PresharedKey string `json:"preshared_key"`
Name string `json:"name"`
TgUserid string `json:"telegram_userid"`
Email string `json:"email"`
SubnetRanges []string `json:"subnet_ranges,omitempty"`
AllocatedIPs []string `json:"allocated_ips"`

View File

@ -6,7 +6,6 @@ import (
"fmt"
"os"
"path"
"strconv"
"time"
"github.com/sdomino/scribble"
@ -170,19 +169,6 @@ func (o *JsonDB) Init() error {
}
}
clients, err := o.GetClients(false)
if err != nil {
return nil
}
for _, cl := range clients {
client := cl.Client
if client.Enabled && len(client.TgUserid) > 0 {
if userid, err := strconv.ParseInt(client.TgUserid, 10, 64); err == nil {
util.UpdateTgToClientID(userid, client.ID)
}
}
}
return nil
}
@ -337,26 +323,17 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti
func (o *JsonDB) SaveClient(client model.Client) error {
clientPath := path.Join(path.Join(o.dbPath, "clients"), client.ID+".json")
output := o.conn.Write("clients", client.ID, client)
if output == nil {
if client.Enabled && len(client.TgUserid) > 0 {
if userid, err := strconv.ParseInt(client.TgUserid, 10, 64); err == nil {
util.UpdateTgToClientID(userid, client.ID)
}
} else {
util.RemoveTgToClientID(client.ID)
}
} else {
util.RemoveTgToClientID(client.ID)
if output != nil {
return output
}
err := util.ManagePerms(clientPath)
if err != nil {
return err
}
return output
return nil
}
func (o *JsonDB) DeleteClient(clientID string) error {
util.RemoveTgToClientID(clientID)
return o.conn.Delete("clients", clientID)
}

View File

@ -1,161 +0,0 @@
package telegram
import (
"fmt"
"sync"
"time"
"github.com/NicoNex/echotron/v3"
"github.com/labstack/gommon/log"
"github.com/ngoduykhanh/wireguard-ui/store"
)
type SendRequestedConfigsToTelegram func(db store.IStore, userid int64) []string
type TgBotInitDependencies struct {
DB store.IStore
SendRequestedConfigsToTelegram SendRequestedConfigsToTelegram
}
var (
Token string
AllowConfRequest bool
FloodWait int
LogLevel log.Lvl
Bot *echotron.API
BotMutex sync.RWMutex
floodWait = make(map[int64]int64)
floodMessageSent = make(map[int64]struct{})
)
func Start(initDeps TgBotInitDependencies) (err error) {
ticker := time.NewTicker(time.Minute)
defer func() {
if err != nil {
BotMutex.Lock()
Bot = nil
BotMutex.Unlock()
ticker.Stop()
}
if r := recover(); r != nil {
err = fmt.Errorf("[PANIC] recovered from panic: %v", r)
}
}()
token := Token
if token == "" || len(token) < 30 {
return
}
bot := echotron.NewAPI(token)
res, err := bot.GetMe()
if !res.Ok || err != nil {
log.Warnf("[Telegram] Unable to connect to bot.\n%v\n%v", res.Description, err)
return
}
BotMutex.Lock()
Bot = &bot
BotMutex.Unlock()
if LogLevel <= log.INFO {
fmt.Printf("[Telegram] Authorized as %s\n", res.Result.Username)
}
go func() {
for range ticker.C {
updateFloodWait()
}
}()
if !AllowConfRequest {
return
}
updatesChan := echotron.PollingUpdatesOptions(token, false, echotron.UpdateOptions{AllowedUpdates: []echotron.UpdateType{echotron.MessageUpdate}})
for update := range updatesChan {
if update.Message != nil {
userid := update.Message.Chat.ID
if _, wait := floodWait[userid]; wait {
if _, notified := floodMessageSent[userid]; notified {
continue
}
floodMessageSent[userid] = struct{}{}
_, err := bot.SendMessage(
fmt.Sprintf("You can only request your configs once per %d minutes", FloodWait),
userid,
&echotron.MessageOptions{
ReplyToMessageID: update.Message.ID,
})
if err != nil {
log.Errorf("Failed to send telegram message. Error %v", err)
}
continue
}
floodWait[userid] = time.Now().Unix()
failed := initDeps.SendRequestedConfigsToTelegram(initDeps.DB, userid)
if len(failed) > 0 {
messageText := "Failed to send configs:\n"
for _, f := range failed {
messageText += f + "\n"
}
_, err := bot.SendMessage(
messageText,
userid,
&echotron.MessageOptions{
ReplyToMessageID: update.Message.ID,
})
if err != nil {
log.Errorf("Failed to send telegram message. Error %v", err)
}
}
}
}
return err
}
func SendConfig(userid int64, clientName string, confData, qrData []byte, ignoreFloodWait bool) error {
BotMutex.RLock()
defer BotMutex.RUnlock()
if Bot == nil {
return fmt.Errorf("telegram bot is not configured or not available")
}
if _, wait := floodWait[userid]; wait && !ignoreFloodWait {
return fmt.Errorf("this client already got their config less than %d minutes ago", FloodWait)
}
if !ignoreFloodWait {
floodWait[userid] = time.Now().Unix()
}
qrAttachment := echotron.NewInputFileBytes("qr.png", qrData)
_, err := Bot.SendPhoto(qrAttachment, userid, &echotron.PhotoOptions{Caption: clientName})
if err != nil {
log.Error(err)
return fmt.Errorf("unable to send qr picture")
}
confAttachment := echotron.NewInputFileBytes(clientName+".conf", confData)
_, err = Bot.SendDocument(confAttachment, userid, nil)
if err != nil {
log.Error(err)
return fmt.Errorf("unable to send conf file")
}
return nil
}
func updateFloodWait() {
thresholdTS := time.Now().Unix() - 60*int64(FloodWait)
for userid, ts := range floodWait {
if ts < thresholdTS {
delete(floodWait, userid)
delete(floodMessageSent, userid)
}
}
}

View File

@ -240,6 +240,7 @@
</label>
<input type="text" data-role="tagsinput" class="form-control" id="client_extra_allowed_ips" value="{{ StringsJoin .client_defaults.ExtraAllowedIps "," }}">
</div>
<div class="form-group">
<label for="client_endpoint" class="control-label">Endpoint</label>
<input type="text" class="form-control" id="client_endpoint" name="client_endpoint">
@ -284,10 +285,6 @@
<details style="margin-top: 0.5rem;">
<summary><strong>Additional configuration</strong>
</summary>
<div class="form-group" style="margin-top: 0.5rem;">
<label for="client_telegram_userid" class="control-label">Telegram userid</label>
<input type="text" class="form-control" id="client_telegram_userid" name="client_telegram_userid">
</div>
<div class="form-group">
<label for="additional_notes" class="control-label">Notes</label>
<textarea class="form-control" style="min-height: 6rem;" id="additional_notes" name="additional_notes" placeholder="Additional notes about this client"></textarea>
@ -461,7 +458,6 @@
function submitNewClient() {
const name = $("#client_name").val();
const email = $("#client_email").val();
const telegram_userid = $("#client_telegram_userid").val();
const allocated_ips = $("#client_allocated_ips").val().split(",");
const allowed_ips = $("#client_allowed_ips").val().split(",");
const endpoint = $("#client_endpoint").val();
@ -487,7 +483,7 @@
const additional_notes = $("#additional_notes").val();
const data = {"name": name, "email": email, "telegram_userid": telegram_userid, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips,
const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips,
"extra_allowed_ips": extra_allowed_ips, "endpoint": endpoint, "use_server_dns": use_server_dns, "enabled": enabled,
"public_key": public_key, "preshared_key": preshared_key, "additional_notes": additional_notes};
@ -629,7 +625,6 @@
$("#client_allocated_ips").importTags('');
$("#client_extra_allowed_ips").importTags('');
$("#client_endpoint").val('');
$("#client_telegram_userid").val('');
$("#additional_notes").val('');
updateSubnetRangesList("#subnet_ranges");
updateIPAllocationSuggestion(true);

View File

@ -80,34 +80,7 @@ Wireguard Clients
</div>
<!-- /.modal -->
<div class="modal fade" id="modal_telegram_client">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Telegram Configuration</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form name="frm_telegram_client" id="frm_telegram_client">
<div class="modal-body">
<input type="hidden" id="tg_client_id" name="tg_client_id">
<div class="form-group">
<label for="tg_client_userid" class="control-label">Telegram userid</label>
<input type="text" class="form-control" id="tg_client_userid" name="tg_client_userid">
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
</form>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
<div class="modal fade" id="modal_edit_client">
<div class="modal-dialog">
@ -191,10 +164,6 @@ Wireguard Clients
<details style="margin-top: 0.5rem;">
<summary><strong>Additional configuration</strong>
</summary>
<div class="form-group" style="margin-top: 0.5rem;">
<label for="_client_telegram_userid" class="control-label">Telegram userid</label>
<input type="text" class="form-control" id="_client_telegram_userid" name="_client_telegram_userid">
</div>
<div class="form-group">
<label for="_additional_notes" class="control-label">Notes</label>
<textarea class="form-control" style="min-height: 6rem;" id="_additional_notes" name="_additional_notes" placeholder="Additional notes about this client"></textarea>
@ -425,11 +394,6 @@ Wireguard Clients
}
})
$(".badge-secondary").filter(':contains("' + query + '")').parent().parent().parent().show();
$(".fa-tguserid").each(function () {
if ($(this).parent().text().trim().indexOf(query) != -1) {
$(this).closest('.col-lg-4').show();
}
})
let upperQuery = query.toUpperCase()
$(".fa-additional_notes").each(function () {
if ($(this).parent().text().trim().indexOf(upperQuery) != -1) {
@ -626,7 +590,6 @@ Wireguard Clients
modal.find(".modal-title").text("Edit Client " + client.name);
modal.find("#_client_id").val(client.id);
modal.find("#_client_telegram_userid").val(client.telegram_userid);
modal.find("#_client_name").val(client.name);
modal.find("#_client_email").val(client.email);
@ -728,29 +691,6 @@ Wireguard Clients
});
}
// submitTelegramClient function for sending a telegram message with the configuration to the client
function submitTelegramClient() {
const client_id = $("#tg_client_id").val();
const userid = $("#tg_client_userid").val();
const data = {"id": client_id, "userid": userid};
$.ajax({
cache: false,
method: 'POST',
url: '{{.basePath}}/send-telegram-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
success: function(resp) {
$("#modal_telegram_client").modal('hide');
toastr.success('Sent config via telegram to client successfully');
},
error: function(jqXHR, exception) {
const responseJson = jQuery.parseJSON(jqXHR.responseText);
toastr.error(responseJson['message']);
}
});
}
// submitEditClient function for updating an existing client
// This sends dialogue data to the back-end when user presses "Save"
// See e.g. routes.go:UpdateClient for where data is processed/verified.
@ -758,7 +698,6 @@ Wireguard Clients
const client_id = $("#_client_id").val();
const name = $("#_client_name").val();
const email = $("#_client_email").val();
const telegram_userid = $("#_client_telegram_userid").val();
const allocated_ips = $("#_client_allocated_ips").val().split(",");
const allowed_ips = $("#_client_allowed_ips").val().split(",");
let use_server_dns = false;
@ -784,7 +723,7 @@ Wireguard Clients
const additional_notes = $("#_additional_notes").val();
const data = {"id": client_id, "name": name, "email": email, "telegram_userid": telegram_userid, "allocated_ips": allocated_ips,
const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips,
"allowed_ips": allowed_ips, "extra_allowed_ips": extra_allowed_ips, "endpoint": endpoint,
"use_server_dns": use_server_dns, "enabled": enabled, "public_key": public_key, "preshared_key": preshared_key, "additional_notes": additional_notes};
@ -815,8 +754,6 @@ Wireguard Clients
submitEditClient();
} else if (formId === "frm_email_client") {
submitEmailClient();
} else if (formId === "frm_telegram_client") {
submitTelegramClient();
}
}
@ -853,29 +790,6 @@ Wireguard Clients
regenerateQRCode();
});
$("#modal_telegram_client").on('show.bs.modal', function (event) {
let modal = $(this);
const button = $(event.relatedTarget);
const client_id = button.data('clientid');
$.ajax({
cache: false,
method: 'GET',
url: '{{.basePath}}/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {
const client = resp.Client;
modal.find(".modal-title").text("Send config to client " + client.name);
modal.find("#tg_client_id").val(client.id);
modal.find("#tg_client_userid").val(client.telegram_userid);
},
error: function (jqXHR, exception) {
const responseJson = jQuery.parseJSON(jqXHR.responseText);
toastr.error(responseJson['message']);
}
});
});
$(document).ready(function () {
$.validator.setDefaults({
@ -932,32 +846,6 @@ Wireguard Clients
$(element).removeClass('is-invalid');
}
});
// Telegram client form validation
$("#frm_telegram_client").validate({
rules: {
tg_client_userid: {
required: true,
number: true,
},
},
messages: {
tg_client_userid: {
required: "Please enter a telegram userid",
number: "Please enter a valid telegram userid"
},
},
errorElement: 'span',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.form-group').append(error);
},
highlight: function (element, errorClass, validClass) {
$(element).addClass('is-invalid');
},
unhighlight: function (element, errorClass, validClass) {
$(element).removeClass('is-invalid');
}
});
//
});
</script>

View File

@ -17,7 +17,6 @@ Table = {{ .globalSettings.Table }}
# ID: {{ .Client.ID }}
# Name: {{ .Client.Name }}
# Email: {{ .Client.Email }}
# Telegram: {{ .Client.TgUserid }}
# Created at: {{ .Client.CreatedAt }}
# Update at: {{ .Client.UpdatedAt }}
{{- if .Client.AdditionalNotes}}

View File

@ -3,6 +3,4 @@ package util
import "sync"
var IPToSubnetRange = map[string]uint16{}
var TgUseridToClientID = map[int64][]string{}
var TgUseridToClientIDMutex sync.RWMutex
var DBUsersToCRC32 = map[string]uint32{}

View File

@ -21,8 +21,6 @@ import (
"time"
"github.com/ngoduykhanh/wireguard-ui/store"
"github.com/ngoduykhanh/wireguard-ui/telegram"
"github.com/skip2/go-qrcode"
"golang.org/x/mod/sumdb/dirhash"
externalip "github.com/glendc/go-external-ip"
@ -598,55 +596,6 @@ func WriteWireGuardServerConfig(tmplDir fs.FS, serverConfig model.Server, client
}
// SendRequestedConfigsToTelegram to send client all their configs. Returns failed configs list.
func SendRequestedConfigsToTelegram(db store.IStore, userid int64) []string {
failedList := make([]string, 0)
TgUseridToClientIDMutex.RLock()
if clids, found := TgUseridToClientID[userid]; found && len(clids) > 0 {
TgUseridToClientIDMutex.RUnlock()
for _, clid := range clids {
clientData, err := db.GetClientByID(clid, qrCodeSettings)
if err != nil {
// return fmt.Errorf("unable to get client")
failedList = append(failedList, clid)
continue
}
// build config
server, _ := db.GetServer()
globalSettings, _ := db.GetGlobalSettings()
config := BuildClientConfig(*clientData.Client, server, globalSettings)
configData := []byte(config)
var qrData []byte
if clientData.Client.PrivateKey != "" {
qrData, err = qrcode.Encode(config, qrcode.Medium, 512)
if err != nil {
// return fmt.Errorf("unable to encode qr")
failedList = append(failedList, clientData.Client.Name)
continue
}
}
userid, err := strconv.ParseInt(clientData.Client.TgUserid, 10, 64)
if err != nil {
// return fmt.Errorf("tg usrid is unreadable")
failedList = append(failedList, clientData.Client.Name)
continue
}
err = telegram.SendConfig(userid, clientData.Client.Name, configData, qrData, true)
if err != nil {
failedList = append(failedList, clientData.Client.Name)
continue
}
time.Sleep(2 * time.Second)
}
} else {
TgUseridToClientIDMutex.RUnlock()
}
return failedList
}
func LookupEnvOrString(key string, defaultVal string) string {
if val, ok := os.LookupEnv(key); ok {
@ -778,68 +727,6 @@ func ManagePerms(path string) error {
return err
}
func AddTgToClientID(userid int64, clientID string) {
TgUseridToClientIDMutex.Lock()
defer TgUseridToClientIDMutex.Unlock()
if _, ok := TgUseridToClientID[userid]; ok && TgUseridToClientID[userid] != nil {
TgUseridToClientID[userid] = append(TgUseridToClientID[userid], clientID)
} else {
TgUseridToClientID[userid] = []string{clientID}
}
}
func UpdateTgToClientID(userid int64, clientID string) {
TgUseridToClientIDMutex.Lock()
defer TgUseridToClientIDMutex.Unlock()
// Detach clientID from any existing userid
for uid, cls := range TgUseridToClientID {
if cls != nil {
filtered := filterStringSlice(cls, clientID)
if len(filtered) > 0 {
TgUseridToClientID[uid] = filtered
} else {
delete(TgUseridToClientID, uid)
}
}
}
// Attach it to the new one
if _, ok := TgUseridToClientID[userid]; ok && TgUseridToClientID[userid] != nil {
TgUseridToClientID[userid] = append(TgUseridToClientID[userid], clientID)
} else {
TgUseridToClientID[userid] = []string{clientID}
}
}
func RemoveTgToClientID(clientID string) {
TgUseridToClientIDMutex.Lock()
defer TgUseridToClientIDMutex.Unlock()
// Detach clientID from any existing userid
for uid, cls := range TgUseridToClientID {
if cls != nil {
filtered := filterStringSlice(cls, clientID)
if len(filtered) > 0 {
TgUseridToClientID[uid] = filtered
} else {
delete(TgUseridToClientID, uid)
}
}
}
}
func filterStringSlice(s []string, excludedStr string) []string {
filtered := s[:0]
for _, v := range s {
if v != excludedStr {
filtered = append(filtered, v)
}
}
return filtered
}
func GetDBUserCRC32(dbuser model.User) uint32 {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)