Files
glkvm-cloud/internal/http/router.go
T
GL.iNet-Yongping.Xie b4366176e1 fix(api): limit script-info to users who may add devices
The endpoint returns the device registration token and the TURN
credentials, but only required a login. Gate it on device.write, which
is what the "add device" entry in the UI already checks.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2026-10-14 00:38:39 -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 (admin only: it returns the device token and TURN credentials)
api.GET("/script-info", middleware.Require(permission.DeviceWrite), 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"`
}