Files
GL.iNet-Yongping.Xie 8f21589239 fix(script-info): let GLKVM_ACCESS_IP take priority over the web host
Use the configured IP whenever it is set, regardless of how the UI was
reached, so the device script no longer falls back to HOSTNAME="localhost".

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2026-06-22 21:30:55 -07:00

201 lines
8.7 KiB
Go
Executable File

package http
import (
"net"
"rttys/internal/domain/device"
"rttys/internal/domain/devicelog"
"rttys/internal/domain/notification"
"rttys/internal/domain/permission"
"rttys/internal/domain/user"
"rttys/internal/http/dto"
"rttys/internal/http/handler"
"rttys/internal/http/middleware"
"rttys/internal/store/memory"
"rttys/internal/store/sqlite"
"rttys/xconfig"
"strings"
"github.com/gin-gonic/gin"
)
type Deps struct {
UserSvc *user.Service
PermSvc *permission.Service
DevSvc *device.Service
GroupRepo *sqlite.GroupRepo
SessionStore *memory.SessionStore
RelationsRepo *sqlite.RelationsRepo
TrustedDeviceRepo *sqlite.TrustedDeviceRepo
DeviceLogSvc *devicelog.Service
NotificationSvc *notification.Service
Cfg *xconfig.Config
CloudVersion string
}
func RegisterAPIRoutes(r *gin.Engine, d Deps) {
cfg := d.Cfg
if cfg == nil {
cfg = xconfig.Must()
}
authH := handler.NewAuthHandler(d.UserSvc, d.SessionStore, d.TrustedDeviceRepo)
meH := handler.NewMeHandler()
devH := handler.NewDeviceHandler(d.DevSvc, d.GroupRepo, d.RelationsRepo)
dgH := handler.NewDeviceGroupHandler(d.GroupRepo, d.RelationsRepo)
ugH := handler.NewUserGroupHandler(d.GroupRepo)
relH := handler.NewRelationsHandler(d.RelationsRepo)
userH := handler.NewUserHandler(d.UserSvc, d.GroupRepo, d.RelationsRepo, d.SessionStore)
personalH := handler.NewPersonalHandler(d.UserSvc, d.TrustedDeviceRepo, "GLKVM Cloud")
devLogH := handler.NewDeviceLogHandler(d.DeviceLogSvc)
notifH := handler.NewNotificationHandler(d.NotificationSvc)
// public
r.GET("/auth-config", func(c *gin.Context) {
traceID := middleware.GetTraceID(c)
data := authConfigResp{
LdapEnabled: cfg.LdapEnabled,
LegacyPassword: cfg.Password != "",
OidcEnabled: cfg.OIDCEnabled,
KVMCloudVersion: d.CloudVersion,
}
dto.Write(c, dto.Ok(traceID, data))
})
// public
r.POST("/api/login", authH.Login)
// authed group
api := r.Group("/api")
api.Use(middleware.Auth(d.SessionStore, d.UserSvc, d.PermSvc))
// device script info (requires login)
api.GET("/script-info", func(c *gin.Context) {
traceID := middleware.GetTraceID(c)
// Get domain info
host := c.Request.Host
hostname, _, err := net.SplitHostPort(host)
if err != nil {
hostname = host // Use host directly if no port
}
chosen := hostname
// GLKVM_ACCESS_IP (cfg.WebrtcIP) takes priority whenever it is set,
// regardless of how the web UI was reached (IP or domain/localhost)
// and regardless of reverse-proxy mode. Only fall back to the current
// web host when it is left empty (auto-detect).
if v := strings.TrimSpace(cfg.WebrtcIP); v != "" {
chosen = v
}
// Determine selfhost WebUI URL
webUIURL := strings.TrimSpace(cfg.SelfhostWebUIURL)
if webUIURL == "" {
scheme := "https"
if c.Request.TLS == nil {
scheme = "http"
}
if fwdProto := c.GetHeader("X-Forwarded-Proto"); fwdProto != "" {
scheme = fwdProto
}
webUIURL = scheme + "://" + c.Request.Host
}
data := scriptInfoResp{
Hostname: chosen, // reuse the same chosen value
Port: cfg.AddrDev,
Token: cfg.Token,
WebrtcIP: chosen, // same as hostname
WebrtcPort: cfg.WebrtcPort,
WebrtcUsername: cfg.WebrtcUsername,
WebrtcPassword: cfg.WebrtcPassword,
WebUIURL: webUIURL,
}
dto.Write(c, dto.Ok(traceID, data))
})
// auth
api.POST("/logout", middleware.Require(permission.AuthWrite), authH.Logout)
// me
api.GET("/me", middleware.Require(permission.MeRead), meH.GetMe)
// personal center
api.GET("/me/profile", middleware.Require(permission.MeRead), personalH.GetProfile)
api.PUT("/me/profile", middleware.Require(permission.MeRead), personalH.UpdateProfile)
api.POST("/me/2fa/setup", middleware.Require(permission.MeRead), personalH.Setup2fa)
api.POST("/me/2fa/enable", middleware.Require(permission.MeRead), personalH.Enable2fa)
api.POST("/me/2fa/disable", middleware.Require(permission.MeRead), personalH.Disable2fa)
api.GET("/me/2fa/trusted-devices", middleware.Require(permission.MeRead), personalH.ListTrustedDevices)
api.DELETE("/me/2fa/trusted-devices/:id", middleware.Require(permission.MeRead), personalH.RevokeTrustedDevice)
// device scope list
api.GET("/devices", middleware.Require(permission.DeviceRead), devH.ListDevices)
api.POST("/devices/move-to-device-group", middleware.Require(permission.DeviceGroupWrite), devH.MoveToDeviceGroup)
api.PUT("/devices/:id", middleware.Require(permission.DeviceWrite), devH.UpdateDevice)
api.DELETE("/devices/:id", middleware.Require(permission.DeviceWrite), devH.DeleteDevice)
// --- users ---
api.GET("/users", middleware.Require(permission.UserRead), userH.ListUsers)
api.POST("/users", middleware.Require(permission.UserWrite), userH.CreateUser)
api.PUT("/users/:id", middleware.Require(permission.UserWrite), userH.UpdateUser)
api.DELETE("/users/:id", middleware.Require(permission.UserWrite), userH.DeleteUser)
// user groups
api.GET("/user-groups", middleware.Require(permission.UserGroupRead), ugH.ListUserGroups)
api.GET("/user-groups/options", middleware.Require(permission.UserGroupRead), ugH.ListOptions)
api.POST("/user-groups", middleware.Require(permission.UserGroupWrite), ugH.Create)
api.PUT("/user-groups/:id", middleware.Require(permission.UserGroupWrite), ugH.Update)
api.DELETE("/user-groups/:id", middleware.Require(permission.UserGroupWrite), ugH.Delete)
// device groups list
api.GET("/device-groups", middleware.Require(permission.DeviceGroupRead), dgH.ListDeviceGroups)
api.GET("/device-groups/options", middleware.Require(permission.DeviceGroupRead), dgH.ListOptions)
api.POST("/device-groups", middleware.Require(permission.DeviceGroupWrite), dgH.Create)
api.PUT("/device-groups/:id", middleware.Require(permission.DeviceGroupWrite), dgH.Update)
api.DELETE("/device-groups/:id", middleware.Require(permission.DeviceGroupWrite), dgH.Delete)
api.POST("/device-groups/:id/devices", middleware.Require(permission.DeviceGroupWrite), dgH.AddDevices)
api.DELETE("/device-groups/:id/devices", middleware.Require(permission.DeviceGroupWrite), dgH.RemoveDevices)
// device event logs (admin only)
api.GET("/device-event-logs", middleware.Require(permission.DeviceLogRead), devLogH.List)
// notification settings (admin only)
notifGroup := api.Group("/notification")
notifGroup.GET("/smtp", middleware.Require(permission.NotificationRead), notifH.GetSMTPConfig)
notifGroup.PUT("/smtp", middleware.Require(permission.NotificationWrite), notifH.SaveSMTPConfig)
notifGroup.POST("/smtp/test", middleware.Require(permission.NotificationWrite), notifH.TestSMTP)
notifGroup.GET("/rules", middleware.Require(permission.NotificationRead), notifH.GetNotifyRules)
notifGroup.PUT("/rules", middleware.Require(permission.NotificationWrite), notifH.SaveNotifyRules)
notifGroup.GET("/recipients", middleware.Require(permission.NotificationRead), notifH.ListRecipients)
notifGroup.POST("/recipients", middleware.Require(permission.NotificationWrite), notifH.AddRecipient)
notifGroup.DELETE("/recipients/:id", middleware.Require(permission.NotificationWrite), notifH.RemoveRecipient)
// Relations (cover / set)
api.PUT("/users/:id/user-groups", middleware.Require(permission.UserWrite), relH.SetUserGroups)
api.PUT("/user-groups/:id/device-groups", middleware.Require(permission.UserGroupWrite), relH.SetUserGroupDeviceGroups)
api.PUT("/device-groups/:id/devices", middleware.Require(permission.DeviceGroupWrite), relH.SetDeviceGroupDevices)
}
type authConfigResp struct {
LdapEnabled bool `json:"ldapEnabled"`
LegacyPassword bool `json:"legacyPassword"`
OidcEnabled bool `json:"oidcEnabled"`
KVMCloudVersion string `json:"kvmCloudVersion"`
}
type scriptInfoResp struct {
Hostname string `json:"hostname"`
Port string `json:"port"`
Token string `json:"token"`
WebrtcIP string `json:"webrtcIP"`
WebrtcPort string `json:"webrtcPort"`
WebrtcUsername string `json:"webrtcUsername"`
WebrtcPassword string `json:"webrtcPassword"`
WebUIURL string `json:"webUIURL"`
}