mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 10:45:47 +08:00
feat: add CLI version cache and proxy support (#3565)
* feat: add CLI version cache mechanism * feat: add /api/refresh-engines to allowed endpoints in demo mode * feat: add proxy support for cli downloader * feat: add SafeGoroutine for CLIDownloader initialization * refactor: optimize code structure
This commit is contained in:
parent
7f9f7c6468
commit
5661942175
@ -157,7 +157,7 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
|||||||
|
|
||||||
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||||
if method == "POST" {
|
if method == "POST" {
|
||||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/verify-code" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") || urlPath == "/api/webhook" || urlPath == "/api/get-qrcode" {
|
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/verify-code" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") || urlPath == "/api/webhook" || urlPath == "/api/get-qrcode" || urlPath == "/api/refresh-engines" {
|
||||||
return true
|
return true
|
||||||
} else if urlPath == "/api/update-user" {
|
} else if urlPath == "/api/update-user" {
|
||||||
// Allow ordinary users to update their own information
|
// Allow ordinary users to update their own information
|
||||||
|
@ -23,9 +23,68 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CLIVersionInfo struct {
|
||||||
|
Version string
|
||||||
|
BinaryPath string
|
||||||
|
BinaryTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cliVersionCache = make(map[string]*CLIVersionInfo)
|
||||||
|
cliVersionMutex sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// getCLIVersion
|
||||||
|
// @Title getCLIVersion
|
||||||
|
// @Description Get CLI version with cache mechanism
|
||||||
|
// @Param language string The language of CLI (go/java/rust etc.)
|
||||||
|
// @Return string The version string of CLI
|
||||||
|
// @Return error Error if CLI execution fails
|
||||||
|
func getCLIVersion(language string) (string, error) {
|
||||||
|
binaryName := fmt.Sprintf("casbin-%s-cli", language)
|
||||||
|
|
||||||
|
binaryPath, err := exec.LookPath(binaryName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("executable file not found: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfo, err := os.Stat(binaryPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get binary info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cliVersionMutex.RLock()
|
||||||
|
if info, exists := cliVersionCache[language]; exists {
|
||||||
|
if info.BinaryPath == binaryPath && info.BinaryTime == fileInfo.ModTime() {
|
||||||
|
cliVersionMutex.RUnlock()
|
||||||
|
return info.Version, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cliVersionMutex.RUnlock()
|
||||||
|
|
||||||
|
cmd := exec.Command(binaryName, "--version")
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get CLI version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
version := strings.TrimSpace(string(output))
|
||||||
|
|
||||||
|
cliVersionMutex.Lock()
|
||||||
|
cliVersionCache[language] = &CLIVersionInfo{
|
||||||
|
Version: version,
|
||||||
|
BinaryPath: binaryPath,
|
||||||
|
BinaryTime: fileInfo.ModTime(),
|
||||||
|
}
|
||||||
|
cliVersionMutex.Unlock()
|
||||||
|
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
func processArgsToTempFiles(args []string) ([]string, []string, error) {
|
func processArgsToTempFiles(args []string) ([]string, []string, error) {
|
||||||
tempFiles := []string{}
|
tempFiles := []string{}
|
||||||
newArgs := []string{}
|
newArgs := []string{}
|
||||||
@ -93,6 +152,16 @@ func (c *ApiController) RunCasbinCommand() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 && args[0] == "--version" {
|
||||||
|
version, err := getCLIVersion(language)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.ResponseOk(version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tempFiles, processedArgs, err := processArgsToTempFiles(args)
|
tempFiles, processedArgs, err := processArgsToTempFiles(args)
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, file := range tempFiles {
|
for _, file := range tempFiles {
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -17,6 +16,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/beego/beego"
|
"github.com/beego/beego"
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -108,9 +109,10 @@ func getFinalBinaryName(lang string) string {
|
|||||||
// @Param language string true "Language type"
|
// @Param language string true "Language type"
|
||||||
// @Success 200 {string} string "Download URL and version"
|
// @Success 200 {string} string "Download URL and version"
|
||||||
func getLatestCLIURL(repoURL string, language string) (string, string, error) {
|
func getLatestCLIURL(repoURL string, language string) (string, string, error) {
|
||||||
resp, err := http.Get(repoURL)
|
client := proxy.GetHttpClient(repoURL)
|
||||||
|
resp, err := client.Get(repoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", fmt.Errorf("failed to fetch release info: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -346,7 +348,8 @@ func downloadCLI() error {
|
|||||||
originalPath := filepath.Join(downloadFolder, getBinaryNames()[lang])
|
originalPath := filepath.Join(downloadFolder, getBinaryNames()[lang])
|
||||||
fmt.Printf("downloading %s CLI: %s\n", lang, cliURL)
|
fmt.Printf("downloading %s CLI: %s\n", lang, cliURL)
|
||||||
|
|
||||||
resp, err := http.Get(cliURL)
|
client := proxy.GetHttpClient(cliURL)
|
||||||
|
resp, err := client.Get(cliURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to download %s CLI: %v\n", lang, err)
|
fmt.Printf("failed to download %s CLI: %v\n", lang, err)
|
||||||
continue
|
continue
|
||||||
@ -354,15 +357,27 @@ func downloadCLI() error {
|
|||||||
|
|
||||||
func() {
|
func() {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
out, err := os.Create(originalPath)
|
|
||||||
if err != nil {
|
if err := os.MkdirAll(filepath.Dir(originalPath), 0o755); err != nil {
|
||||||
fmt.Printf("failed to create %s CLI file: %v\n", lang, err)
|
fmt.Printf("failed to create directory for %s CLI: %v\n", lang, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
if _, err = io.Copy(out, resp.Body); err != nil {
|
tmpFile := originalPath + ".tmp"
|
||||||
fmt.Printf("failed to save %s CLI: %v\n", lang, err)
|
out, err := os.Create(tmpFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create or write %s CLI: %v\n", lang, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
out.Close()
|
||||||
|
os.Remove(tmpFile)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = io.Copy(out, resp.Body); err != nil ||
|
||||||
|
out.Close() != nil ||
|
||||||
|
os.Rename(tmpFile, originalPath) != nil {
|
||||||
|
fmt.Printf("failed to download %s CLI: %v\n", lang, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -493,10 +508,12 @@ func InitCLIDownloader() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := DownloadCLI()
|
util.SafeGoroutine(func() {
|
||||||
if err != nil {
|
err := DownloadCLI()
|
||||||
fmt.Printf("failed to initialize CLI downloader: %v\n", err)
|
if err != nil {
|
||||||
}
|
fmt.Printf("failed to initialize CLI downloader: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
go ScheduleCLIUpdater()
|
ScheduleCLIUpdater()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
2
main.go
2
main.go
@ -46,7 +46,7 @@ func main() {
|
|||||||
object.InitCasvisorConfig()
|
object.InitCasvisorConfig()
|
||||||
|
|
||||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||||
controllers.InitCLIDownloader()
|
util.SafeGoroutine(func() { controllers.InitCLIDownloader() })
|
||||||
|
|
||||||
// beego.DelStaticPath("/static")
|
// beego.DelStaticPath("/static")
|
||||||
// beego.SetStaticPath("/static", "web/build/static")
|
// beego.SetStaticPath("/static", "web/build/static")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user