mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-13 10:00:53 +08:00
代码同步
This commit is contained in:
@@ -41,7 +41,7 @@ func UploadCert(c *gin.Context) {
|
||||
}
|
||||
form.Key = strings.TrimSpace(form.Key)
|
||||
form.Cert = strings.TrimSpace(form.Cert)
|
||||
|
||||
|
||||
if form.Key == "" {
|
||||
public.FailMsg(c, "名称不能为空")
|
||||
return
|
||||
@@ -50,12 +50,12 @@ func UploadCert(c *gin.Context) {
|
||||
public.FailMsg(c, "类型不能为空")
|
||||
return
|
||||
}
|
||||
err = cert.UploadCert(form.Key, form.Cert)
|
||||
sha256, err := cert.UploadCert(form.Key, form.Cert)
|
||||
if err != nil {
|
||||
public.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
public.SuccessMsg(c, "添加成功")
|
||||
public.SuccessData(c, sha256, 0)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func DelCert(c *gin.Context) {
|
||||
|
||||
func DownloadCert(c *gin.Context) {
|
||||
ID := c.Query("id")
|
||||
|
||||
|
||||
if ID == "" {
|
||||
public.FailMsg(c, "ID不能为空")
|
||||
return
|
||||
@@ -93,11 +93,11 @@ func DownloadCert(c *gin.Context) {
|
||||
public.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 构建 zip 包(内存中)
|
||||
buf := new(bytes.Buffer)
|
||||
zipWriter := zip.NewWriter(buf)
|
||||
|
||||
|
||||
for filename, content := range certData {
|
||||
if filename == "cert" || filename == "key" {
|
||||
writer, err := zipWriter.Create(filename + ".pem")
|
||||
@@ -118,10 +118,10 @@ func DownloadCert(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
// 设置响应头
|
||||
|
||||
|
||||
zipName := strings.ReplaceAll(certData["domains"], ".", "_")
|
||||
zipName = strings.ReplaceAll(zipName, ",", "-")
|
||||
|
||||
|
||||
c.Header("Content-Type", "application/zip")
|
||||
c.Header("Content-Disposition", "attachment; filename="+zipName+".zip")
|
||||
c.Data(200, "application/zip", buf.Bytes())
|
||||
|
||||
@@ -39,18 +39,18 @@ func GetDNSProvider(providerName string, creds map[string]string) (challenge.Pro
|
||||
config.SecretID = creds["secret_id"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
return tencentcloud.NewDNSProviderConfig(config)
|
||||
|
||||
|
||||
// case "cloudflare":
|
||||
// config := cloudflare.NewDefaultConfig()
|
||||
// config.AuthToken = creds["CLOUDFLARE_API_TOKEN"]
|
||||
// return cloudflare.NewDNSProviderConfig(config)
|
||||
|
||||
|
||||
case "aliyun":
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.APIKey = creds["access_key"]
|
||||
config.SecretKey = creds["access_secret"]
|
||||
return alidns.NewDNSProviderConfig(config)
|
||||
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
|
||||
email, ok := cfg["email"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:email")
|
||||
@@ -84,7 +84,11 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("参数错误:provider_id")
|
||||
}
|
||||
|
||||
domainArr := strings.Split(domains, ",")
|
||||
for i := range domainArr {
|
||||
domainArr[i] = strings.TrimSpace(domainArr[i])
|
||||
}
|
||||
|
||||
// 获取上次申请的证书
|
||||
runId, ok := cfg["_runId"].(string)
|
||||
if !ok {
|
||||
@@ -114,11 +118,17 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
var maxDays float64
|
||||
var maxItem map[string]any
|
||||
for i := range certs {
|
||||
if !public.ContainsAllIgnoreBRepeats(strings.Split(certs[i]["domains"].(string), ","), domainArr) {
|
||||
continue
|
||||
}
|
||||
endTimeStr, ok := certs[i]["end_time"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
endTime, _ := time.Parse(layout, endTimeStr)
|
||||
endTime, err := time.Parse(layout, endTimeStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
diff := endTime.Sub(time.Now()).Hours() / 24
|
||||
if diff > maxDays {
|
||||
maxDays = diff
|
||||
@@ -131,10 +141,10 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
if !ok || cfgEnd <= 0 {
|
||||
cfgEnd = 30
|
||||
}
|
||||
|
||||
|
||||
if int(maxDays) > cfgEnd {
|
||||
// 证书未过期,直接返回
|
||||
logger.Debug(fmt.Sprintf("上次证书申请成功,剩余天数:%d 大于%d天,已跳过申请复用此证书", int(maxDays), cfgEnd))
|
||||
logger.Debug(fmt.Sprintf("上次证书申请成功,域名:%s,剩余天数:%d 大于%d天,已跳过申请复用此证书", certObj["domains"], int(maxDays), cfgEnd))
|
||||
return map[string]any{
|
||||
"cert": certObj["cert"],
|
||||
"key": certObj["key"],
|
||||
@@ -145,7 +155,7 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
}
|
||||
}
|
||||
logger.Debug("正在申请证书,域名: " + domains)
|
||||
|
||||
|
||||
user, err := LoadUserFromDB(db, email)
|
||||
if err != nil {
|
||||
logger.Debug("acme账号不存在,注册新账号")
|
||||
@@ -154,10 +164,10 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
|
||||
config := lego.NewConfig(user)
|
||||
config.Certificate.KeyType = certcrypto.EC384
|
||||
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -168,14 +178,14 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
|
||||
err = SaveUserToDB(db, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("账号注册并保存成功")
|
||||
}
|
||||
|
||||
|
||||
// 初始化 ACME 客户端
|
||||
client, err := lego.NewClient(lego.NewConfig(user))
|
||||
if err != nil {
|
||||
@@ -196,13 +206,13 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// DNS 验证
|
||||
provider, err := GetDNSProvider(providerStr, providerConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 DNS provider 失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
|
||||
// 跳过预检查
|
||||
@@ -215,29 +225,29 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// fmt.Println(strings.Split(domains, ","))
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: strings.Split(domains, ","),
|
||||
Domains: domainArr,
|
||||
Bundle: true,
|
||||
}
|
||||
certObj, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
certStr := string(certObj.Certificate)
|
||||
keyStr := string(certObj.PrivateKey)
|
||||
issuerCertStr := string(certObj.IssuerCertificate)
|
||||
|
||||
|
||||
// 保存证书和私钥
|
||||
data := map[string]any{
|
||||
"cert": certStr,
|
||||
"key": keyStr,
|
||||
"issuerCert": issuerCertStr,
|
||||
}
|
||||
|
||||
err = cert.SaveCert("workflow", keyStr, certStr, issuerCertStr, runId)
|
||||
|
||||
_, err = cert.SaveCert("workflow", keyStr, certStr, issuerCertStr, runId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
@@ -35,7 +35,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("domains like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("domains like ?", []interface{}{"%" + search + "%"}).Limit(limits).Order("create_time", "desc").Select()
|
||||
@@ -80,7 +80,7 @@ func AddCert(source, key, cert, issuer, issuerCert, domains, sha256, historyId,
|
||||
workflowId = wh[0]["workflow_id"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Insert(map[string]any{
|
||||
"source": source,
|
||||
@@ -104,40 +104,40 @@ func AddCert(source, key, cert, issuer, issuerCert, domains, sha256, historyId,
|
||||
return nil
|
||||
}
|
||||
|
||||
func SaveCert(source, key, cert, issuerCert, historyId string) error {
|
||||
func SaveCert(source, key, cert, issuerCert, historyId string) (string, error) {
|
||||
if err := public.ValidateSSLCertificate(cert, key); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
certObj, err := public.ParseCertificate([]byte(cert))
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析证书失败: %v", err)
|
||||
return "", fmt.Errorf("解析证书失败: %v", err)
|
||||
}
|
||||
// SHA256
|
||||
sha256, err := public.GetSHA256(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 SHA256 失败: %v", err)
|
||||
return "", fmt.Errorf("获取 SHA256 失败: %v", err)
|
||||
}
|
||||
if d, _ := GetCert(sha256); d != nil {
|
||||
return nil
|
||||
return sha256, nil
|
||||
}
|
||||
|
||||
|
||||
domainSet := make(map[string]bool)
|
||||
|
||||
|
||||
if certObj.Subject.CommonName != "" {
|
||||
domainSet[certObj.Subject.CommonName] = true
|
||||
}
|
||||
for _, dns := range certObj.DNSNames {
|
||||
domainSet[dns] = true
|
||||
}
|
||||
|
||||
|
||||
// 转成切片并拼接成逗号分隔的字符串
|
||||
var domains []string
|
||||
for domain := range domainSet {
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
domainList := strings.Join(domains, ",")
|
||||
|
||||
|
||||
// 提取 CA 名称(Issuer 的组织名)
|
||||
caName := "UNKNOWN"
|
||||
if len(certObj.Issuer.Organization) > 0 {
|
||||
@@ -149,20 +149,20 @@ func SaveCert(source, key, cert, issuerCert, historyId string) error {
|
||||
startTime := certObj.NotBefore.Format("2006-01-02 15:04:05")
|
||||
endTime := certObj.NotAfter.Format("2006-01-02 15:04:05")
|
||||
endDay := fmt.Sprintf("%d", int(certObj.NotAfter.Sub(time.Now()).Hours()/24))
|
||||
|
||||
|
||||
err = AddCert(source, key, cert, caName, issuerCert, domainList, sha256, historyId, startTime, endTime, endDay)
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存证书失败: %v", err)
|
||||
return "", fmt.Errorf("保存证书失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
return sha256, nil
|
||||
}
|
||||
|
||||
func UploadCert(key, cert string) error {
|
||||
err := SaveCert("upload", key, cert, "", "")
|
||||
func UploadCert(key, cert string) (string, error) {
|
||||
sha256, err := SaveCert("upload", key, cert, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存证书失败: %v", err)
|
||||
return sha256, fmt.Errorf("保存证书失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
return sha256, nil
|
||||
}
|
||||
|
||||
func DelCert(id string) error {
|
||||
@@ -171,7 +171,7 @@ func DelCert(id string) error {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -185,7 +185,7 @@ func GetCert(id string) (map[string]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
res, err := s.Where("id=? or sha256=?", []interface{}{id, id}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -193,13 +193,13 @@ func GetCert(id string) (map[string]string, error) {
|
||||
if len(res) == 0 {
|
||||
return nil, fmt.Errorf("证书不存在")
|
||||
}
|
||||
|
||||
|
||||
data := map[string]string{
|
||||
"domains": res[0]["domains"].(string),
|
||||
"cert": res[0]["cert"].(string),
|
||||
"key": res[0]["key"].(string),
|
||||
}
|
||||
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
@@ -50,11 +51,12 @@ func Request1panel(data *map[string]any, method, providerID, requestUrl string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if providerConfig["url"][len(providerConfig["url"])-1:] != "/" {
|
||||
providerConfig["url"] += "/"
|
||||
parsedURL, err := url.Parse(providerConfig["url"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, providerConfig["url"]+requestUrl, bytes.NewBuffer(jsonData))
|
||||
baseURL := fmt.Sprintf("%s://%s/", parsedURL.Scheme, parsedURL.Host)
|
||||
req, err := http.NewRequest(method, baseURL+requestUrl, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
// fmt.Println(err)
|
||||
return nil, err
|
||||
|
||||
@@ -41,14 +41,17 @@ func RequestBt(data *url.Values, method, providerID, requestUrl string) (map[str
|
||||
}
|
||||
timestamp := time.Now().Unix()
|
||||
token := generateSignature(fmt.Sprintf("%d", timestamp), providerConfig["api_key"])
|
||||
if providerConfig["url"][len(providerConfig["url"])-1:] != "/" {
|
||||
providerConfig["url"] += "/"
|
||||
}
|
||||
|
||||
data.Set("request_time", fmt.Sprintf("%d", timestamp))
|
||||
data.Set("request_token", token)
|
||||
|
||||
req, err := http.NewRequest(method, providerConfig["url"]+requestUrl, strings.NewReader(data.Encode()))
|
||||
parsedURL, err := url.Parse(providerConfig["url"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseURL := fmt.Sprintf("%s://%s/", parsedURL.Scheme, parsedURL.Host)
|
||||
|
||||
req, err := http.NewRequest(method, baseURL+requestUrl, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -112,7 +115,7 @@ func DeployBt(cfg map[string]any) error {
|
||||
data.Set("cert_type", "1")
|
||||
data.Set("privateKey", keyPem)
|
||||
data.Set("certPem", certPem)
|
||||
_, err := RequestBt(&data, "POST", providerID, "/config?action=SetPanelSSL")
|
||||
_, err := RequestBt(&data, "POST", providerID, "config?action=SetPanelSSL")
|
||||
if err != nil {
|
||||
return fmt.Errorf("证书部署失败: %v", err)
|
||||
}
|
||||
@@ -150,7 +153,7 @@ func DeployBtSite(cfg map[string]any) error {
|
||||
data.Set("key", keyPem)
|
||||
data.Set("csr", certPem)
|
||||
data.Set("siteName", siteName)
|
||||
_, err := RequestBt(&data, "POST", providerID, "/site?action=SetSSL")
|
||||
_, err := RequestBt(&data, "POST", providerID, "site?action=SetSSL")
|
||||
if err != nil {
|
||||
return fmt.Errorf("证书部署失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -37,8 +37,9 @@ func Deploy(cfg map[string]any, logger *public.Logger) error {
|
||||
case "aliyun-cdn":
|
||||
logger.Debug("部署到阿里云CDN...")
|
||||
return DeployAliCdn(cfg)
|
||||
// case "aliyun-oss":
|
||||
|
||||
case "aliyun-oss":
|
||||
logger.Debug("部署到阿里云OSS...")
|
||||
return DeployOss(cfg)
|
||||
default:
|
||||
return fmt.Errorf("不支持的部署: %s", providerName)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ type SSHConfig struct {
|
||||
Password string // 可选
|
||||
PrivateKey string // 可选
|
||||
Host string
|
||||
Port string
|
||||
Port float64
|
||||
}
|
||||
|
||||
type RemoteFile struct {
|
||||
@@ -24,7 +24,7 @@ type RemoteFile struct {
|
||||
|
||||
func buildAuthMethods(password, privateKey string) ([]ssh.AuthMethod, error) {
|
||||
var methods []ssh.AuthMethod
|
||||
|
||||
|
||||
if privateKey != "" {
|
||||
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||
if err != nil {
|
||||
@@ -32,71 +32,71 @@ func buildAuthMethods(password, privateKey string) ([]ssh.AuthMethod, error) {
|
||||
}
|
||||
methods = append(methods, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
|
||||
if password != "" {
|
||||
methods = append(methods, ssh.Password(password))
|
||||
}
|
||||
|
||||
|
||||
if len(methods) == 0 {
|
||||
return nil, fmt.Errorf("no authentication methods provided")
|
||||
}
|
||||
|
||||
|
||||
return methods, nil
|
||||
}
|
||||
|
||||
func writeMultipleFilesViaSSH(config SSHConfig, files []RemoteFile, preCmd, postCmd string) error {
|
||||
addr := fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", config.Host, int(config.Port))
|
||||
|
||||
authMethods, err := buildAuthMethods(config.Password, config.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: config.User,
|
||||
Auth: authMethods,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
|
||||
client, err := ssh.Dial("tcp", addr, sshConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to dial: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("会话创建失败: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
|
||||
var script bytes.Buffer
|
||||
|
||||
|
||||
if preCmd != "" {
|
||||
script.WriteString(preCmd + " && ")
|
||||
}
|
||||
|
||||
|
||||
for i, file := range files {
|
||||
if i > 0 {
|
||||
script.WriteString(" && ")
|
||||
}
|
||||
|
||||
|
||||
dirCmd := fmt.Sprintf("mkdir -p $(dirname %q)", file.Path)
|
||||
writeCmd := fmt.Sprintf("printf %%s '%s' > %s", file.Content, file.Path)
|
||||
|
||||
|
||||
script.WriteString(dirCmd + " && " + writeCmd)
|
||||
}
|
||||
|
||||
|
||||
if postCmd != "" {
|
||||
script.WriteString(" && " + postCmd)
|
||||
}
|
||||
|
||||
|
||||
cmd := script.String()
|
||||
|
||||
|
||||
if err := session.Run(cmd); err != nil {
|
||||
return fmt.Errorf("运行出错: %v", err)
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -127,17 +127,17 @@ func DeploySSH(cfg map[string]any) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("参数错误:keyPath")
|
||||
}
|
||||
certPath, ok := cfg["keyPath"].(string)
|
||||
certPath, ok := cfg["certPath"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("参数错误:certPath")
|
||||
}
|
||||
beforeCmd, ok := cfg["beforeCmd"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("参数错误:beforeCmd")
|
||||
beforeCmd = ""
|
||||
}
|
||||
afterCmd, ok := cfg["afterCmd"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("参数错误:afterCmd")
|
||||
afterCmd = ""
|
||||
}
|
||||
providerData, err := access.GetAccess(providerID)
|
||||
if err != nil {
|
||||
@@ -155,8 +155,8 @@ func DeploySSH(cfg map[string]any) error {
|
||||
}
|
||||
// 自动创建多级目录
|
||||
files := []RemoteFile{
|
||||
{Path: keyPath, Content: certPem},
|
||||
{Path: certPath, Content: keyPem},
|
||||
{Path: certPath, Content: certPem},
|
||||
{Path: keyPath, Content: keyPem},
|
||||
}
|
||||
err = writeMultipleFilesViaSSH(providerConfig, files, beforeCmd, afterCmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,7 +15,7 @@ func GetWorkflowCount() (map[string]any, error) {
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
workflow, err := s.Query(`select count(*) as count,
|
||||
count(case when active=1 then 1 end ) as active,
|
||||
count(case when exec_type='auto' then 1 end ) as active,
|
||||
count(case when last_run_status='fail' then 1 end ) as failure
|
||||
from workflow
|
||||
`)
|
||||
@@ -113,9 +113,9 @@ func GetWorkflowHistory() ([]map[string]any, error) {
|
||||
}
|
||||
switch v["exec_type"] {
|
||||
case "manual":
|
||||
mode = "手动触发"
|
||||
mode = "手动"
|
||||
case "auto":
|
||||
mode = "定时触发"
|
||||
mode = "自动"
|
||||
}
|
||||
wk, err := s.Where("id=?", []interface{}{v["workflow_id"]}).Select()
|
||||
if err != nil {
|
||||
@@ -126,7 +126,7 @@ func GetWorkflowHistory() ([]map[string]any, error) {
|
||||
} else {
|
||||
name = "未知"
|
||||
}
|
||||
|
||||
|
||||
result = append(result, map[string]any{
|
||||
"name": name,
|
||||
"state": state,
|
||||
|
||||
@@ -1,189 +1,206 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jordan-wright/email"
|
||||
"net/smtp"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetSqlite() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "report"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
var data []map[string]any
|
||||
var count int64
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
if p > 1 {
|
||||
limits[0] = (p - 1) * limit
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Limit(limits).Order("update_time", "desc").Select()
|
||||
} else {
|
||||
count, err = s.Count()
|
||||
data, err = s.Order("update_time", "desc").Limit(limits).Select()
|
||||
}
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
return data, int(count), nil
|
||||
}
|
||||
|
||||
func GetReport(id string) (map[string]any, error) {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
data, err := s.Where("id=?", []interface{}{id}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("没有找到此通知配置")
|
||||
}
|
||||
return data[0], nil
|
||||
|
||||
}
|
||||
|
||||
func AddReport(Type, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Insert(map[string]interface{}{
|
||||
"name": name,
|
||||
"type": Type,
|
||||
"config": config,
|
||||
"create_time": now,
|
||||
"update_time": now,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdReport(id, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]interface{}{
|
||||
"name": name,
|
||||
"config": config,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func DelReport(id string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyTest(id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerData, err := GetReport(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := map[string]any{
|
||||
"provider_id": id,
|
||||
"body": "测试消息通道",
|
||||
"subject": "测试消息通道",
|
||||
}
|
||||
switch providerData["type"] {
|
||||
case "mail":
|
||||
err = NotifyMail(params)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Notify(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerName, ok := params["provider"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("通知类型错误")
|
||||
}
|
||||
switch providerName {
|
||||
case "mail":
|
||||
return NotifyMail(params)
|
||||
// case "btpanel-site":
|
||||
// return NotifyBt(params)
|
||||
default:
|
||||
return fmt.Errorf("不支持的通知类型")
|
||||
}
|
||||
}
|
||||
|
||||
func NotifyMail(params map[string]any) error {
|
||||
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
// fmt.Println(providerID)
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
|
||||
e := email.NewEmail()
|
||||
e.From = config["sender"]
|
||||
e.To = []string{config["receiver"]}
|
||||
e.Subject = params["subject"].(string)
|
||||
|
||||
e.Text = []byte(params["body"].(string))
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", config["smtpHost"], config["smtpPort"])
|
||||
|
||||
auth := smtp.PlainAuth("", config["sender"], config["password"], config["smtpHost"])
|
||||
|
||||
// 使用 SSL(通常是 465)
|
||||
if config["smtpPort"] == "465" {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true, // 开发阶段跳过证书验证,生产建议关闭
|
||||
ServerName: config["smtpHost"],
|
||||
}
|
||||
return e.SendWithTLS(addr, auth, tlsConfig)
|
||||
}
|
||||
|
||||
// 普通明文发送(25端口,非推荐)
|
||||
return e.Send(addr, auth)
|
||||
}
|
||||
package report
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jordan-wright/email"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetSqlite() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "report"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
var data []map[string]any
|
||||
var count int64
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
if p > 1 {
|
||||
limits[0] = (p - 1) * limit
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Limit(limits).Order("update_time", "desc").Select()
|
||||
} else {
|
||||
count, err = s.Count()
|
||||
data, err = s.Order("update_time", "desc").Limit(limits).Select()
|
||||
}
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
return data, int(count), nil
|
||||
}
|
||||
|
||||
func GetReport(id string) (map[string]any, error) {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
data, err := s.Where("id=?", []interface{}{id}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("没有找到此通知配置")
|
||||
}
|
||||
return data[0], nil
|
||||
|
||||
}
|
||||
|
||||
func AddReport(Type, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Insert(map[string]interface{}{
|
||||
"name": name,
|
||||
"type": Type,
|
||||
"config": config,
|
||||
"create_time": now,
|
||||
"update_time": now,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdReport(id, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]interface{}{
|
||||
"name": name,
|
||||
"config": config,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func DelReport(id string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyTest(id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerData, err := GetReport(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := map[string]any{
|
||||
"provider_id": id,
|
||||
"body": "测试消息通道",
|
||||
"subject": "测试消息通道",
|
||||
}
|
||||
switch providerData["type"] {
|
||||
case "mail":
|
||||
err = NotifyMail(params)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Notify(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerName, ok := params["provider"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("通知类型错误")
|
||||
}
|
||||
switch providerName {
|
||||
case "mail":
|
||||
return NotifyMail(params)
|
||||
// case "btpanel-site":
|
||||
// return NotifyBt(params)
|
||||
default:
|
||||
return fmt.Errorf("不支持的通知类型")
|
||||
}
|
||||
}
|
||||
|
||||
func NotifyMail(params map[string]any) error {
|
||||
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
// fmt.Println(providerID)
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
|
||||
e := email.NewEmail()
|
||||
e.From = config["sender"]
|
||||
e.To = []string{config["receiver"]}
|
||||
e.Subject = params["subject"].(string)
|
||||
|
||||
e.Text = []byte(params["body"].(string))
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", config["smtpHost"], config["smtpPort"])
|
||||
|
||||
auth := smtp.PlainAuth("", config["sender"], config["password"], config["smtpHost"])
|
||||
|
||||
// 使用 SSL(通常是 465)
|
||||
if config["smtpPort"] == "465" {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true, // 开发阶段跳过证书验证,生产建议关闭
|
||||
ServerName: config["smtpHost"],
|
||||
}
|
||||
err = e.SendWithTLS(addr, auth, tlsConfig)
|
||||
if err != nil {
|
||||
if err.Error() == "EOF" || strings.Contains(err.Error(), "short response") || err.Error() == "server response incomplete" {
|
||||
// 忽略短响应错误
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 普通明文发送(25端口,非推荐)
|
||||
err = e.Send(addr, auth)
|
||||
if err != nil {
|
||||
if err.Error() == "EOF" || strings.Contains(err.Error(), "short response") || err.Error() == "server response incomplete" {
|
||||
// 忽略短响应错误
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -95,34 +95,37 @@ func Save(setting *Setting) error {
|
||||
reload = true
|
||||
}
|
||||
s.TableName = "settings"
|
||||
if setting.Timeout != 0 {
|
||||
if setting.Timeout != 0 && setting.Timeout != public.TimeOut {
|
||||
s.Where("key = 'timeout'", []interface{}{}).Update(map[string]interface{}{"value": setting.Timeout})
|
||||
public.TimeOut = setting.Timeout
|
||||
}
|
||||
if setting.Secure != "" {
|
||||
s.Where("key = 'secure'", []interface{}{}).Update(map[string]interface{}{"value": setting.Secure})
|
||||
public.TimeOut = setting.Timeout
|
||||
}
|
||||
if setting.Https == "1" {
|
||||
if setting.Key == "" || setting.Cert == "" {
|
||||
return fmt.Errorf("key or cert is empty")
|
||||
}
|
||||
// fmt.Println(setting.Key, setting.Cert)
|
||||
err := public.ValidateSSLCertificate(setting.Cert, setting.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Where("key = 'https'", []interface{}{}).Update(map[string]interface{}{"value": setting.Https})
|
||||
// dir := filepath.Dir("data/https")
|
||||
if err := os.MkdirAll("data/https", os.ModePerm); err != nil {
|
||||
panic("创建目录失败: " + err.Error())
|
||||
}
|
||||
err = os.WriteFile("data/https/key.pem", []byte(setting.Key), 0644)
|
||||
// fmt.Println(err)
|
||||
os.WriteFile("data/https/cert.pem", []byte(setting.Cert), 0644)
|
||||
restart = true
|
||||
}
|
||||
|
||||
if setting.Secure != "" && setting.Secure != public.Secure {
|
||||
s.Where("key = 'secure'", []interface{}{}).Update(map[string]interface{}{"value": setting.Secure})
|
||||
public.TimeOut = setting.Timeout
|
||||
restart = true
|
||||
}
|
||||
if setting.Https != "" && setting.Https != public.GetSettingIgnoreError("https") {
|
||||
if setting.Https == "1" {
|
||||
if setting.Key == "" || setting.Cert == "" {
|
||||
return fmt.Errorf("key or cert is empty")
|
||||
}
|
||||
// fmt.Println(setting.Key, setting.Cert)
|
||||
err := public.ValidateSSLCertificate(setting.Cert, setting.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// dir := filepath.Dir("data/https")
|
||||
if err := os.MkdirAll("data/https", os.ModePerm); err != nil {
|
||||
panic("创建目录失败: " + err.Error())
|
||||
}
|
||||
err = os.WriteFile("data/https/key.pem", []byte(setting.Key), 0644)
|
||||
// fmt.Println(err)
|
||||
os.WriteFile("data/https/cert.pem", []byte(setting.Cert), 0644)
|
||||
}
|
||||
s.Where("key = 'https'", []interface{}{}).Update(map[string]interface{}{"value": setting.Https})
|
||||
restart = true
|
||||
}
|
||||
if restart {
|
||||
Restart()
|
||||
return nil
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"ALLinSSL/backend/public"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// var executors map[string]func(map[string]any) (any, error)
|
||||
@@ -33,7 +34,7 @@ func Executors(exec string, params map[string]any) (any, error) {
|
||||
|
||||
func apply(params map[string]any) (any, error) {
|
||||
logger := params["logger"].(*public.Logger)
|
||||
|
||||
|
||||
logger.Info("=============申请证书=============")
|
||||
certificate, err := certApply.Apply(params, logger)
|
||||
if err != nil {
|
||||
@@ -67,28 +68,57 @@ func deploy(params map[string]any) (any, error) {
|
||||
func upload(params map[string]any) (any, error) {
|
||||
logger := params["logger"].(*public.Logger)
|
||||
logger.Info("=============上传证书=============")
|
||||
|
||||
keyStr, ok := params["key"].(string)
|
||||
if !ok {
|
||||
logger.Error("上传的密钥有误")
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, errors.New("上传的密钥有误")
|
||||
// 判断证书id走本地还是走旧上传,应在之后的迭代中移除旧代码
|
||||
if params["cert_id"] == nil {
|
||||
keyStr, ok := params["key"].(string)
|
||||
if !ok {
|
||||
logger.Error("上传的密钥有误")
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, errors.New("上传的密钥有误")
|
||||
}
|
||||
certStr, ok := params["cert"].(string)
|
||||
if !ok {
|
||||
logger.Error("上传的证书有误")
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, errors.New("上传的证书有误")
|
||||
}
|
||||
_, err := cert.UploadCert(keyStr, certStr)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, err
|
||||
}
|
||||
logger.Info("=============上传成功=============")
|
||||
|
||||
return params, nil
|
||||
} else {
|
||||
certId := ""
|
||||
switch v := params["cert_id"].(type) {
|
||||
case float64:
|
||||
certId = strconv.Itoa(int(v))
|
||||
case string:
|
||||
certId = v
|
||||
default:
|
||||
logger.Info("=============上传证书获取失败=============")
|
||||
return nil, errors.New("证书 ID 类型错误")
|
||||
}
|
||||
result := map[string]any{}
|
||||
certObj, err := cert.GetCert(certId)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
logger.Info("=============上传证书获取失败=============")
|
||||
return nil, err
|
||||
}
|
||||
if certObj == nil {
|
||||
logger.Error("证书不存在")
|
||||
logger.Info("=============上传证书获取失败=============")
|
||||
return nil, errors.New("证书不存在")
|
||||
}
|
||||
logger.Debug(fmt.Sprintf("证书 ID: %s", certId))
|
||||
result["cert"] = certObj["cert"]
|
||||
result["key"] = certObj["key"]
|
||||
return result, nil
|
||||
}
|
||||
certStr, ok := params["cert"].(string)
|
||||
if !ok {
|
||||
logger.Error("上传的证书有误")
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, errors.New("上传的证书有误")
|
||||
}
|
||||
err := cert.UploadCert(keyStr, certStr)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
logger.Info("=============上传失败=============")
|
||||
return nil, err
|
||||
}
|
||||
logger.Info("=============上传成功=============")
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func notify(params map[string]any) (any, error) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"ALLinSSL/backend/public"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -27,7 +26,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
@@ -36,7 +35,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Order("update_time", "desc").Limit(limits).Select()
|
||||
@@ -56,7 +55,7 @@ func AddWorkflow(name, content, execType, active, execTime string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("检测到工作流配置有问题:%v", err)
|
||||
}
|
||||
|
||||
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -161,7 +160,7 @@ func ExecuteWorkflow(id string) error {
|
||||
return fmt.Errorf("工作流正在执行中")
|
||||
}
|
||||
content := data[0]["content"].(string)
|
||||
|
||||
|
||||
go func(id, c string) {
|
||||
// defer wg.Done()
|
||||
// WorkflowID := strconv.FormatInt(id, 10)
|
||||
@@ -192,13 +191,15 @@ func resolveInputs(inputs []WorkflowNodeParams, ctx *ExecutionContext) map[strin
|
||||
for _, input := range inputs {
|
||||
if input.FromNodeID != "" {
|
||||
if val, ok := ctx.GetOutput(input.FromNodeID); ok {
|
||||
switch strings.Split(strings.TrimPrefix(input.FromNodeID, "-"), "-")[0] {
|
||||
case "apply":
|
||||
input.Name = "certificate"
|
||||
case "upload":
|
||||
input.Name = "certificate"
|
||||
}
|
||||
resolved[input.Name] = val
|
||||
// 暂时没有新的类型可以先写死
|
||||
// switch strings.Split(strings.TrimPrefix(input.FromNodeID, "-"), "-")[0] {
|
||||
// case "apply":
|
||||
// input.Name = "certificate"
|
||||
// case "upload":
|
||||
// input.Name = "certificate"
|
||||
// }
|
||||
// resolved[input.Name] = val
|
||||
resolved["certificate"] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,10 +218,10 @@ func RunNode(node *WorkflowNode, ctx *ExecutionContext) error {
|
||||
}
|
||||
node.Config["_runId"] = ctx.RunID
|
||||
node.Config["logger"] = ctx.Logger
|
||||
|
||||
|
||||
// 执行当前节点
|
||||
result, err := Executors(node.Type, node.Config)
|
||||
|
||||
|
||||
var status ExecutionStatus
|
||||
if err != nil {
|
||||
status = StatusFailed
|
||||
@@ -230,9 +231,9 @@ func RunNode(node *WorkflowNode, ctx *ExecutionContext) error {
|
||||
} else {
|
||||
status = StatusSuccess
|
||||
}
|
||||
|
||||
|
||||
ctx.SetOutput(node.Id, result, status)
|
||||
|
||||
|
||||
// 普通的并行
|
||||
if node.Type == "branch" {
|
||||
if len(node.ConditionNodes) > 0 {
|
||||
@@ -268,7 +269,7 @@ func RunNode(node *WorkflowNode, ctx *ExecutionContext) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if node.ChildNode != nil {
|
||||
return RunNode(node.ChildNode, ctx)
|
||||
}
|
||||
|
||||
@@ -1,117 +1,117 @@
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetSqliteObjWH 工作流执行历史记录表对象
|
||||
func GetSqliteObjWH() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "workflow_history"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// GetListWH 获取工作流执行历史记录列表
|
||||
func GetListWH(id string, p, limit int64) ([]map[string]any, int, error) {
|
||||
var data []map[string]any
|
||||
var count int64
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
if p > 1 {
|
||||
limits[0] = (p - 1) * limit
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
if id == "" {
|
||||
count, err = s.Count()
|
||||
data, err = s.Limit(limits).Order("create_time", "desc").Select()
|
||||
} else {
|
||||
count, err = s.Where("workflow_id=?", []interface{}{id}).Count()
|
||||
data, err = s.Where("workflow_id=?", []interface{}{id}).Limit(limits).Order("create_time", "desc").Select()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
return data, int(count), nil
|
||||
}
|
||||
|
||||
// 添加工作流执行历史记录
|
||||
func AddWorkflowHistory(workflowID, execType string) (string, error) {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
ID := public.GenerateUUID()
|
||||
_, err = s.Insert(map[string]interface{}{
|
||||
"id": ID,
|
||||
"workflow_id": workflowID,
|
||||
"status": "running",
|
||||
"exec_type": execType,
|
||||
"create_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = UpdDb(workflowID, map[string]interface{}{"last_run_status": "running", "last_run_time": now})
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
// 工作流执行结束
|
||||
func UpdateWorkflowHistory(id, status string) error {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]interface{}{
|
||||
"status": status,
|
||||
"end_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopWorkflow(id string) error {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
data, err := s.Where("id=?", []interface{}{id}).Select()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
SetWorkflowStatus(data[0]["workflow_id"].(string), id, "fail")
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetExecLog(id string) (string, error) {
|
||||
log, err := os.ReadFile(filepath.Join(public.GetSettingIgnoreError("workflow_log_path"), id+".log"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(log), nil
|
||||
}
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetSqliteObjWH 工作流执行历史记录表对象
|
||||
func GetSqliteObjWH() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "workflow_history"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// GetListWH 获取工作流执行历史记录列表
|
||||
func GetListWH(id string, p, limit int64) ([]map[string]any, int, error) {
|
||||
var data []map[string]any
|
||||
var count int64
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
if p > 1 {
|
||||
limits[0] = (p - 1) * limit
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
if id == "" {
|
||||
count, err = s.Count()
|
||||
data, err = s.Limit(limits).Order("create_time", "desc").Select()
|
||||
} else {
|
||||
count, err = s.Where("workflow_id=?", []interface{}{id}).Count()
|
||||
data, err = s.Where("workflow_id=?", []interface{}{id}).Limit(limits).Order("create_time", "desc").Select()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
return data, int(count), nil
|
||||
}
|
||||
|
||||
// 添加工作流执行历史记录
|
||||
func AddWorkflowHistory(workflowID, execType string) (string, error) {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
ID := public.GenerateUUID()
|
||||
_, err = s.Insert(map[string]interface{}{
|
||||
"id": ID,
|
||||
"workflow_id": workflowID,
|
||||
"status": "running",
|
||||
"exec_type": execType,
|
||||
"create_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = UpdDb(workflowID, map[string]interface{}{"last_run_status": "running", "last_run_time": now})
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
// 工作流执行结束
|
||||
func UpdateWorkflowHistory(id, status string) error {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]interface{}{
|
||||
"status": status,
|
||||
"end_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopWorkflow(id string) error {
|
||||
s, err := GetSqliteObjWH()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
data, err := s.Where("id=?", []interface{}{id}).Select()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
SetWorkflowStatus(data[0]["workflow_id"].(string), id, "fail")
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetExecLog(id string) (string, error) {
|
||||
log, err := os.ReadFile(filepath.Join(public.GetSettingIgnoreError("workflow_log_path"), id+".log"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(log), nil
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package middleware
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -20,6 +23,10 @@ var Html404 = []byte(`<html>
|
||||
|
||||
func SessionAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if checkApiKey(c) {
|
||||
return
|
||||
}
|
||||
|
||||
routePath := c.Request.URL.Path
|
||||
method := c.Request.Method
|
||||
paths := strings.Split(strings.TrimPrefix(routePath, "/"), "/")
|
||||
@@ -28,12 +35,14 @@ func SessionAuthMiddleware() gin.HandlerFunc {
|
||||
gob.Register(time.Time{})
|
||||
last := session.Get("lastRequestTime")
|
||||
|
||||
if routePath == public.Secure && session.Get("secure") == nil {
|
||||
// 访问安全入口,设置 session
|
||||
session.Set("secure", true)
|
||||
session.Set("lastRequestTime", now)
|
||||
// 一定要保存 session BEFORE redirect
|
||||
session.Save()
|
||||
if routePath == public.Secure {
|
||||
if session.Get("secure") == nil {
|
||||
// 访问安全入口,设置 session
|
||||
session.Set("secure", true)
|
||||
session.Set("lastRequestTime", now)
|
||||
// 一定要保存 session BEFORE redirect
|
||||
session.Save()
|
||||
}
|
||||
// 返回登录页
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
// c.Abort()
|
||||
@@ -73,6 +82,9 @@ func SessionAuthMiddleware() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
}
|
||||
if routePath == "/favicon.ico" {
|
||||
return
|
||||
}
|
||||
// 判断是否为静态文件路径
|
||||
if method == "GET" {
|
||||
if len(paths) > 1 && paths[0] == "static" {
|
||||
@@ -86,14 +98,21 @@ func SessionAuthMiddleware() gin.HandlerFunc {
|
||||
return
|
||||
} else {
|
||||
if session.Get("__login_key") != public.GetSettingIgnoreError("login_key") {
|
||||
session.Clear()
|
||||
// session.Set("secure", true)
|
||||
session.Set("login", nil)
|
||||
session.Save()
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "登录信息发生变化,请重新登录"})
|
||||
c.Abort()
|
||||
// c.JSON(http.StatusUnauthorized, gin.H{"message": "登录信息发生变化,请重新登录"})
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
// c.Abort()
|
||||
} else {
|
||||
// 访问正常,更新最后请求时间
|
||||
session.Set("lastRequestTime", now)
|
||||
session.Save()
|
||||
if paths[0] == "login" {
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,3 +125,52 @@ func SessionAuthMiddleware() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkApiKey(c *gin.Context) bool {
|
||||
var form struct {
|
||||
ApiToken string `form:"api_token"`
|
||||
Timestamp string `form:"timestamp"`
|
||||
}
|
||||
err := c.Bind(&form)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
c.Abort()
|
||||
return false
|
||||
}
|
||||
if form.ApiToken == "" || form.Timestamp == "" {
|
||||
return false
|
||||
}
|
||||
apiKey := public.GetSettingIgnoreError("api_key")
|
||||
if apiKey == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "未开启api"})
|
||||
c.Abort()
|
||||
return false
|
||||
}
|
||||
// timestamp := time.Now().Unix()
|
||||
ApiToken := generateSignature(form.Timestamp, apiKey)
|
||||
if form.ApiToken != ApiToken {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
|
||||
c.Abort()
|
||||
return false
|
||||
}
|
||||
// 这里可以添加其他的验证逻辑,比如检查时间戳是否过期等
|
||||
timestamp, err := strconv.ParseInt(form.Timestamp, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid timestamp"})
|
||||
return false
|
||||
}
|
||||
if time.Now().Unix()-timestamp > 60*5 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "timestamp expired"})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func generateSignature(timestamp, apiKey string) string {
|
||||
keyMd5 := md5.Sum([]byte(apiKey))
|
||||
keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:]))
|
||||
|
||||
signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex))
|
||||
signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:]))
|
||||
return signMd5Hex
|
||||
}
|
||||
|
||||
@@ -4,18 +4,18 @@ import (
|
||||
"ALLinSSL/backend/public"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.MkdirAll("data", os.ModePerm)
|
||||
|
||||
|
||||
dbPath := "data/data.db"
|
||||
_, _ = filepath.Abs(dbPath)
|
||||
// fmt.Println("数据库路径:", absPath)
|
||||
db, err := sql.Open("sqlite3", dbPath)
|
||||
db, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
// fmt.Println("创建数据库失败:", err)
|
||||
return
|
||||
@@ -176,15 +176,15 @@ func init() {
|
||||
INSERT INTO access_type (name, type) VALUES ('ssh', 'host');
|
||||
INSERT INTO access_type (name, type) VALUES ('btpanel', 'host');
|
||||
INSERT INTO access_type (name, type) VALUES ('1panel', 'host');`)
|
||||
|
||||
|
||||
uuidStr := public.GenerateUUID()
|
||||
randomStr := public.RandomString(8)
|
||||
|
||||
|
||||
port, err := public.GetFreePort()
|
||||
if err != nil {
|
||||
port = 20773
|
||||
}
|
||||
|
||||
|
||||
Isql := fmt.Sprintf(
|
||||
`INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES ('log_path', 'logs/ALLinSSL.log', '2025-04-15 15:58', '2025-04-15 15:58', 1, null);
|
||||
INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES ( 'workflow_log_path', 'logs/workflows/', '2025-04-15 15:58', '2025-04-15 15:58', 1, null);
|
||||
@@ -194,7 +194,7 @@ INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES
|
||||
INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES ('session_key', '%s', '2025-04-15 15:58', '2025-04-15 15:58', 1, null);
|
||||
INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES ('secure', '/%s', '2025-04-15 15:58', '2025-04-15 15:58', 1, null);
|
||||
INSERT INTO settings (key, value, create_time, update_time, active, type) VALUES ('port', '%d', '2025-04-15 15:58', '2025-04-15 15:58', 1, null);`, uuidStr, uuidStr, randomStr, port)
|
||||
|
||||
|
||||
insertDefaultData(db, "settings", Isql)
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ func insertDefaultData(db *sql.DB, table, insertSQL string) {
|
||||
// fmt.Println("检查数据行数失败:", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 如果表为空,则插入默认数据
|
||||
if count == 0 {
|
||||
// fmt.Println("表为空,插入默认数据...")
|
||||
|
||||
@@ -1,181 +1,202 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// GetSettingIgnoreError 获取系统配置-忽略错误
|
||||
func GetSettingIgnoreError(key string) string {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("key=?", []interface{}{key}).Select()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return ""
|
||||
}
|
||||
setting, ok := res[0]["value"].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return setting
|
||||
}
|
||||
|
||||
func UpdateSetting(key, val string) error {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
_, err = s.Where("key=?", []interface{}{key}).Update(map[string]any{"value": val})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSettingsFromType(typ string) ([]map[string]any, error) {
|
||||
db := "data/data.db"
|
||||
s, err := NewSqlite(db, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("type=?", []interface{}{typ}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// GetFreePort 获取一个可用的随机端口
|
||||
func GetFreePort() (int, error) {
|
||||
// 端口为 0,表示让系统自动分配一个可用端口
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
addr := ln.Addr().String()
|
||||
// 提取端口号
|
||||
parts := strings.Split(addr, ":")
|
||||
if len(parts) < 2 {
|
||||
return 0, fmt.Errorf("invalid address: %s", addr)
|
||||
}
|
||||
|
||||
var port int
|
||||
fmt.Sscanf(parts[len(parts)-1], "%d", &port)
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// RandomString 生成指定长度的随机字符串
|
||||
func RandomString(length int) string {
|
||||
if str, err := RandomStringWithCharset(length, defaultCharset); err != nil {
|
||||
return "allinssl"
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
// RandomStringWithCharset 使用指定字符集生成随机字符串
|
||||
func RandomStringWithCharset(length int, charset string) (string, error) {
|
||||
result := make([]byte, length)
|
||||
charsetLen := big.NewInt(int64(len(charset)))
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
num, err := rand.Int(rand.Reader, charsetLen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// GenerateUUID 生成 UUID
|
||||
func GenerateUUID() string {
|
||||
// 生成一个新的 UUID
|
||||
uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// 返回 UUID 的字符串表示
|
||||
return uuidStr
|
||||
}
|
||||
|
||||
func GetLocalIP() (string, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // 接口未启用
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // 忽略回环地址
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
// 只返回 IPv4 内网地址
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() {
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("没有找到内网 IP")
|
||||
}
|
||||
|
||||
func GetPublicIP() (string, error) {
|
||||
resp, err := http.Get("https://www.bt.cn/Api/getIpAddress")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("HTTP状态错误: %v", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
package public
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// GetSettingIgnoreError 获取系统配置-忽略错误
|
||||
func GetSettingIgnoreError(key string) string {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("key=?", []interface{}{key}).Select()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return ""
|
||||
}
|
||||
setting, ok := res[0]["value"].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return setting
|
||||
}
|
||||
|
||||
func UpdateSetting(key, val string) error {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
_, err = s.Where("key=?", []interface{}{key}).Update(map[string]any{"value": val})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSettingsFromType(typ string) ([]map[string]any, error) {
|
||||
db := "data/data.db"
|
||||
s, err := NewSqlite(db, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("type=?", []interface{}{typ}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// GetFreePort 获取一个可用的随机端口
|
||||
func GetFreePort() (int, error) {
|
||||
// 端口为 0,表示让系统自动分配一个可用端口
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
addr := ln.Addr().String()
|
||||
// 提取端口号
|
||||
parts := strings.Split(addr, ":")
|
||||
if len(parts) < 2 {
|
||||
return 0, fmt.Errorf("invalid address: %s", addr)
|
||||
}
|
||||
|
||||
var port int
|
||||
fmt.Sscanf(parts[len(parts)-1], "%d", &port)
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// RandomString 生成指定长度的随机字符串
|
||||
func RandomString(length int) string {
|
||||
if str, err := RandomStringWithCharset(length, defaultCharset); err != nil {
|
||||
return "allinssl"
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
// RandomStringWithCharset 使用指定字符集生成随机字符串
|
||||
func RandomStringWithCharset(length int, charset string) (string, error) {
|
||||
result := make([]byte, length)
|
||||
charsetLen := big.NewInt(int64(len(charset)))
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
num, err := rand.Int(rand.Reader, charsetLen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// GenerateUUID 生成 UUID
|
||||
func GenerateUUID() string {
|
||||
// 生成一个新的 UUID
|
||||
uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// 返回 UUID 的字符串表示
|
||||
return uuidStr
|
||||
}
|
||||
|
||||
func GetLocalIP() (string, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // 接口未启用
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // 忽略回环地址
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
// 只返回 IPv4 内网地址
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() {
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("没有找到内网 IP")
|
||||
}
|
||||
|
||||
func GetPublicIP() (string, error) {
|
||||
resp, err := http.Get("https://www.bt.cn/Api/getIpAddress")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("HTTP状态错误: %v", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func ContainsAllIgnoreBRepeats(a, b []string) bool {
|
||||
// 构建 A 的集合
|
||||
setA := make(map[string]struct{})
|
||||
for _, item := range a {
|
||||
setA[item] = struct{}{}
|
||||
}
|
||||
|
||||
// 遍历 B 的唯一元素,判断是否在 A 中
|
||||
seen := make(map[string]struct{})
|
||||
for _, item := range b {
|
||||
if _, checked := seen[item]; checked {
|
||||
continue
|
||||
}
|
||||
seen[item] = struct{}{}
|
||||
if _, ok := setA[item]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
func Register(r *gin.Engine) {
|
||||
v1 := r.Group("/v1")
|
||||
|
||||
|
||||
login := v1.Group("/login")
|
||||
{
|
||||
login.POST("/sign", api.Sign)
|
||||
@@ -70,11 +70,15 @@ func Register(r *gin.Engine) {
|
||||
{
|
||||
overview.POST("/get_overviews", api.GetOverview)
|
||||
}
|
||||
|
||||
|
||||
// 1. 提供静态文件服务
|
||||
r.StaticFS("/static", http.Dir("./frontend/static")) // 静态资源路径
|
||||
r.StaticFS("/auto-deploy/static", http.Dir("./frontend/static")) // 静态资源路径
|
||||
|
||||
// 返回 favicon.ico
|
||||
r.GET("/favicon.ico", func(c *gin.Context) {
|
||||
c.File("./frontend/favicon.ico")
|
||||
})
|
||||
|
||||
// 3. 前端路由托管:匹配所有其他路由并返回 index.html
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.File("./frontend/index.html")
|
||||
|
||||
@@ -93,37 +93,3 @@ func (s *Scheduler) loop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// package scheduler
|
||||
//
|
||||
// import (
|
||||
// "sync"
|
||||
// "time"
|
||||
// )
|
||||
//
|
||||
// var funcs = []func(){
|
||||
// SiteMonitor,
|
||||
// RunWorkflows,
|
||||
// }
|
||||
//
|
||||
// func Scheduler() {
|
||||
// for {
|
||||
// start := time.Now()
|
||||
//
|
||||
// var wg sync.WaitGroup
|
||||
// wg.Add(len(funcs))
|
||||
//
|
||||
// for _, f := range funcs {
|
||||
// go func(fn func()) {
|
||||
// defer wg.Done()
|
||||
// fn()
|
||||
// }(f)
|
||||
// }
|
||||
// wg.Wait()
|
||||
// // 保证每轮间隔至少10秒
|
||||
// elapsed := time.Since(start)
|
||||
// if elapsed < 10*time.Second {
|
||||
// time.Sleep(10*time.Second - elapsed)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user