2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-08 16:34:32 +08:00

feat: api初步支持

This commit is contained in:
耗子
2024-09-18 19:02:49 +08:00
parent 615744e0cf
commit ef2ecfcc80
7 changed files with 208 additions and 4 deletions

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
}

View File

@@ -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 {