feat: add chaoxing support (#1457)
Co-authored-by: Admin <kbtxwer@qq.com>
This commit is contained in:
@ -8,7 +8,9 @@ import (
|
||||
_ "github.com/Xhofe/alist/drivers/alidrive"
|
||||
_ "github.com/Xhofe/alist/drivers/alist"
|
||||
_ "github.com/Xhofe/alist/drivers/baidu"
|
||||
_ "github.com/Xhofe/alist/drivers/baiduphoto"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
_ "github.com/Xhofe/alist/drivers/chaoxing"
|
||||
_ "github.com/Xhofe/alist/drivers/ftp"
|
||||
_ "github.com/Xhofe/alist/drivers/google"
|
||||
_ "github.com/Xhofe/alist/drivers/lanzou"
|
||||
@ -25,7 +27,6 @@ import (
|
||||
_ "github.com/Xhofe/alist/drivers/webdav"
|
||||
_ "github.com/Xhofe/alist/drivers/xunlei"
|
||||
_ "github.com/Xhofe/alist/drivers/yandex"
|
||||
_ "github.com/Xhofe/alist/drivers/baiduphoto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"strings"
|
||||
)
|
||||
|
90
drivers/chaoxing/chaoxing.go
Normal file
90
drivers/chaoxing/chaoxing.go
Normal file
@ -0,0 +1,90 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"path"
|
||||
)
|
||||
|
||||
func LoginOrRefreshToken(account *model.Account) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Request(u string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeaders(map[string]string{
|
||||
"Authorization": "Bearer" + account.AccessToken,
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
})
|
||||
if headers != nil {
|
||||
req.SetHeaders(headers)
|
||||
}
|
||||
if query != nil {
|
||||
req.SetQueryParams(query)
|
||||
}
|
||||
if form != nil {
|
||||
req.SetFormData(form)
|
||||
}
|
||||
if data != nil {
|
||||
req.SetBody(data)
|
||||
}
|
||||
if resp != nil {
|
||||
req.SetResult(resp)
|
||||
}
|
||||
var e Resp
|
||||
var err error
|
||||
var res *resty.Response
|
||||
req.SetError(&e)
|
||||
switch method {
|
||||
case base.Get:
|
||||
res, err = req.Get(u)
|
||||
case base.Post:
|
||||
res, err = req.Post(u)
|
||||
case base.Delete:
|
||||
res, err = req.Delete(u)
|
||||
case base.Patch:
|
||||
res, err = req.Patch(u)
|
||||
case base.Put:
|
||||
res, err = req.Put(u)
|
||||
default:
|
||||
return nil, base.ErrNotSupport
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if e.Code >= 400 {
|
||||
if e.Code == 401 {
|
||||
err = LoginOrRefreshToken(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Request(u, method, headers, query, form, data, resp, account)
|
||||
}
|
||||
return nil, errors.New(e.Message)
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) formatFile(f *File) *model.File {
|
||||
file := model.File{
|
||||
Id: f.Id,
|
||||
Name: f.FileName,
|
||||
Size: f.Size,
|
||||
Driver: driver.Config().Name,
|
||||
UpdatedAt: f.UpdatedAt,
|
||||
}
|
||||
if f.File {
|
||||
file.Type = utils.GetFileType(path.Ext(f.FileName))
|
||||
} else {
|
||||
file.Type = conf.FOLDER
|
||||
}
|
||||
return &file
|
||||
}
|
||||
|
||||
func init() {
|
||||
base.RegisterDriver(&ChaoxingDrive{})
|
||||
}
|
261
drivers/chaoxing/driver.go
Normal file
261
drivers/chaoxing/driver.go
Normal file
@ -0,0 +1,261 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/robfig/cron/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ChaoxingDrive struct {
|
||||
base.Base
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) Config() base.DriverConfig {
|
||||
return base.DriverConfig{
|
||||
Name: "ChaoXing",
|
||||
OnlyProxy: true,
|
||||
OnlyLocal: false,
|
||||
ApiProxy: false,
|
||||
NoNeedSetLink: false,
|
||||
NoCors: false,
|
||||
LocalSort: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) Items() []base.Item {
|
||||
// TODO fill need info
|
||||
return []base.Item{
|
||||
{
|
||||
Name: "username",
|
||||
Label: "手机号/超星号",
|
||||
Type: base.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "password",
|
||||
Label: "enc (加密后的密码)",
|
||||
Type: base.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "root_folder",
|
||||
Label: "根目录id",
|
||||
Type: base.TypeString,
|
||||
Default: "",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "limit",
|
||||
Label: "目录文件上限",
|
||||
Type: base.TypeNumber,
|
||||
Default: "50",
|
||||
Required: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Save 用户更新账号信息
|
||||
func (driver ChaoxingDrive) Save(account *model.Account, old *model.Account) error {
|
||||
// TODO test available or init
|
||||
if old != nil {
|
||||
conf.Cron.Remove(cron.EntryID(old.CronId))
|
||||
}
|
||||
if account == nil {
|
||||
return nil
|
||||
}
|
||||
if account.Limit <= 0 {
|
||||
account.Limit = 50
|
||||
}
|
||||
|
||||
// 登录网盘并取得 Enc 字符串 ( enc保存在 account.AccessSecret 中; cookie 以键值对的形式保存在 account.AccessToken 中)
|
||||
err := driver.GetEnc(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 每隔一天重新获取一次 Enc(和
|
||||
cronId, err := conf.Cron.AddFunc("@every 24h", func() {
|
||||
id := account.ID
|
||||
log.Debugf("ali account id: %d", id)
|
||||
newAccount, err := model.GetAccountById(id)
|
||||
log.Debugf("ali account: %+v", newAccount)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = driver.GetEnc(newAccount)
|
||||
_ = model.SaveAccount(newAccount)
|
||||
})
|
||||
|
||||
// 记录当前计划任务的id
|
||||
account.CronId = int(cronId)
|
||||
err = model.SaveAccount(account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// File 通过用户路径获取到文件对象(主要是id号)
|
||||
func (driver ChaoxingDrive) File(path string, account *model.Account) (*model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
if path == "/" {
|
||||
return &model.File{
|
||||
Id: "",
|
||||
Name: account.Name,
|
||||
Size: 0,
|
||||
Type: conf.FOLDER,
|
||||
Driver: driver.Config().Name,
|
||||
UpdatedAt: account.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
// 将路径分割成 父文件夹 和 文件名
|
||||
dir, name := filepath.Split(path)
|
||||
// 等同于访问上级目录
|
||||
files, err := driver.Files(dir, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.Name == name {
|
||||
return &file, nil
|
||||
}
|
||||
}
|
||||
return nil, base.ErrPathNotFound
|
||||
}
|
||||
|
||||
// Files 列出所有文件
|
||||
func (driver ChaoxingDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
// 先从缓存中获取结果
|
||||
cache, err := base.GetCache(path, account)
|
||||
var fileList []model.File
|
||||
if err == nil {
|
||||
// 缓存命中,将目录信息保存到变量中
|
||||
fileList = cache.([]model.File)
|
||||
} else {
|
||||
// 缓存未命中
|
||||
//尝试获取上级目录的id(递归地尝试从上级目录的缓存中读取信息,直到获取到根目录为止)
|
||||
file, err := driver.File(path, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//列出上级目录的文件
|
||||
fileList, err = driver.ListFile(file.Id, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 缓存数据
|
||||
if len(fileList) > 0 {
|
||||
_ = base.SetCache(path, fileList, account)
|
||||
}
|
||||
}
|
||||
files := make([]model.File, 0)
|
||||
for _, file := range fileList {
|
||||
files = append(files, file)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
var api_file_link = "https://pan-yz.chaoxing.com/download/downloadfile?fleid=%s&puid=1"
|
||||
|
||||
// Link 返回传入路径对应的文件的直链(本地除外),并包含需要携带的请求头
|
||||
func (driver ChaoxingDrive) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||
// TODO get file link
|
||||
file, e := driver.File(args.Path, account)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
url := fmt.Sprintf(api_file_link, file.Id[:strings.Index(file.Id, "_")])
|
||||
//var resp base.Json
|
||||
//var err Resp
|
||||
|
||||
// https://pan-yz.chaoxing.com/download/downloadfile?fleid=582519780768600064&puid=1
|
||||
//_, e = chaoxingClient.R().SetResult(&resp).SetError(&err).
|
||||
// SetHeader("Cookie", account.AccessToken).
|
||||
// SetHeader("Referer", "https://pan-yz.chaoxing.com/").
|
||||
// Get(url)
|
||||
return &base.Link{
|
||||
Headers: []base.Header{
|
||||
{
|
||||
Name: "Referer",
|
||||
Value: "https://pan-yz.chaoxing.com/",
|
||||
},
|
||||
//{
|
||||
// Name: "Cookie",
|
||||
// Value: account.AccessToken,
|
||||
//},
|
||||
},
|
||||
Url: url,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Path 通过调用上述的File与Files函数判断是文件还是文件夹,并进行返回,当是文件时附带文件的直链。
|
||||
func (driver ChaoxingDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
file, err := driver.File(path, account)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !file.IsDir() {
|
||||
return file, nil, nil
|
||||
}
|
||||
files, err := driver.Files(path, account)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil, files, nil
|
||||
}
|
||||
|
||||
// Optional function
|
||||
//func (driver ChaoxingDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||
// //TODO preview interface if driver support
|
||||
// return nil, base.ErrNotImplement
|
||||
//}
|
||||
|
||||
func (driver ChaoxingDrive) MakeDir(path string, account *model.Account) error {
|
||||
// 三个参数缺一不可,均不能写死
|
||||
// https://pan-yz.chaoxing.com/opt/newfolder?parentId=205255741446029312&name=test&puid=54351295
|
||||
|
||||
return base.ErrNotImplement
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) Move(src string, dst string, account *model.Account) error {
|
||||
// 注意 folderid 是由 {id}_{puid} 组成的(截取前两段即可)
|
||||
// https://pan-yz.chaoxing.com/opt/moveres?folderid=762268051373813760_54351295&resids=762263362701209600,
|
||||
return base.ErrNotImplement
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) Rename(src string, dst string, account *model.Account) error {
|
||||
// resid 就是 fileid
|
||||
// https://pan-yz.chaoxing.com/opt/rename?resid=762263362701209600&name=test.pdf&puid=54351295
|
||||
return base.ErrNotImplement
|
||||
}
|
||||
|
||||
// 超星网盘不支持复制
|
||||
//func (driver ChaoxingDrive) Copy(src string, dst string, account *model.Account) error {
|
||||
// //TODO copy file/dir
|
||||
// return base.ErrNotImplement
|
||||
//}
|
||||
|
||||
// Delete 这个函数太危险了,不想实现
|
||||
func (driver ChaoxingDrive) Delete(path string, account *model.Account) error {
|
||||
// 删除单个文件
|
||||
// https://pan-yz.chaoxing.com/opt/delres?resids=762268051373813760&resourcetype=0&puids=54351295
|
||||
// 删除多个文件
|
||||
// https://pan-yz.chaoxing.com/opt/delres?resids=762269933587513344,762269920078848000,&resourcetype=0,0,&puids=54351295,54351295,
|
||||
return base.ErrNotImplement
|
||||
}
|
||||
|
||||
//func (driver ChaoxingDrive) Upload(file *model.FileStream, account *model.Account) error {
|
||||
// //TODO upload file
|
||||
// return base.ErrNotImplement
|
||||
//}
|
||||
|
||||
var _ base.Driver = (*ChaoxingDrive)(nil)
|
27
drivers/chaoxing/types.go
Normal file
27
drivers/chaoxing/types.go
Normal file
@ -0,0 +1,27 @@
|
||||
package template
|
||||
|
||||
import "time"
|
||||
|
||||
// write all struct here
|
||||
|
||||
// TYPE_CX_FILE type=1 文件
|
||||
var TYPE_CX_FILE = int64(1)
|
||||
|
||||
// TYPE_CX_FOLDER type=2 文件夹
|
||||
var TYPE_CX_FOLDER = int64(2)
|
||||
|
||||
// TYPE_CX_SHARED_ROOT type=4 共享文件的根目录
|
||||
var TYPE_CX_SHARED_ROOT = int64(4)
|
||||
|
||||
type Resp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Id string `json:"id"`
|
||||
FileName string `json:"file_name"`
|
||||
Size int64 `json:"size"`
|
||||
File bool `json:"file"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
}
|
151
drivers/chaoxing/util.go
Normal file
151
drivers/chaoxing/util.go
Normal file
@ -0,0 +1,151 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// write util func here, such as cal sign
|
||||
|
||||
var chaoxingClient = resty.New()
|
||||
|
||||
var form_login_fmt = "fid=-1&uname=%s&password=%s&t=true&forbidotherlogin=0&validate=&doubleFactorLogin=0"
|
||||
|
||||
var api_list_root = "https://pan-yz.chaoxing.com/opt/listres?page=1&size=%d&enc=%s"
|
||||
var api_list_file = "https://pan-yz.chaoxing.com/opt/listres?puid=%s&shareid=%s&parentId=%s&page=1&size=%d&enc=%s"
|
||||
var api_list_shared_root = "https://pan-yz.chaoxing.com/opt/listres?puid=0&shareid=-1&parentId=0&page=1&size=%d&enc=%s"
|
||||
|
||||
var reg_enc_fmt = regexp.MustCompile("enc[ ]*=\"(.*)\"")
|
||||
|
||||
func (driver ChaoxingDrive) Login(account *model.Account) error {
|
||||
url := "https://passport2.chaoxing.com/fanyalogin"
|
||||
var resp base.Json
|
||||
var err Resp
|
||||
|
||||
req_body := fmt.Sprintf(form_login_fmt, account.Username, account.Password)
|
||||
|
||||
loginReq, e := chaoxingClient.R().SetBody(req_body).
|
||||
SetResult(&resp).SetError(&err).
|
||||
SetHeader("Content-Length", strconv.FormatInt(int64(len(req_body)), 10)).
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").
|
||||
SetHeader("Host", "passport2.chaoxing.com").
|
||||
SetHeader("User-Agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36").
|
||||
SetHeader("X-Requested-With", "XMLHttpRequest").
|
||||
Post(url)
|
||||
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
account.AccessToken = ""
|
||||
for _, cookie := range loginReq.Cookies() {
|
||||
//route=9d169c0aea4b7c89fa0d073417b5645f;
|
||||
account.AccessToken += fmt.Sprintf("%s=%s; ", cookie.Name, cookie.Value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) GetEnc(account *model.Account) error {
|
||||
url := "https://pan-yz.chaoxing.com/"
|
||||
|
||||
encReq, e := chaoxingClient.R().SetHeader("Cookie", account.AccessToken).Get(url)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
//直接读取响应
|
||||
sresp := string(encReq.Body())
|
||||
submatch := reg_enc_fmt.FindAllStringSubmatch(sresp, 1)
|
||||
//第一次获取失败,可能是未登录
|
||||
if len(submatch) == 0 {
|
||||
e = driver.Login(account)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
encReq, e = chaoxingClient.R().SetHeader("Cookie", account.AccessToken).Get(url)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
sresp = string(encReq.Body())
|
||||
submatch = reg_enc_fmt.FindAllStringSubmatch(sresp, 1)
|
||||
if len(submatch) == 0 {
|
||||
account.Status = "failed"
|
||||
return fmt.Errorf("登录失败,服务器返回信息:%s", sresp)
|
||||
}
|
||||
}
|
||||
enc := submatch[0][1]
|
||||
account.AccessSecret = enc
|
||||
account.Status = "work"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver ChaoxingDrive) ListFile(folder_id string, account *model.Account) ([]model.File, error) {
|
||||
var url string
|
||||
//按规则解析 id 号
|
||||
folder_id_info := strings.Split(folder_id, "_")
|
||||
if len(folder_id_info) == 3 {
|
||||
folder_id = folder_id_info[0]
|
||||
folder_puid := folder_id_info[1]
|
||||
folder_shareid := folder_id_info[2]
|
||||
if folder_id == "0" {
|
||||
//访问“共享给我的文件夹”
|
||||
url = fmt.Sprintf(api_list_shared_root, account.Limit, account.AccessSecret)
|
||||
} else {
|
||||
//访问其他目录
|
||||
url = fmt.Sprintf(api_list_file, folder_puid, folder_shareid, folder_id, account.Limit, account.AccessSecret)
|
||||
}
|
||||
} else {
|
||||
//id无法解析为三段,应当是访问根目录(此时为 "")
|
||||
url = fmt.Sprintf(api_list_root, account.Limit, account.AccessSecret)
|
||||
}
|
||||
|
||||
listFileReq, e := chaoxingClient.R().SetHeader("Cookie", account.AccessToken).Post(url)
|
||||
resp, e := simplejson.NewJson(listFileReq.Body())
|
||||
if e != nil || resp == nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
files := make([]model.File, 0)
|
||||
array, _ := resp.Get("list").Array()
|
||||
|
||||
for _, file := range array {
|
||||
var f = model.File{}
|
||||
file_ := file.(map[string]interface{})
|
||||
//f.Id = file_["id"].(string)
|
||||
f.Id = fmt.Sprintf("%s_%s_%s", file_["id"].(string), file_["puid"].(json.Number).String(), file_["shareid"].(json.Number).String())
|
||||
f.Name = file_["name"].(string)
|
||||
f_server_type, _ := file_["type"].(json.Number).Int64()
|
||||
if f_server_type != TYPE_CX_SHARED_ROOT {
|
||||
f.Size, _ = file_["filesize"].(json.Number).Int64()
|
||||
}
|
||||
// 为文件分配类型
|
||||
switch f_server_type {
|
||||
case TYPE_CX_FILE:
|
||||
{
|
||||
f.Type = utils.GetFileType(file_["suffix"].(string))
|
||||
}
|
||||
case TYPE_CX_FOLDER:
|
||||
f.Type = conf.FOLDER
|
||||
case TYPE_CX_SHARED_ROOT:
|
||||
f.Type = conf.FOLDER
|
||||
}
|
||||
modifyDate, e := time.Parse("2006-01-02 15:04:05", file_["modifyDate"].(string))
|
||||
if e == nil {
|
||||
f.UpdatedAt = &modifyDate
|
||||
}
|
||||
f.Thumbnail = file_["thumbnail"].(string)
|
||||
files = append(files, f)
|
||||
}
|
||||
return files, nil
|
||||
}
|
5
go.mod
5
go.mod
@ -87,3 +87,8 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
)
|
4
go.sum
4
go.sum
@ -1010,3 +1010,7 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
Reference in New Issue
Block a user