基本功能已初步完善
This commit is contained in:
@@ -5,7 +5,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"xboard-go/internal/database"
|
||||
"xboard-go/internal/model"
|
||||
"xboard-go/internal/service"
|
||||
@@ -47,21 +49,62 @@ func AdminConfigSave(c *gin.Context) {
|
||||
Success(c, true)
|
||||
}
|
||||
|
||||
// AdminGetEmailTemplate list available email templates.
|
||||
func AdminGetEmailTemplate(c *gin.Context) {
|
||||
path := filepath.Join("resource", "views", "mail")
|
||||
files, err := listFiles(path, "*")
|
||||
if err != nil {
|
||||
Success(c, []string{})
|
||||
func AdminTestSendMail(c *gin.Context) {
|
||||
config := service.LoadEmailConfig()
|
||||
recipient := currentAdminEmail(c)
|
||||
if recipient == "" {
|
||||
recipient = config.SenderAddress()
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("[%s] SMTP test email", service.MustGetString("app_name", "XBoard"))
|
||||
result := gin.H{
|
||||
"email": recipient,
|
||||
"subject": subject,
|
||||
"template_name": config.TemplateName,
|
||||
"config": config.DebugConfig(),
|
||||
}
|
||||
|
||||
if recipient == "" {
|
||||
result["error"] = "no test recipient is available"
|
||||
Success(c, result)
|
||||
return
|
||||
}
|
||||
Success(c, files)
|
||||
|
||||
textBody := strings.Join([]string{
|
||||
"This is a test email from SingBox-Gopanel.",
|
||||
"",
|
||||
"If you received this email, the current SMTP configuration is working.",
|
||||
}, "\n")
|
||||
htmlBody := strings.Join([]string{
|
||||
"<h2>SMTP test email</h2>",
|
||||
"<p>If you received this email, the current SMTP configuration is working.</p>",
|
||||
}, "")
|
||||
|
||||
if err := service.SendMail(config, service.EmailMessage{
|
||||
To: []string{recipient},
|
||||
Subject: subject,
|
||||
TextBody: textBody,
|
||||
HTMLBody: htmlBody,
|
||||
}); err != nil {
|
||||
result["error"] = err.Error()
|
||||
}
|
||||
|
||||
Success(c, result)
|
||||
}
|
||||
|
||||
func AdminSetTelegramWebhook(c *gin.Context) {
|
||||
SuccessMessage(c, "telegram webhook setup is not wired in the Go backend yet", true)
|
||||
}
|
||||
|
||||
// AdminGetEmailTemplate list available email templates.
|
||||
func AdminGetEmailTemplate(c *gin.Context) {
|
||||
Success(c, collectEmailTemplateNames())
|
||||
}
|
||||
|
||||
// AdminGetThemeTemplate list available themes.
|
||||
func AdminGetThemeTemplate(c *gin.Context) {
|
||||
path := filepath.Join("public", "theme")
|
||||
files, err := listFiles(path, "*")
|
||||
files, err := listDirectoryEntries(path)
|
||||
if err != nil {
|
||||
Success(c, []string{})
|
||||
return
|
||||
@@ -89,7 +132,7 @@ func getAllConfigMappings() gin.H {
|
||||
"site": gin.H{
|
||||
"logo": service.MustGetString("logo", ""),
|
||||
"force_https": service.MustGetInt("force_https", 0),
|
||||
"stop_register": service.MustGetInt("stop_register", 0),
|
||||
"stop_register": service.MustGetInt("stop_register", 0),
|
||||
"app_name": service.MustGetString("app_name", "XBoard"),
|
||||
"app_description": service.MustGetString("app_description", "XBoard is best!"),
|
||||
"app_url": service.MustGetString("app_url", ""),
|
||||
@@ -111,14 +154,14 @@ func getAllConfigMappings() gin.H {
|
||||
"show_info_to_server_enable": service.MustGetBool("show_info_to_server_enable", false),
|
||||
"show_protocol_to_server_enable": service.MustGetBool("show_protocol_to_server_enable", false),
|
||||
"default_remind_expire": service.MustGetBool("default_remind_expire", true),
|
||||
"default_remind_traffic" : service.MustGetBool("default_remind_traffic", true),
|
||||
"default_remind_traffic": service.MustGetBool("default_remind_traffic", true),
|
||||
"subscribe_path": service.MustGetString("subscribe_path", "s"),
|
||||
},
|
||||
"frontend": gin.H{
|
||||
"frontend_theme": service.MustGetString("frontend_theme", "Xboard"),
|
||||
"frontend_theme_sidebar": service.MustGetString("frontend_theme_sidebar", "light"),
|
||||
"frontend_theme_header": service.MustGetString("frontend_theme_header", "dark"),
|
||||
"frontend_theme_color": service.MustGetString("frontend_theme_color", "default"),
|
||||
"frontend_theme": service.MustGetString("frontend_theme", "Xboard"),
|
||||
"frontend_theme_sidebar": service.MustGetString("frontend_theme_sidebar", "light"),
|
||||
"frontend_theme_header": service.MustGetString("frontend_theme_header", "dark"),
|
||||
"frontend_theme_color": service.MustGetString("frontend_theme_color", "default"),
|
||||
"frontend_background_url": service.MustGetString("frontend_background_url", ""),
|
||||
},
|
||||
"server": gin.H{
|
||||
@@ -130,18 +173,42 @@ func getAllConfigMappings() gin.H {
|
||||
"server_ws_url": service.MustGetString("server_ws_url", ""),
|
||||
},
|
||||
"safe": gin.H{
|
||||
"email_verify": service.MustGetBool("email_verify", false),
|
||||
"safe_mode_enable": service.MustGetBool("safe_mode_enable", false),
|
||||
"email_verify": service.MustGetBool("email_verify", false),
|
||||
"safe_mode_enable": service.MustGetBool("safe_mode_enable", false),
|
||||
"secure_path": service.GetAdminSecurePath(),
|
||||
"email_whitelist_enable": service.MustGetBool("email_whitelist_enable", false),
|
||||
"email_whitelist_suffix": service.MustGetString("email_whitelist_suffix", ""),
|
||||
"email_gmail_limit_enable": service.MustGetBool("email_gmail_limit_enable", false),
|
||||
"captcha_enable": service.MustGetBool("captcha_enable", false),
|
||||
"captcha_type": service.MustGetString("captcha_type", "recaptcha"),
|
||||
"email_whitelist_enable": service.MustGetBool("email_whitelist_enable", false),
|
||||
"email_whitelist_suffix": service.MustGetString("email_whitelist_suffix", ""),
|
||||
"email_gmail_limit_enable": service.MustGetBool("email_gmail_limit_enable", false),
|
||||
"captcha_enable": service.MustGetBool("captcha_enable", false),
|
||||
"captcha_type": service.MustGetString("captcha_type", "recaptcha"),
|
||||
"register_limit_by_ip_enable": service.MustGetBool("register_limit_by_ip_enable", false),
|
||||
"register_limit_count": service.MustGetInt("register_limit_count", 3),
|
||||
"register_limit_count": service.MustGetInt("register_limit_count", 3),
|
||||
"password_limit_enable": service.MustGetBool("password_limit_enable", true),
|
||||
},
|
||||
"email": gin.H{
|
||||
"email_template": service.MustGetString("email_template", "classic"),
|
||||
"email_host": service.MustGetString("email_host", ""),
|
||||
"email_port": service.MustGetInt("email_port", 465),
|
||||
"email_username": service.MustGetString("email_username", ""),
|
||||
"email_password": service.MustGetString("email_password", ""),
|
||||
"email_encryption": service.MustGetString("email_encryption", ""),
|
||||
"email_from_address": service.LoadEmailConfig().SenderAddress(),
|
||||
"email_from_name": service.MustGetString("email_from_name", service.MustGetString("app_name", "XBoard")),
|
||||
"remind_mail_enable": service.MustGetBool("remind_mail_enable", false),
|
||||
},
|
||||
"nebula": gin.H{
|
||||
"nebula_theme_color": service.MustGetString("nebula_theme_color", "aurora"),
|
||||
"nebula_hero_slogan": service.MustGetString("nebula_hero_slogan", ""),
|
||||
"nebula_welcome_target": service.MustGetString("nebula_welcome_target", ""),
|
||||
"nebula_register_title": service.MustGetString("nebula_register_title", ""),
|
||||
"nebula_background_url": service.MustGetString("nebula_background_url", ""),
|
||||
"nebula_metrics_base_url": service.MustGetString("nebula_metrics_base_url", ""),
|
||||
"nebula_default_theme_mode": service.MustGetString("nebula_default_theme_mode", "system"),
|
||||
"nebula_light_logo_url": service.MustGetString("nebula_light_logo_url", ""),
|
||||
"nebula_dark_logo_url": service.MustGetString("nebula_dark_logo_url", ""),
|
||||
"nebula_custom_html": service.MustGetString("nebula_custom_html", ""),
|
||||
"nebula_static_cdn_url": service.MustGetString("nebula_static_cdn_url", ""),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +222,7 @@ func saveSetting(name string, value any) {
|
||||
case int64:
|
||||
val = strconv.FormatInt(v, 10)
|
||||
case float64:
|
||||
val = fmt.Sprintf("%f", v)
|
||||
val = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
val = "1"
|
||||
@@ -166,19 +233,109 @@ func saveSetting(name string, value any) {
|
||||
// serialize complex types if needed
|
||||
}
|
||||
|
||||
database.DB.Model(&model.Setting{}).Where("name = ?", name).Update("value", val)
|
||||
result := database.DB.Model(&model.Setting{}).Where("name = ?", name).Update("value", val)
|
||||
if result.Error == nil && result.RowsAffected > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
setting := model.Setting{
|
||||
Name: name,
|
||||
Value: val,
|
||||
}
|
||||
if group := settingGroupName(name); group != "" {
|
||||
setting.Group = &group
|
||||
}
|
||||
_ = database.DB.Where("name = ?", name).FirstOrCreate(&setting).Error
|
||||
}
|
||||
|
||||
func listFiles(dir string, pattern string) ([]string, error) {
|
||||
func listDirectoryEntries(dir string) ([]string, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var files []string
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
files = append(files, entry.Name())
|
||||
}
|
||||
files = append(files, entry.Name())
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func collectEmailTemplateNames() []string {
|
||||
names := map[string]struct{}{
|
||||
"classic": {},
|
||||
}
|
||||
|
||||
candidates := []string{
|
||||
filepath.Join("resource", "views", "mail"),
|
||||
filepath.Join("reference", "Xboard", "resources", "views", "mail"),
|
||||
}
|
||||
|
||||
for _, dir := range candidates {
|
||||
entries, err := listDirectoryEntries(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
entry = strings.TrimSpace(entry)
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
names[entry] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(names))
|
||||
for name := range names {
|
||||
result = append(result, name)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func currentAdminEmail(c *gin.Context) string {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
var user model.User
|
||||
if err := database.DB.Select("email").Where("id = ?", intFromAny(userID)).First(&user).Error; err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(user.Email)
|
||||
}
|
||||
|
||||
func settingGroupName(name string) string {
|
||||
switch name {
|
||||
case "invite_force", "invite_commission", "invite_gen_limit", "invite_never_expire",
|
||||
"commission_first_time_enable", "commission_auto_check_enable", "commission_withdraw_limit",
|
||||
"commission_withdraw_method", "withdraw_close_enable", "commission_distribution_enable",
|
||||
"commission_distribution_l1", "commission_distribution_l2", "commission_distribution_l3":
|
||||
return "invite"
|
||||
case "logo", "force_https", "stop_register", "app_name", "app_description", "app_url",
|
||||
"subscribe_url", "try_out_plan_id", "try_out_hour", "tos_url", "currency",
|
||||
"currency_symbol", "ticket_must_wait_reply":
|
||||
return "site"
|
||||
case "plan_change_enable", "reset_traffic_method", "surplus_enable", "new_order_event_id",
|
||||
"renew_order_event_id", "change_order_event_id", "show_info_to_server_enable",
|
||||
"show_protocol_to_server_enable", "default_remind_expire", "default_remind_traffic", "subscribe_path":
|
||||
return "subscribe"
|
||||
case "frontend_theme", "frontend_theme_sidebar", "frontend_theme_header", "frontend_theme_color", "frontend_background_url":
|
||||
return "frontend"
|
||||
case "server_token", "server_pull_interval", "server_push_interval", "device_limit_mode", "server_ws_enable", "server_ws_url":
|
||||
return "server"
|
||||
case "email_verify", "safe_mode_enable", "secure_path", "email_whitelist_enable",
|
||||
"email_whitelist_suffix", "email_gmail_limit_enable", "captcha_enable", "captcha_type",
|
||||
"register_limit_by_ip_enable", "register_limit_count", "password_limit_enable":
|
||||
return "safe"
|
||||
case "email_template", "email_host", "email_port", "email_username", "email_password", "email_encryption",
|
||||
"email_from_address", "email_from_name", "email_from", "remind_mail_enable":
|
||||
return "email"
|
||||
case "nebula_theme_color", "nebula_hero_slogan", "nebula_welcome_target", "nebula_register_title",
|
||||
"nebula_background_url", "nebula_metrics_base_url", "nebula_default_theme_mode",
|
||||
"nebula_light_logo_url", "nebula_dark_logo_url", "nebula_custom_html", "nebula_static_cdn_url":
|
||||
return "nebula"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user