From 845717489160b83a2efd974a5efc86fb7b836746 Mon Sep 17 00:00:00 2001 From: movsb Date: Tue, 17 Dec 2024 19:08:00 +0800 Subject: [PATCH] =?UTF-8?q?API=20=E8=A2=AB=E7=A6=81=E7=94=A8=EF=BC=8C?= =?UTF-8?q?=E6=9A=82=E6=97=B6=E5=88=A0=E9=99=A4=20Hostdare=20=E6=B5=81?= =?UTF-8?q?=E9=87=8F=E7=9B=91=E6=8E=A7=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 有个 js 脚本(被混淆了)运行会生成临时 cf token,请求后会返回 cf_clearance Cookie,目前无法生成。 --- cmd/config/new.go | 16 - cmd/config/type.go | 1 - cmd/server/main.go | 13 - .../metrics/exporters/hostdare/hostdare.go | 282 ------------------ setup/migration/list.go | 1 + setup/migration/versions.go | 5 + 6 files changed, 6 insertions(+), 312 deletions(-) delete mode 100644 modules/metrics/exporters/hostdare/hostdare.go diff --git a/cmd/config/new.go b/cmd/config/new.go index d1906325..df5823d2 100644 --- a/cmd/config/new.go +++ b/cmd/config/new.go @@ -30,19 +30,3 @@ func (c *WhoisApiLayerConfig) BeforeSet(paths Segments, obj any) error { } return nil } - -type VpsConfig struct { - Current CurrentVpsConfig `json:"current" yaml:"current"` - Hostdare HostdareVpsConfig `json:"hostdare" yaml:"hostdare"` -} - -type CurrentVpsConfig string - -func (CurrentVpsConfig) CanSave() {} - -type HostdareVpsConfig struct { - Username string `json:"username" yaml:"username"` - Password string `json:"password" yaml:"password"` -} - -func (HostdareVpsConfig) CanSave() {} diff --git a/cmd/config/type.go b/cmd/config/type.go index 68fccda3..ffebda8a 100644 --- a/cmd/config/type.go +++ b/cmd/config/type.go @@ -19,7 +19,6 @@ type Config struct { // 所以我就在这里定义了针对所有站点适用的自定义样式表(或主题)集合。 Theme ThemeConfig `json:"theme" yaml:"theme"` - VPS VpsConfig `json:"vps" yaml:"vps"` Notify NotificationConfig `json:"notify" yaml:"notify"` } diff --git a/cmd/server/main.go b/cmd/server/main.go index ba877e98..a95fda08 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -25,7 +25,6 @@ import ( "github.com/movsb/taoblog/modules/auth" "github.com/movsb/taoblog/modules/logs" "github.com/movsb/taoblog/modules/metrics" - "github.com/movsb/taoblog/modules/metrics/exporters/hostdare" "github.com/movsb/taoblog/modules/notify" "github.com/movsb/taoblog/modules/utils" "github.com/movsb/taoblog/modules/version" @@ -103,18 +102,6 @@ func (s *Server) Serve(ctx context.Context, testing bool, cfg *config.Config, re var mux = http.NewServeMux() r := metrics.NewRegistry(context.TODO()) mux.Handle(`/v3/metrics`, r.Handler()) // TODO: insecure - switch cfg.VPS.Current { - case `hostdare`: - if hd := cfg.VPS.Hostdare; hd.Username != "" && hd.Password != "" { - exporter, err := hostdare.New(hd.Username, hd.Password) - if err != nil { - log.Println(err) - } else { - r.MustRegister(exporter) - log.Println(`注册 hostdare 指标`) - } - } - } var store theme_fs.FS if path := cfg.Data.File.Path; path == "" { diff --git a/modules/metrics/exporters/hostdare/hostdare.go b/modules/metrics/exporters/hostdare/hostdare.go deleted file mode 100644 index d5af90d9..00000000 --- a/modules/metrics/exporters/hostdare/hostdare.go +++ /dev/null @@ -1,282 +0,0 @@ -package hostdare - -import ( - "context" - "encoding/json" - "fmt" - "log" - "math/rand" - "net/http" - "net/http/cookiejar" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -func New(username, password string) (prometheus.Collector, error) { - hd, err := NewHostDare(username, password) - if err != nil { - return nil, err - } - return NewHostDareExporter(hd), nil -} - -type HostDare struct { - client *http.Client - redirect *url.URL -} - -func NewHostDare(username, password string) (*HostDare, error) { - jar, err := cookiejar.New(nil) - if err != nil { - return nil, err - } - httpClient := &http.Client{ - Jar: jar, - } - hd := &HostDare{ - client: httpClient, - } - if err := hd.login(username, password); err != nil { - return nil, fmt.Errorf(`error logging in: %v`, err) - } - return hd, nil -} - -type LoginResponse struct { - Redirect string `json:"redirect"` -} - -func (hd *HostDare) login(username, password string) error { - values := url.Values{} - values.Set(`username`, username) - values.Set(`password`, password) - values.Set(`login`, `1`) - rsp, err := hd.client.PostForm(`https://vps.hostdare.com/index.php?api=json&act=login`, values) - if err != nil { - return fmt.Errorf(`error posting form: %v`, err) - } - defer rsp.Body.Close() - if rsp.StatusCode != 200 { - return fmt.Errorf(`unexpected status code: %v`, rsp.Status) - } - var lr LoginResponse - if err := json.NewDecoder(rsp.Body).Decode(&lr); err != nil { - return fmt.Errorf(`error decoding response json: %v`, err) - } - if lr.Redirect == `` || !strings.HasPrefix(lr.Redirect, `/sess`) { - return fmt.Errorf(`no valid redirect was returned: %s`, lr.Redirect) - } - ru, err := url.Parse(lr.Redirect) - if err != nil { - return fmt.Errorf(`error parsing redirect url: [%s] %v`, lr.Redirect, err) - } - hd.redirect = ru - return nil -} - -type Status int - -const ( - StatusUp Status = 1 -) - -// buggy: bits not standard compliant, but most time works well. -type IntAsString int64 - -func (i IntAsString) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprint(i)), nil -} - -func (i *IntAsString) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - n, err := strconv.ParseInt(s, 10, 53) - if err != nil { - return err - } - *(*int64)(i) = n - return nil -} - -type ListServersResponse struct { - Counts struct { - VPS IntAsString `json:"vps"` - } `json:"counts"` - Status map[int]Status `json:"status"` - Servers map[int]struct { - ID IntAsString `json:"vpsid"` - HostName string `json:"hostname"` - } `json:"vs"` -} - -// 不支持分页。 -func (hd *HostDare) ListServers() (*ListServersResponse, error) { - u, err := url.Parse(`https://vps.hostdare.com/sessXXX/index.php?api=json&act=listvs&&random=0.2665219561593345`) - if err != nil { - return nil, fmt.Errorf(`invalid reference url: %v`, err) - } - u.Path = hd.redirect.Path - v := u.Query() - v.Set(`random`, fmt.Sprint(rand.Float32())) - u.RawQuery = v.Encode() - - rsp, err := hd.client.Get(u.String()) - if err != nil { - return nil, fmt.Errorf(`error listing servers: %v`, err) - } - defer rsp.Body.Close() - if rsp.StatusCode != http.StatusOK { - return nil, fmt.Errorf(`invalid status code: %v`, rsp.Status) - } - - var lsr ListServersResponse - if err := json.NewDecoder(rsp.Body).Decode(&lsr); err != nil { - return nil, fmt.Errorf(`error parsing json: %v`, err) - } - - return &lsr, nil -} - -type StatsServerResponse struct { - Info struct { - ID IntAsString `json:"vpsid"` - Bandwidth struct { - Limit float64 `json:"limit"` - Used float64 `json:"used"` - // Free float64 `json:"free"` - Usage map[int]float64 `json:"usage"` - In map[int]float64 `json:"in"` - Out map[int]float64 `json:"out"` - } `json:"bandwidth"` - } `json:"info"` -} - -// https://vps.hostdare.com/sessXXX/index.php?api=json&act=vpsmanage&stats=1&svs=43546 -func (hd *HostDare) Stats(vpsID int) (*StatsServerResponse, error) { - u, err := url.Parse(`https://vps.hostdare.com/sessXXX/index.php?api=json&act=vpsmanage&stats=1&svs=43546&random=0.2665219561593345`) - if err != nil { - return nil, fmt.Errorf(`invalid reference url: %v`, err) - } - u.Path = hd.redirect.Path - v := u.Query() - v.Set(`random`, fmt.Sprint(rand.Float32())) - v.Set(`svs`, fmt.Sprint(vpsID)) - u.RawQuery = v.Encode() - - rsp, err := hd.client.Get(u.String()) - if err != nil { - return nil, fmt.Errorf(`error listing servers: %v`, err) - } - defer rsp.Body.Close() - if rsp.StatusCode != http.StatusOK { - return nil, fmt.Errorf(`invalid status code: %v`, rsp.Status) - } - - var ssr StatsServerResponse - if err := json.NewDecoder(rsp.Body).Decode(&ssr); err != nil { - return nil, fmt.Errorf(`error parsing json: %v`, err) - } - - return &ssr, nil -} - -type HostDareExporter struct { - hd *HostDare - - bandwidthQuota *prometheus.GaugeVec - bandwidthUsed *prometheus.GaugeVec - - lock sync.Mutex -} - -func NewHostDareExporter(hd *HostDare) *HostDareExporter { - hde := HostDareExporter{ - hd: hd, - } - - hde.bandwidthQuota = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: `vps`, - Subsystem: `bandwidth`, - Name: `quota`, - Help: `Bandwidth quota`, - }, - []string{`vendor`, `name`}, - ) - hde.bandwidthUsed = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: `vps`, - Subsystem: `bandwidth`, - Name: `used`, - Help: `Bandwidth used`, - }, - []string{`vendor`, `name`}, - ) - - go hde.run(context.TODO()) - - return &hde -} - -func (hde *HostDareExporter) updateStats() error { - lsr, err := hde.hd.ListServers() - if err != nil { - return err - } - - stats := []*StatsServerResponse{} - for _, s := range lsr.Servers { - stat, err := hde.hd.Stats(int(s.ID)) - if err != nil { - log.Println(`error updating:`, err) - return err - } - stats = append(stats, stat) - } - - for _, server := range lsr.Servers { - for _, stat := range stats { - if server.ID == stat.Info.ID { - hde.bandwidthQuota.WithLabelValues(`hostdare`, server.HostName).Set(stat.Info.Bandwidth.Limit * (1 << 20)) - hde.bandwidthUsed.WithLabelValues(`hostdare`, server.HostName).Set(stat.Info.Bandwidth.Used * (1 << 20)) - } - } - } - - return nil -} - -func (hde *HostDareExporter) Describe(w chan<- *prometheus.Desc) { - hde.bandwidthQuota.Describe(w) - hde.bandwidthUsed.Describe(w) -} - -func (hde *HostDareExporter) run(ctx context.Context) { - time.Sleep(time.Second * 10) - hde.updateStats() - for { - select { - case <-ctx.Done(): - return - case <-time.After(time.Minute * 3): - if err := hde.updateStats(); err != nil { - log.Println(`error describing:`, err) - } - } - } -} - -func (hde *HostDareExporter) Collect(w chan<- prometheus.Metric) { - hde.lock.Lock() - defer hde.lock.Unlock() - - hde.bandwidthQuota.Collect(w) - hde.bandwidthUsed.Collect(w) -} diff --git a/setup/migration/list.go b/setup/migration/list.go index 93640e7a..9a1f1707 100644 --- a/setup/migration/list.go +++ b/setup/migration/list.go @@ -42,6 +42,7 @@ var gVersions = []VersionUpdater{ {28, v28}, {29, v29}, {30, v30}, + {31, v31}, } // MaxVersionNumber ... diff --git a/setup/migration/versions.go b/setup/migration/versions.go index 3ec6b906..0935f3b3 100644 --- a/setup/migration/versions.go +++ b/setup/migration/versions.go @@ -472,3 +472,8 @@ func v30(tx *sql.Tx) { mustExec(tx, "ALTER TABLE comments ADD COLUMN `date_timezone` TEXT NOT NULL DEFAULT ''") mustExec(tx, "ALTER TABLE comments ADD COLUMN `modified_timezone` TEXT NOT NULL DEFAULT ''") } + +func v31(tx *sql.Tx) { + mustExec(tx, `DELETE FROM options WHERE name = 'vps.hostdare'`) + mustExec(tx, `DELETE FROM options WHERE name = 'vps.current'`) +}