mirror of
https://github.com/acepanel/panel.git
synced 2026-02-08 16:34:32 +08:00
feat: api初步支持
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -15,12 +16,12 @@ func Throttle(tokens uint64, interval time.Duration) func(next http.Handler) htt
|
||||
Interval: interval,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Sprintf("failed to create throttle memorystore: %v", err))
|
||||
}
|
||||
|
||||
limiter, err := httplimit.NewMiddleware(store, httplimit.IPKeyFunc())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Sprintf("failed to initialize throttle middleware: %v", err))
|
||||
}
|
||||
|
||||
return limiter.Handle
|
||||
|
||||
55
pkg/api/api.go
Normal file
55
pkg/api/api.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/pkg/copier"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func NewAPI(url ...string) *API {
|
||||
if len(url) == 0 {
|
||||
url = append(url, "https://panel.haozi.net/api")
|
||||
}
|
||||
|
||||
hostInfo, err := host.Info()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to get host info: %v", err))
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
client.SetTimeout(10 * time.Second)
|
||||
client.SetBaseURL(url[0])
|
||||
client.SetHeader("User-Agent", fmt.Sprintf("rat-panel/%s %s/%s", app.Version, hostInfo.Platform, hostInfo.PlatformVersion))
|
||||
|
||||
return &API{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func getResponseData[T any](resp *resty.Response) (*T, error) {
|
||||
raw, ok := resp.Result().(*Response)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get response data: %s", resp.String())
|
||||
}
|
||||
|
||||
res, err := copier.Copy[T](raw.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to copy response data: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
31
pkg/api/api_test.go
Normal file
31
pkg/api/api_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
)
|
||||
|
||||
type APITestSuite struct {
|
||||
suite.Suite
|
||||
api *API
|
||||
}
|
||||
|
||||
func TestAPITestSuite(t *testing.T) {
|
||||
app.Version = "2.3.0"
|
||||
suite.Run(t, &APITestSuite{
|
||||
api: NewAPI(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *APITestSuite) TestGetLatestVersion() {
|
||||
_, err := s.api.GetLatestVersion()
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
func (s *APITestSuite) TestGetVersionsLog() {
|
||||
_, err := s.api.GetIntermediateVersions()
|
||||
s.NoError(err)
|
||||
}
|
||||
44
pkg/api/app.go
Normal file
44
pkg/api/app.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Slug string `json:"slug"`
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Categories []string `json:"categories"`
|
||||
Requires []string `json:"requires"`
|
||||
Excludes []string `json:"excludes"`
|
||||
Versions []struct {
|
||||
Url string `json:"url"`
|
||||
Checksum string `json:"checksum"`
|
||||
PanelVersion string `json:"panel_version"`
|
||||
} `json:"versions"`
|
||||
Order int `json:"order"`
|
||||
}
|
||||
|
||||
type Apps []App
|
||||
|
||||
// GetApps 返回所有应用
|
||||
func (r *API) GetApps() (*Apps, error) {
|
||||
resp, err := r.client.R().SetResult(&Response{}).Get("/apps")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.IsSuccess() {
|
||||
return nil, fmt.Errorf("failed to get apps: %s", resp.String())
|
||||
}
|
||||
|
||||
apps, err := getResponseData[Apps](resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return apps, nil
|
||||
}
|
||||
55
pkg/api/version.go
Normal file
55
pkg/api/version.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type Versions []Version
|
||||
|
||||
// GetLatestVersion 返回最新版本
|
||||
func (r *API) GetLatestVersion() (*Version, error) {
|
||||
resp, err := r.client.R().SetResult(&Response{}).Get("/versions/latest")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.IsSuccess() {
|
||||
return nil, fmt.Errorf("failed to get latest version: %s", resp.String())
|
||||
}
|
||||
|
||||
version, err := getResponseData[Version](resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// GetIntermediateVersions 返回当前版本之后的所有版本
|
||||
func (r *API) GetIntermediateVersions() (*Versions, error) {
|
||||
resp, err := r.client.R().
|
||||
SetQueryParam("start", app.Version).
|
||||
SetResult(&Response{}).Get("/versions/log")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.IsSuccess() {
|
||||
return nil, fmt.Errorf("failed to get latest version: %s", resp.String())
|
||||
}
|
||||
|
||||
versions, err := getResponseData[Versions](resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return versions, nil
|
||||
}
|
||||
18
pkg/copier/copier.go
Normal file
18
pkg/copier/copier.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package copier
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Copy[T any](from any) (*T, error) {
|
||||
to := new(T)
|
||||
b, err := json.Marshal(from)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("copier: marshal data err: %w", err)
|
||||
}
|
||||
if err = json.Unmarshal(b, to); err != nil {
|
||||
return nil, fmt.Errorf("copier: unmarshal data err: %w", err)
|
||||
}
|
||||
return to, nil
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func RandomNumber(length int) string {
|
||||
b := make([]byte, length)
|
||||
n, err := io.ReadAtLeast(rand.Reader, b, length)
|
||||
if n != length {
|
||||
panic(err)
|
||||
panic(fmt.Sprintf("failed to generate random number: %v", err))
|
||||
}
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = table[int(b[i])%len(table)]
|
||||
@@ -37,7 +37,7 @@ func RandomString(length int) string {
|
||||
b := make([]byte, length)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Sprintf("failed to generate random string: %v", err))
|
||||
}
|
||||
letters := "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
for i, v := range b {
|
||||
|
||||
Reference in New Issue
Block a user