- Add hide_from_user_ui on tokens; EnsureUserRelayToken on login and Agnet session - List/search tokens: end-users see only visible keys; admins see all - Add /available-models and sidebar entry; i18n en/zh + locales - desktop download / router hooks if present under heicode/
126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
package controller
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/heicode/manager/common"
|
|
"github.com/heicode/manager/i18n"
|
|
)
|
|
|
|
const envDesktopVersion = "HEICODE_DESKTOP_CLIENT_VERSION"
|
|
|
|
const (
|
|
envWindows = "HEICODE_DESKTOP_FILE_WINDOWS"
|
|
envMacArm = "HEICODE_DESKTOP_FILE_MACOS_ARM64"
|
|
envMacIntel = "HEICODE_DESKTOP_FILE_MACOS_X64"
|
|
envNotes = "HEICODE_DESKTOP_DOWNLOAD_NOTES"
|
|
)
|
|
|
|
type desktopDownloadItem struct {
|
|
ID string `json:"id"`
|
|
Label string `json:"label"`
|
|
Filename string `json:"filename"`
|
|
DownloadURL string `json:"downloadUrl"`
|
|
}
|
|
|
|
type desktopDownloadsPayload struct {
|
|
Version string `json:"version"`
|
|
Notes string `json:"notes,omitempty"`
|
|
Items []desktopDownloadItem `json:"items"`
|
|
}
|
|
|
|
func resolveDesktopPath(envKey string) string {
|
|
p := strings.TrimSpace(os.Getenv(envKey))
|
|
if p == "" {
|
|
return ""
|
|
}
|
|
if abs, err := filepath.Abs(p); err == nil {
|
|
return abs
|
|
}
|
|
return filepath.Clean(p)
|
|
}
|
|
|
|
func safeFileBase(path string) string {
|
|
return filepath.Base(path)
|
|
}
|
|
|
|
// GetDesktopDownloads returns metadata for authenticated users (login required via UserAuth).
|
|
func GetDesktopDownloads(c *gin.Context) {
|
|
version := common.GetEnvOrDefaultString(envDesktopVersion, "")
|
|
if version == "" {
|
|
version = "0.0.0"
|
|
}
|
|
notes := strings.TrimSpace(os.Getenv(envNotes))
|
|
|
|
items := make([]desktopDownloadItem, 0, 3)
|
|
add := func(id, label, envKey string) {
|
|
p := resolveDesktopPath(envKey)
|
|
if p == "" {
|
|
return
|
|
}
|
|
if st, err := os.Stat(p); err != nil || st.IsDir() {
|
|
return
|
|
}
|
|
items = append(items, desktopDownloadItem{
|
|
ID: id,
|
|
Label: label,
|
|
Filename: safeFileBase(p),
|
|
DownloadURL: "/api/user/desktop-downloads/file/" + id,
|
|
})
|
|
}
|
|
add("windows", "Windows (x64)", envWindows)
|
|
add("macos_arm64", "macOS (Apple silicon)", envMacArm)
|
|
add("macos_x64", "macOS (Intel)", envMacIntel)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": desktopDownloadsPayload{
|
|
Version: version,
|
|
Notes: notes,
|
|
Items: items,
|
|
},
|
|
})
|
|
}
|
|
|
|
var desktopPlatformEnv = map[string]string{
|
|
"windows": envWindows,
|
|
"macos_arm64": envMacArm,
|
|
"macos_x64": envMacIntel,
|
|
}
|
|
|
|
// DownloadDesktopFile streams an installer for logged-in users only.
|
|
func DownloadDesktopFile(c *gin.Context) {
|
|
platform := strings.TrimSpace(c.Param("platform"))
|
|
envKey, ok := desktopPlatformEnv[platform]
|
|
if !ok {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"success": false,
|
|
"message": common.TranslateMessage(c, i18n.MsgInvalidParams),
|
|
})
|
|
return
|
|
}
|
|
path := resolveDesktopPath(envKey)
|
|
if path == "" {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"success": false,
|
|
"message": common.TranslateMessage(c, i18n.MsgNotFound),
|
|
})
|
|
return
|
|
}
|
|
st, err := os.Stat(path)
|
|
if err != nil || st.IsDir() {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"success": false,
|
|
"message": common.TranslateMessage(c, i18n.MsgNotFound),
|
|
})
|
|
return
|
|
}
|
|
c.Header("Content-Disposition", "attachment; filename=\""+safeFileBase(path)+"\"")
|
|
c.File(path)
|
|
}
|