572 lines
14 KiB
Go
572 lines
14 KiB
Go
package models
|
||
|
||
import (
|
||
"bufio"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"io/ioutil"
|
||
"os"
|
||
"regexp"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/beego/beego/v2/client/httplib"
|
||
"github.com/beego/beego/v2/core/logs"
|
||
"github.com/buger/jsonparser"
|
||
)
|
||
|
||
const (
|
||
QL = "ql"
|
||
V4 = "v4"
|
||
LI = "li"
|
||
)
|
||
|
||
type Container struct {
|
||
Type string
|
||
Name string
|
||
Default bool
|
||
Address string
|
||
Username string
|
||
Password string
|
||
Cid string
|
||
Secret string
|
||
Path string
|
||
Version string
|
||
Token string
|
||
Available bool
|
||
Delete []string
|
||
Weigth int
|
||
Mode string
|
||
Reader *bufio.Reader
|
||
Config string
|
||
Limit int
|
||
}
|
||
|
||
func initContainer() {
|
||
for i := range Config.Containers {
|
||
if Config.Containers[i].Weigth == 0 {
|
||
Config.Containers[i].Weigth = 1
|
||
}
|
||
Config.Containers[i].Type = ""
|
||
if Config.Containers[i].Address != "" {
|
||
vv := regexp.MustCompile(`^(https?://[\.\w]+:?\d*)`).FindStringSubmatch(Config.Containers[i].Address)
|
||
if len(vv) == 2 {
|
||
Config.Containers[i].Address = vv[1]
|
||
} else {
|
||
logs.Warn("%s地址错误", Config.Containers[i].Type)
|
||
}
|
||
version, err := GetQlVersion(Config.Containers[i].Address)
|
||
if err == nil {
|
||
if Config.Containers[i].getToken() == nil {
|
||
logs.Info("青龙" + version + "通道登录成功")
|
||
} else {
|
||
logs.Warn("青龙" + version + "通道登录失败")
|
||
}
|
||
Config.Containers[i].Type = "ql"
|
||
Config.Containers[i].Version = version
|
||
} else {
|
||
if err := Config.Containers[i].getSession(); err == nil {
|
||
logs.Info("v系登录成功")
|
||
} else {
|
||
logs.Info("v系登录失败")
|
||
}
|
||
Config.Containers[i].Type = "v4"
|
||
}
|
||
} else if Config.Containers[i].Path != "" {
|
||
f, err := os.Open(Config.Containers[i].Path)
|
||
if err != nil {
|
||
logs.Warn("无法打开%s,请检查路径是否正确", Config.Containers[i].Path)
|
||
} else {
|
||
rd := bufio.NewReader(f)
|
||
for {
|
||
line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
|
||
if err != nil || io.EOF == err {
|
||
break
|
||
}
|
||
if pt := regexp.MustCompile(`^pt_key=`).FindString(line); pt != "" {
|
||
Config.Containers[i].Type = "li"
|
||
break
|
||
}
|
||
if pt := regexp.MustCompile(`^Cookie\d+`).FindString(line); pt != "" {
|
||
Config.Containers[i].Type = "v4"
|
||
break
|
||
}
|
||
if strings.Contains(line, "TempBlockCookie") {
|
||
Config.Containers[i].Type = "v4"
|
||
break
|
||
}
|
||
if strings.Contains(line, "QYWX_KEY") {
|
||
Config.Containers[i].Type = "v4"
|
||
break
|
||
}
|
||
}
|
||
if Config.Containers[i].Type == "" {
|
||
Config.Containers[i].Type = "li"
|
||
}
|
||
f.Close()
|
||
logs.Info(Config.Containers[i].Type + "配置文件正确")
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
func (c *Container) write(cks []JdCookie) error {
|
||
switch c.Type {
|
||
case "ql":
|
||
if c.Version == "openapi" {
|
||
if len(c.Delete) > 0 {
|
||
c.request("/api/envs", DELETE, fmt.Sprintf(`[%s]`, strings.Join(c.Delete, ",")))
|
||
}
|
||
hh := []string{}
|
||
if len(cks) != 0 {
|
||
for _, ck := range cks {
|
||
if ck.Available == True {
|
||
hh = append(hh, fmt.Sprintf(`{"name":"JD_COOKIE","value":"pt_key=%s;pt_pin=%s;","remarks":"%s"}`, ck.PtKey, ck.PtPin, ck.Nickname))
|
||
}
|
||
}
|
||
c.request("/api/envs", POST, fmt.Sprintf(`[%s]`, strings.Join(hh, ",")))
|
||
if Config.IsHelp {
|
||
type AutoGenerated struct {
|
||
Code int `json:"code"`
|
||
Data []struct {
|
||
Value string `json:"value"`
|
||
ID string `json:"_id"`
|
||
Created int64 `json:"created"`
|
||
Status int `json:"status"`
|
||
Timestamp string `json:"timestamp"`
|
||
Position float64 `json:"position"`
|
||
Name string `json:"name"`
|
||
Remarks string `json:"remarks,omitempty"`
|
||
} `json:"data"`
|
||
}
|
||
help := getQLHelp(len(cks))
|
||
for k := range help {
|
||
var data, err = c.request("/api/envs?searchValue=" + k)
|
||
a := AutoGenerated{}
|
||
err = json.Unmarshal(data, &a)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
toDelete := []string{}
|
||
for _, env := range a.Data {
|
||
toDelete = append(toDelete, fmt.Sprintf("\"%s\"", env.ID))
|
||
}
|
||
if len(toDelete) > 0 {
|
||
c.request("/api/envs", DELETE, fmt.Sprintf(`[%s]`, strings.Join(toDelete, ",")))
|
||
}
|
||
}
|
||
for k, v := range help {
|
||
if v == "" {
|
||
v = "&"
|
||
}
|
||
r := map[string]string{
|
||
"name": k,
|
||
"value": v,
|
||
}
|
||
d, _ := json.Marshal(r)
|
||
c.request("/api/envs", POST, fmt.Sprintf(`[%s]`, string(d)))
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if len(c.Delete) > 0 {
|
||
c.request("/api/cookies", DELETE, fmt.Sprintf(`[%s]`, strings.Join(c.Delete, ",")))
|
||
}
|
||
d := []string{}
|
||
for _, ck := range cks {
|
||
if ck.Available == True {
|
||
d = append(d, fmt.Sprintf("\"pt_key=%s;pt_pin=%s;\"", ck.PtKey, ck.PtPin))
|
||
}
|
||
}
|
||
if len(d) != 0 {
|
||
c.request("/api/cookies", POST, fmt.Sprintf(`[%s]`, strings.Join(d, ",")))
|
||
}
|
||
}
|
||
case "v4":
|
||
return c.postConfig(func(config string) string {
|
||
TempBlockCookie := ""
|
||
cookies := ""
|
||
for i, ck := range cks {
|
||
if ck.Available == False {
|
||
TempBlockCookie += fmt.Sprintf("%d ", i+1)
|
||
}
|
||
cookies += fmt.Sprintf("Cookie%d=\"pt_key=%s;pt_pin=%s;\"\n", i+1, ck.PtKey, ck.PtPin)
|
||
}
|
||
config = fmt.Sprintf(`TempBlockCookie="%s"`, TempBlockCookie) + "\n" + cookies + getVhelpRule(len(cks)) + config
|
||
return config
|
||
})
|
||
case "li":
|
||
config := ""
|
||
f, err := os.OpenFile(c.Path, os.O_RDWR|os.O_CREATE, 0777) //打开文件 |os.O_RDWR
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
rd := bufio.NewReader(f)
|
||
for {
|
||
line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
|
||
if err != nil || io.EOF == err {
|
||
break
|
||
}
|
||
if pt := regexp.MustCompile(`^pt_key=(.*);pt_pin=([^'";\s]+);?`).FindStringSubmatch(line); len(pt) != 0 {
|
||
continue
|
||
}
|
||
if pt := regexp.MustCompile(`^pt_key=(.*)`).FindStringSubmatch(line); len(pt) != 0 {
|
||
continue
|
||
}
|
||
config += line
|
||
}
|
||
for _, ck := range cks {
|
||
if ck.PtPin == "" || ck.PtKey == "" {
|
||
continue
|
||
}
|
||
if ck.Available == True {
|
||
config += fmt.Sprintf("pt_key=%s;pt_pin=%s\n", ck.PtKey, ck.PtPin)
|
||
}
|
||
}
|
||
f.Truncate(0)
|
||
f.Seek(0, 0)
|
||
if _, err := io.WriteString(f, config); err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Container) read() error {
|
||
c.Available = true
|
||
switch c.Type {
|
||
case "ql":
|
||
if c.Version == "openapi" {
|
||
type AutoGenerated struct {
|
||
Code int `json:"code"`
|
||
Data []struct {
|
||
Value string `json:"value"`
|
||
ID string `json:"_id"`
|
||
Created int64 `json:"created"`
|
||
Status int `json:"status"`
|
||
Timestamp string `json:"timestamp"`
|
||
Position float64 `json:"position"`
|
||
Name string `json:"name"`
|
||
Remarks string `json:"remarks,omitempty"`
|
||
} `json:"data"`
|
||
}
|
||
var data, err = c.request("/api/envs?searchValue=JD_COOKIE")
|
||
a := AutoGenerated{}
|
||
err = json.Unmarshal(data, &a)
|
||
if err != nil {
|
||
c.Available = false
|
||
return err
|
||
}
|
||
c.Delete = []string{}
|
||
|
||
for _, env := range a.Data {
|
||
c.Delete = append(c.Delete, fmt.Sprintf("\"%s\"", env.ID))
|
||
res := regexp.MustCompile(`pt_key=(\S+);pt_pin=([^\s;]+);?`).FindAllStringSubmatch(env.Value, -1)
|
||
for _, v := range res {
|
||
CheckIn(v[2], v[1])
|
||
}
|
||
}
|
||
return nil
|
||
} else {
|
||
var data, err = c.request("/api/cookies")
|
||
if err != nil {
|
||
c.Available = false
|
||
return err
|
||
}
|
||
type AutoGenerated struct {
|
||
Code int `json:"code"`
|
||
Data []struct {
|
||
Value string `json:"value"`
|
||
ID string `json:"_id"`
|
||
Created int64 `json:"created"`
|
||
Status int `json:"status"`
|
||
Timestamp string `json:"timestamp"`
|
||
Position float64 `json:"position"`
|
||
Nickname string `json:"nickname"`
|
||
} `json:"data"`
|
||
}
|
||
var a = AutoGenerated{}
|
||
c.Delete = []string{}
|
||
json.Unmarshal(data, &a)
|
||
for _, vv := range a.Data {
|
||
c.Delete = append(c.Delete, fmt.Sprintf("\"%s\"", vv.ID))
|
||
res := regexp.MustCompile(`pt_key=(\S+);pt_pin=([^\s;]+);?`).FindStringSubmatch(vv.Value)
|
||
if len(res) == 3 {
|
||
CheckIn(res[2], res[1])
|
||
}
|
||
}
|
||
}
|
||
case "v4":
|
||
return c.getConfig(func(rd *bufio.Reader) string {
|
||
config := ""
|
||
for {
|
||
line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
|
||
if err != nil || io.EOF == err {
|
||
config += line
|
||
break
|
||
}
|
||
if pt := regexp.MustCompile(`^#?\s?Cookie(\d+)=\S+pt_key=(.+);pt_pin=([^'";\s]+);?`).FindStringSubmatch(line); len(pt) != 0 {
|
||
CheckIn(pt[3], pt[2])
|
||
continue
|
||
}
|
||
if pt := regexp.MustCompile(`^ForOther`).FindString(line); pt != "" {
|
||
continue
|
||
}
|
||
if pt := regexp.MustCompile(`^My.*\d+=`).FindString(line); pt != "" {
|
||
continue
|
||
}
|
||
if pt := regexp.MustCompile(`^Cookie\d+`).FindString(line); pt != "" {
|
||
continue
|
||
}
|
||
if pt := regexp.MustCompile(`^TempBlockCookie`).FindString(line); pt != "" {
|
||
continue
|
||
}
|
||
config += line
|
||
}
|
||
return config
|
||
})
|
||
case "li":
|
||
f, err := os.OpenFile(c.Path, os.O_RDWR|os.O_CREATE, 0777) //打开文件 |os.O_RDWR
|
||
if err != nil {
|
||
c.Available = false
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
rd := bufio.NewReader(f)
|
||
for {
|
||
line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
|
||
if err != nil || io.EOF == err {
|
||
break
|
||
}
|
||
if pt := regexp.MustCompile(`^pt_key=(.+);pt_pin=([^'";\s]+);?`).FindStringSubmatch(line); len(pt) != 0 {
|
||
CheckIn(pt[2], pt[1])
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Container) getToken() error {
|
||
version, err := GetQlVersion(c.Address)
|
||
logs.Debug(err)
|
||
if version == "openapi" {
|
||
token := &Token{}
|
||
err, b2 := getT(c, token)
|
||
if b2 {
|
||
c.Token = token.Token
|
||
}
|
||
return err
|
||
} else {
|
||
req := httplib.Post(c.Address + "/api/login")
|
||
req.Header("Content-Type", "application/json;charset=UTF-8")
|
||
req.Body(fmt.Sprintf(`{"username":"%s","password":"%s"}`, c.Username, c.Password))
|
||
if rsp, err := req.Response(); err == nil {
|
||
data, err := ioutil.ReadAll(rsp.Body)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
c.Token, _ = jsonparser.GetString(data, "token")
|
||
if c.Token == "" {
|
||
c.Token, _ = jsonparser.GetString(data, "data", "token")
|
||
}
|
||
} else {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func getT(c *Container, token *Token) (error, bool) {
|
||
logs.Info("获取token")
|
||
req := httplib.Get(c.Address + fmt.Sprintf(`/open/auth/token?client_id=%s&client_secret=%s`, c.Cid, c.Secret))
|
||
req.Header("Content-Type", "application/json;charset=UTF-8")
|
||
if rsp, err := req.Response(); err == nil {
|
||
data, err := ioutil.ReadAll(rsp.Body)
|
||
if err != nil {
|
||
return err, true
|
||
}
|
||
c.Token, _ = jsonparser.GetString(data, "data", "token")
|
||
token.Token, _ = jsonparser.GetString(data, "data", "token")
|
||
zero, _ := time.ParseInLocation("2006-01-02", time.Now().Local().Format("2006-01-02"), time.Local)
|
||
token.Expiration = zero
|
||
token.Address = c.Address
|
||
logs.Info(c.Token + token.Expiration.String())
|
||
} else {
|
||
return err, true
|
||
}
|
||
return nil, false
|
||
}
|
||
|
||
func (c *Container) request(ss ...string) ([]byte, error) {
|
||
var api, method, body string
|
||
for _, s := range ss {
|
||
if s == GET || s == POST || s == PUT || s == DELETE {
|
||
method = s
|
||
} else if strings.Contains(s, "/api/") {
|
||
if c.Version == "openapi" {
|
||
api = strings.ReplaceAll(s, "api", "open")
|
||
} else {
|
||
api = s
|
||
}
|
||
} else {
|
||
body = s
|
||
}
|
||
}
|
||
var req *httplib.BeegoHTTPRequest
|
||
var i = 0
|
||
for {
|
||
i++
|
||
switch method {
|
||
case POST:
|
||
req = httplib.Post(c.Address + api)
|
||
case PUT:
|
||
req = httplib.Put(c.Address + api)
|
||
case DELETE:
|
||
req = httplib.Delete(c.Address + api)
|
||
default:
|
||
req = httplib.Get(c.Address + api)
|
||
}
|
||
req.Header("Authorization", "Bearer "+c.Token)
|
||
if body != "" {
|
||
req.Header("Content-Type", "application/json;charset=UTF-8")
|
||
req.Body(body)
|
||
}
|
||
if data, err := req.Bytes(); err == nil {
|
||
code, _ := jsonparser.GetInt(data, "code")
|
||
if code == 200 {
|
||
return data, nil
|
||
} else {
|
||
logs.Warn(string(data))
|
||
if i >= 5 {
|
||
return nil, errors.New("异常")
|
||
}
|
||
c.getToken()
|
||
}
|
||
}
|
||
}
|
||
return []byte{}, nil
|
||
}
|
||
|
||
func GetQlVersion(address string) (string, error) {
|
||
data, err := httplib.Get(address).String()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
js := regexp.MustCompile(`/umi\.\w+\.js`).FindString(data)
|
||
if js == "" {
|
||
return "", errors.New("好像不是青龙面板")
|
||
}
|
||
data, err = httplib.Get(address + js).String()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
v := ""
|
||
//logs.Info(data)
|
||
if strings.Contains(data, "v2.2") {
|
||
v = "api"
|
||
} else {
|
||
v = "openapi"
|
||
}
|
||
return v, nil
|
||
}
|
||
|
||
const (
|
||
GET = "GET"
|
||
POST = "POST"
|
||
PUT = "PUT"
|
||
DELETE = "DELETE"
|
||
)
|
||
|
||
func (c *Container) getConfig(handle func(*bufio.Reader) string) error {
|
||
if c.Address == "" {
|
||
f, err := os.OpenFile(c.Path, os.O_RDWR|os.O_CREATE, 0777) //打开文件 |os.O_RDWR
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
c.Config = handle(bufio.NewReader(f))
|
||
} else {
|
||
err := c.getSession()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
req := httplib.Get(c.Address + "/api/config/config")
|
||
req.Header("Cookie", c.Token)
|
||
rsp, err := req.Response()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
c.Config = handle(bufio.NewReader(rsp.Body))
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Container) postConfig(handle func(config string) string) error {
|
||
if c.Address == "" {
|
||
f, err := os.OpenFile(c.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
f.WriteString(handle(c.Config))
|
||
} else {
|
||
req := httplib.Post(c.Address + "/api/save")
|
||
req.Header("Cookie", c.Token)
|
||
req.Param("content", handle(c.Config))
|
||
req.Param("name", "config.sh")
|
||
_, err := req.Bytes()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Container) getSession() error {
|
||
|
||
if Config.IsOldV4 {
|
||
req := httplib.Post(c.Address + "/api/auth")
|
||
req.Param("username", c.Username)
|
||
req.Param("password", c.Password)
|
||
rsp, err := req.Response()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
c.Token = rsp.Header.Get("Set-Cookie")
|
||
if data, err := ioutil.ReadAll(rsp.Body); err != nil {
|
||
return err
|
||
} else {
|
||
err, _ := jsonparser.GetInt(data, "err")
|
||
if err != 0 {
|
||
return errors.New(string(data))
|
||
}
|
||
}
|
||
return nil
|
||
} else {
|
||
req := httplib.Post(c.Address + "/auth")
|
||
req.Param("username", c.Username)
|
||
req.Param("password", c.Password)
|
||
rsp, err := req.Response()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
c.Token = rsp.Header.Get("Set-Cookie")
|
||
if data, err := ioutil.ReadAll(rsp.Body); err != nil {
|
||
return err
|
||
} else {
|
||
err, _ := jsonparser.GetInt(data, "err")
|
||
if err != 0 {
|
||
return errors.New(string(data))
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
}
|