├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── berforebuild.bat ├── config ├── appinfo.go ├── blacklist.go ├── config.go ├── ddns.go ├── portforward.go ├── reverseproxy.go ├── safecheck.go ├── sslcertficate.go ├── whitelist.go └── wol.go ├── ddns ├── alidns.go ├── baidu.go ├── callback.go ├── cloudflare.go ├── dns.go ├── dnscommon.go ├── dnspod.go ├── godaddy.go ├── huawei.go ├── porkbun.go └── worker.go ├── ddnscore.go ├── cache.go ├── domain.go ├── taskinfo.go ├── taskstate.go └── webhook.go ├── debug.go ├── go.mod ├── go.sum ├── goports ├── main.go ├── module ├── ddns │ ├── conf │ │ └── ddns.go │ ├── ddnscore.go │ │ ├── cache.go │ │ ├── domain.go │ │ ├── taskinfo.go │ │ ├── taskstate.go │ │ └── webhook.go │ ├── ddnsgo │ │ └── ddns.go │ ├── providers │ │ ├── alidns.go │ │ ├── baidu.go │ │ ├── callback.go │ │ ├── cloudflare.go │ │ ├── dnspod.go │ │ ├── godaddy.go │ │ ├── huawei.go │ │ ├── porkbun.go │ │ ├── provider.go │ │ └── providercommon.go │ └── worker.go ├── portforward │ ├── conf │ │ └── portforward.go │ ├── portforward.go │ └── socketproxy │ │ ├── baseproxyconf.go │ │ ├── proxy.go │ │ ├── socketproxy.go │ │ ├── tcpproxy.go │ │ ├── tcpudpcommon.go │ │ └── udpproxy.go ├── reverseproxy │ ├── conf │ │ └── reverseproxy.go │ ├── proxy.go │ └── reverseproxy.go ├── safe │ ├── blacklist.go │ ├── conf │ │ ├── black.go │ │ └── white.go │ ├── safe.go │ ├── safecheck.go │ └── whitelist.go ├── service │ └── service.go ├── sslcertficate │ ├── conf │ │ └── sslconf.go │ ├── ssl.go │ └── sslcertficate.go ├── weblog │ └── weblog.go └── wol │ ├── client.go │ ├── conf │ ├── device.go │ └── service.go │ ├── ctl.go │ ├── device.go │ ├── httpapi │ └── api.go │ ├── module.go │ ├── msg.go │ ├── service.go │ └── websocketcommon.go ├── previews ├── ddnslist.png ├── domainsync.png ├── iphistroy.png ├── relayrules.png ├── relayruleset.png ├── reverseproxy.png ├── webhookhistroy.png ├── whitelist.png ├── whitelistset.png ├── wol001.png └── wol002.png ├── reverseproxy └── proxy.go ├── scripts ├── lucky.service ├── luckyservice └── misnap_init.sh ├── socketproxy ├── baseproxyconf.go ├── proxy.go ├── tcpproxy.go ├── tcpudpcommon.go └── udpproxy.go ├── thirdlib ├── fatedier │ └── golib │ │ └── json │ │ ├── msg.go │ │ ├── pack.go │ │ └── process.go ├── gdylib │ ├── bemfa │ │ ├── device.go │ │ └── global.go │ ├── blinker │ │ ├── VoiceAssistant.go │ │ ├── device.go │ │ └── global.go │ ├── dnsutils │ │ └── resolve.go │ ├── fileutils │ │ ├── fileutils.go │ │ ├── run_linux.go │ │ └── run_windows.go │ ├── ginutils │ │ ├── basicAuth.go │ │ ├── jwt.go │ │ ├── staticFilesHandler.go │ │ └── utils.go │ ├── httputils │ │ ├── common.go │ │ ├── goututils.go │ │ └── httpclient.go │ ├── logsbuffer │ │ └── logsbuffer.go │ ├── netinterfaces │ │ └── netInterface.go │ ├── pool │ │ └── buf.go │ ├── recoverutil │ │ └── recoverutil.go │ ├── service │ │ └── service.go │ ├── slice │ │ └── options.go │ ├── stderrredirect │ │ ├── stderrredirect_linux.go │ │ └── stderrredirect_windows.go │ ├── stringsp │ │ ├── binary.go │ │ ├── randomutils.go │ │ ├── stringsp.go │ │ └── url.go │ └── websocketController │ │ └── controller.go ├── go-wol │ ├── magic_packet.go │ └── wol.go └── jeessy2 │ └── ddns-go │ └── util │ ├── aliyun_signer.go │ ├── aliyun_signer_util.go │ ├── baidu_signer.go │ ├── escape.go │ ├── huawei_signer.go │ └── net.go ├── web.go └── web ├── adminviews ├── .gitignore ├── .vscode │ └── extensions.json ├── README.md ├── auto-imports.d.ts ├── components.d.ts ├── dist │ ├── assets │ │ ├── index.0c84c960.js │ │ ├── index.abda1f8d.css │ │ ├── index.e5c8aec2.js │ │ └── index.f23c7bd8.css │ ├── diandeng_log.png │ ├── favicon.ico │ └── index.html ├── index.html ├── package-lock.json ├── package.json ├── public │ ├── diandeng_log.png │ └── favicon.ico ├── src │ ├── App.vue │ ├── apis │ │ ├── storage.js │ │ └── utils.js │ ├── assets │ │ ├── appbase.css │ │ ├── base.css │ │ ├── common-layout.scss │ │ └── logo.svg │ ├── components │ │ ├── About.vue │ │ ├── BlackLists.vue │ │ ├── DDNS.vue │ │ ├── DDNSSet.vue │ │ ├── Log.vue │ │ ├── Login.vue │ │ ├── PSet.vue │ │ ├── Pmenu.vue │ │ ├── PortForward.vue │ │ ├── PortForwardSet.vue │ │ ├── ReverseProxy.vue │ │ ├── SSL.vue │ │ ├── Status.vue │ │ ├── WhiteListSet.vue │ │ ├── WhiteLists.vue │ │ └── tools │ │ │ ├── WOL.vue │ │ │ └── WOLServiceSet.vue │ ├── main.js │ ├── request │ │ └── index.js │ └── utils │ │ ├── ui.ts │ │ └── utils.ts └── vite.config.js ├── blackwhitelist.go ├── common.go ├── configure.go ├── ddns.go ├── portforward.go ├── reverseproxy.go ├── ssl.go ├── web.go └── wol.go /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=Go -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | relayports 8 | lucky 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | .goreleaser.yaml 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | *.log 17 | *.upx 18 | *.pem 19 | *.crt 20 | *.key 21 | lucky.conf 22 | 23 | # Dependency directories (remove the comment below to include it) 24 | # vendor/ 25 | /dist/ 26 | config.json 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY lucky / 3 | EXPOSE 16601 4 | WORKDIR /goodluck 5 | ENTRYPOINT ["/lucky"] 6 | CMD ["-c", "/goodluck/lucky.conf"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 gdy , 272288813@qq.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /berforebuild.bat: -------------------------------------------------------------------------------- 1 | cd ./web/adminviews 2 | npm run build -------------------------------------------------------------------------------- /config/appinfo.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | ) 7 | 8 | type AppInfo struct { 9 | AppName string 10 | Version string 11 | OS string 12 | ARCH string 13 | Date string 14 | RunTime string 15 | GoVersion string 16 | } 17 | 18 | var appInfo AppInfo 19 | 20 | func GetAppInfo() *AppInfo { 21 | return &appInfo 22 | } 23 | 24 | func InitAppInfo(version, date string) { 25 | appInfo.AppName = "Lucky" 26 | appInfo.Version = version 27 | appInfo.Date = date 28 | appInfo.OS = runtime.GOOS 29 | appInfo.ARCH = runtime.GOARCH 30 | appInfo.RunTime = time.Now().Format("2006-01-02 15:04:05") 31 | appInfo.GoVersion = runtime.Version() 32 | 33 | time.Now().Format("2006-01-02T15:04:05Z") 34 | 35 | buildTime, err := time.Parse("2006-01-02T15:04:05Z", date) 36 | if err != nil { 37 | return 38 | } 39 | appInfo.Date = buildTime.Local().Format("2006-01-02 15:04:05") 40 | 41 | } 42 | -------------------------------------------------------------------------------- /config/blacklist.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package config 3 | 4 | import ( 5 | "fmt" 6 | "net" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type BlackListItem WhiteListItem 12 | 13 | func (w *BlackListItem) Contains(ip string) bool { 14 | netIP := net.ParseIP(ip) 15 | if netIP == nil { 16 | return false 17 | } 18 | if w.NetIP != nil { 19 | return w.NetIP.Equal(netIP) 20 | } 21 | 22 | if w.Cidr != nil { 23 | return w.Cidr.Contains(netIP) 24 | } 25 | return false 26 | } 27 | 28 | type BlackListConfigure struct { 29 | BlackList []BlackListItem `json:"BlackList"` //黑名单列表 30 | } 31 | 32 | func GetBlackList() []BlackListItem { 33 | programConfigureMutex.RLock() 34 | defer programConfigureMutex.RUnlock() 35 | 36 | BlackListFlush(false) 37 | 38 | var resList []BlackListItem 39 | if programConfigure == nil { 40 | return resList 41 | } 42 | for i := range programConfigure.BlackListConfigure.BlackList { 43 | resList = append(resList, programConfigure.BlackListConfigure.BlackList[i]) 44 | } 45 | return resList 46 | } 47 | 48 | func BlackListInit() { 49 | programConfigureMutex.RLock() 50 | defer programConfigureMutex.RUnlock() 51 | var netIP net.IP 52 | var cidr *net.IPNet 53 | 54 | for i := range programConfigure.BlackListConfigure.BlackList { 55 | netIP = nil 56 | cidr = nil 57 | if strings.Contains(programConfigure.BlackListConfigure.BlackList[i].IP, "/") { 58 | _, cidr, _ = net.ParseCIDR(programConfigure.BlackListConfigure.BlackList[i].IP) 59 | } else { 60 | netIP = net.ParseIP(programConfigure.BlackListConfigure.BlackList[i].IP) 61 | } 62 | programConfigure.BlackListConfigure.BlackList[i].Cidr = cidr 63 | programConfigure.BlackListConfigure.BlackList[i].NetIP = netIP 64 | } 65 | } 66 | 67 | func BlackListAdd(ip string, activelifeDuration int32) (string, error) { 68 | programConfigureMutex.Lock() 69 | defer programConfigureMutex.Unlock() 70 | 71 | var err error 72 | var netIP net.IP = nil 73 | var cidr *net.IPNet = nil 74 | if strings.Contains(ip, "/") { 75 | _, cidr, err = net.ParseCIDR(ip) 76 | if err != nil { 77 | return "", fmt.Errorf("网段格式有误,转换出错:%s", err.Error()) 78 | } 79 | } else { 80 | netIP = net.ParseIP(ip) 81 | if netIP == nil { 82 | return "", fmt.Errorf("IP格式有误") 83 | } 84 | } 85 | 86 | if activelifeDuration <= 0 { 87 | activelifeDuration = 666666 88 | } 89 | 90 | EffectiveTimeStr := time.Now().Add(time.Hour * time.Duration(activelifeDuration)).Format("2006-01-02 15:04:05") 91 | 92 | for i, ipr := range programConfigure.BlackListConfigure.BlackList { 93 | if ipr.IP == ip { 94 | programConfigure.BlackListConfigure.BlackList[i].EffectiveTime = EffectiveTimeStr 95 | return EffectiveTimeStr, Save() 96 | } 97 | } 98 | item := BlackListItem{IP: ip, EffectiveTime: EffectiveTimeStr, NetIP: netIP, Cidr: cidr} 99 | programConfigure.BlackListConfigure.BlackList = append(programConfigure.BlackListConfigure.BlackList, item) 100 | return EffectiveTimeStr, Save() 101 | } 102 | 103 | func BlackListDelete(ip string) error { 104 | programConfigureMutex.Lock() 105 | defer programConfigureMutex.Unlock() 106 | 107 | removeCount := 0 108 | CONTINUECHECK: 109 | removeIndex := -1 110 | 111 | for i, ipr := range programConfigure.BlackListConfigure.BlackList { 112 | if ipr.IP == ip { 113 | removeIndex = i 114 | break 115 | } 116 | } 117 | 118 | if removeIndex >= 0 { 119 | removeCount++ 120 | programConfigure.BlackListConfigure.BlackList = DeleteBlackListlice(programConfigure.BlackListConfigure.BlackList, removeIndex) 121 | goto CONTINUECHECK 122 | } 123 | if removeCount == 0 { 124 | return nil 125 | } 126 | return Save() 127 | } 128 | 129 | func BlackListFlush(lock bool) error { 130 | if lock { 131 | programConfigureMutex.Lock() 132 | defer programConfigureMutex.Unlock() 133 | } 134 | 135 | removeCount := 0 136 | 137 | CONTINUECHECK: 138 | removeIndex := -1 139 | 140 | for i, ipr := range programConfigure.BlackListConfigure.BlackList { 141 | ipat, err := time.ParseInLocation("2006-01-02 15:04:05", ipr.EffectiveTime, time.Local) 142 | if err != nil { //有效时间格式有误,当失效处理 143 | removeIndex = i 144 | 145 | break 146 | } 147 | 148 | if time.Since(ipat) > 0 { 149 | removeIndex = i 150 | break 151 | } 152 | } 153 | 154 | if removeIndex >= 0 { 155 | removeCount++ 156 | programConfigure.BlackListConfigure.BlackList = DeleteBlackListlice(programConfigure.BlackListConfigure.BlackList, removeIndex) 157 | goto CONTINUECHECK 158 | } 159 | 160 | if removeCount == 0 { 161 | return nil 162 | } 163 | return Save() 164 | } 165 | 166 | func DeleteBlackListlice(a []BlackListItem, deleteIndex int) []BlackListItem { 167 | j := 0 168 | for i := range a { 169 | if i != deleteIndex { 170 | a[j] = a[i] 171 | j++ 172 | } 173 | } 174 | return a[:j] 175 | } 176 | -------------------------------------------------------------------------------- /config/safecheck.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package config 3 | 4 | import ( 5 | "time" 6 | ) 7 | 8 | func SafeCheck(mode, ip string) bool { 9 | switch mode { 10 | case "whitelist": 11 | //log.Printf("whitelist") 12 | return whiteListCheck(ip) 13 | case "blacklist": 14 | //log.Printf("blacklist") 15 | return blackListCheck(ip) 16 | default: 17 | return false 18 | } 19 | } 20 | 21 | func whiteListCheck(ip string) bool { 22 | programConfigureMutex.RLock() 23 | defer programConfigureMutex.RUnlock() 24 | if programConfigure == nil { 25 | return false 26 | } 27 | 28 | for _, item := range programConfigure.WhiteListConfigure.WhiteList { 29 | 30 | if !item.Contains(ip) { 31 | continue 32 | } 33 | 34 | itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local) 35 | if err != nil { 36 | return false 37 | } 38 | 39 | if time.Since(itemEffectiveTime) < 0 { 40 | //log.Printf("CCC") 41 | return true 42 | } 43 | return false 44 | } 45 | 46 | //log.Printf("DDDD") 47 | return false 48 | } 49 | 50 | func blackListCheck(ip string) bool { 51 | programConfigureMutex.RLock() 52 | defer programConfigureMutex.RUnlock() 53 | if programConfigure == nil { 54 | return true 55 | } 56 | 57 | for _, item := range programConfigure.BlackListConfigure.BlackList { 58 | if !item.Contains(ip) { 59 | continue 60 | } 61 | itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local) 62 | if err != nil { 63 | return true 64 | } 65 | 66 | if time.Since(itemEffectiveTime) < 0 { 67 | return false 68 | } 69 | return true 70 | } 71 | 72 | return true 73 | } 74 | -------------------------------------------------------------------------------- /config/wol.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gdy666/lucky/thirdlib/gdylib/netinterfaces" 7 | "github.com/gdy666/lucky/thirdlib/gdylib/stringsp" 8 | "github.com/gdy666/lucky/thirdlib/go-wol" 9 | ) 10 | 11 | type WOLDevice struct { 12 | Key string 13 | DeviceName string 14 | MacList []string 15 | BroadcastIPs []string 16 | Port int 17 | Relay bool //交给中继设备发送 18 | Repeat int //重复发送次数 19 | } 20 | 21 | func (d *WOLDevice) WakeUp() error { 22 | return WakeOnLan(d.MacList, d.BroadcastIPs, d.Port, d.Repeat) 23 | } 24 | 25 | func WakeOnLan(macList []string, broadcastIps []string, port, repeat int) (err error) { 26 | globalBroadcastList := netinterfaces.GetGlobalIPv4BroadcastList() 27 | matchCount := 0 28 | 29 | defer func() { 30 | if matchCount <= 0 { 31 | err = fmt.Errorf("找不到匹配的局域网广播IP,未能发送唤醒指令") 32 | } 33 | }() 34 | 35 | if len(broadcastIps) > 0 { 36 | for _, bcst := range broadcastIps { 37 | bcstOk := stringsp.StrIsInList(bcst, globalBroadcastList) 38 | if !bcstOk { 39 | continue 40 | } 41 | matchCount++ 42 | for _, mac := range macList { 43 | wol.WakeUpRepeat(mac, bcst, "", port, repeat) 44 | } 45 | 46 | } 47 | return 48 | } 49 | 50 | for _, bcst := range globalBroadcastList { 51 | matchCount++ 52 | for _, mac := range macList { 53 | wol.WakeUpRepeat(mac, bcst, "", port, repeat) 54 | } 55 | } 56 | 57 | return 58 | } 59 | 60 | //---------------------------------------- 61 | 62 | func GetWOLDeviceByKey(key string) *WOLDevice { 63 | programConfigureMutex.Lock() 64 | defer programConfigureMutex.Unlock() 65 | index := -1 66 | 67 | for i := range programConfigure.WOLDeviceList { 68 | if programConfigure.WOLDeviceList[i].Key == key { 69 | index = i 70 | break 71 | } 72 | } 73 | 74 | if index < 0 { 75 | return nil 76 | } 77 | device := programConfigure.WOLDeviceList[index] 78 | return &device 79 | 80 | } 81 | 82 | func GetWOLDeviceList() []WOLDevice { 83 | programConfigureMutex.RLock() 84 | defer programConfigureMutex.RUnlock() 85 | var res []WOLDevice 86 | if programConfigure == nil { 87 | return res 88 | } 89 | 90 | for i := range programConfigure.WOLDeviceList { 91 | res = append(res, programConfigure.WOLDeviceList[i]) 92 | } 93 | return res 94 | } 95 | 96 | func WOLDeviceListAdd(d *WOLDevice) error { 97 | programConfigureMutex.Lock() 98 | defer programConfigureMutex.Unlock() 99 | 100 | if d.Key == "" { 101 | d.Key = stringsp.GetRandomString(8) 102 | } 103 | programConfigure.WOLDeviceList = append(programConfigure.WOLDeviceList, *d) 104 | return Save() 105 | } 106 | 107 | func WOLDeviceListAlter(d *WOLDevice) error { 108 | programConfigureMutex.Lock() 109 | defer programConfigureMutex.Unlock() 110 | index := -1 111 | for i := range programConfigure.WOLDeviceList { 112 | if programConfigure.WOLDeviceList[i].Key == d.Key { 113 | index = i 114 | break 115 | } 116 | } 117 | if index < 0 { 118 | return fmt.Errorf("key:%s 不存在", d.Key) 119 | } 120 | programConfigure.WOLDeviceList[index] = *d 121 | return Save() 122 | } 123 | 124 | func WOLDeviceListDelete(key string) error { 125 | programConfigureMutex.Lock() 126 | defer programConfigureMutex.Unlock() 127 | deleteIndex := -1 128 | 129 | for i := range programConfigure.WOLDeviceList { 130 | if programConfigure.WOLDeviceList[i].Key == key { 131 | deleteIndex = i 132 | break 133 | } 134 | } 135 | 136 | if deleteIndex < 0 { 137 | return fmt.Errorf("key:%s 不存在", key) 138 | } 139 | programConfigure.WOLDeviceList = DeleteWOLDeviceListslice(programConfigure.WOLDeviceList, deleteIndex) 140 | return Save() 141 | } 142 | 143 | func DeleteWOLDeviceListslice(a []WOLDevice, deleteIndex int) []WOLDevice { 144 | j := 0 145 | for i := range a { 146 | if i != deleteIndex { 147 | a[j] = a[i] 148 | j++ 149 | } 150 | } 151 | return a[:j] 152 | } 153 | -------------------------------------------------------------------------------- /ddns/alidns.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | 10 | "github.com/gdy666/lucky/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | "github.com/gdy666/lucky/thirdlib/jeessy2/ddns-go/util" 13 | ) 14 | 15 | const ( 16 | alidnsEndpoint string = "https://alidns.aliyuncs.com/" 17 | ) 18 | 19 | // https://help.aliyun.com/document_detail/29776.html?spm=a2c4g.11186623.6.672.715a45caji9dMA 20 | // Alidns Alidns 21 | type Alidns struct { 22 | DNSCommon 23 | TTL string 24 | } 25 | 26 | // AlidnsRecord record 27 | type AlidnsRecord struct { 28 | DomainName string 29 | RecordID string 30 | Value string 31 | } 32 | 33 | // AlidnsSubDomainRecords 记录 34 | type AlidnsSubDomainRecords struct { 35 | TotalCount int 36 | DomainRecords struct { 37 | Record []AlidnsRecord 38 | } 39 | } 40 | 41 | // AlidnsResp 修改/添加返回结果 42 | type AlidnsResp struct { 43 | RecordID string 44 | RequestID string 45 | } 46 | 47 | // Init 初始化 48 | func (ali *Alidns) Init(task *ddnscore.DDNSTaskInfo) { 49 | ali.DNSCommon.Init(task) 50 | 51 | if task.TTL == "" { 52 | // 默认600s 53 | ali.TTL = "600" 54 | } else { 55 | ali.TTL = task.TTL 56 | } 57 | ali.SetCreateUpdateDomainFunc(ali.createUpdateDomain) 58 | } 59 | 60 | func (ali *Alidns) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 61 | var records AlidnsSubDomainRecords 62 | // 获取当前域名信息 63 | params := domain.GetCustomParams() 64 | params.Set("Action", "DescribeSubDomainRecords") 65 | params.Set("DomainName", domain.DomainName) 66 | params.Set("SubDomain", domain.GetFullDomain()) 67 | params.Set("Type", recordType) 68 | err := ali.request(params, &records) 69 | 70 | if err != nil { 71 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error()) 72 | return 73 | } 74 | 75 | if records.TotalCount > 0 { 76 | // 默认第一个 77 | recordSelected := records.DomainRecords.Record[0] 78 | if params.Has("RecordId") { 79 | for i := 0; i < len(records.DomainRecords.Record); i++ { 80 | if records.DomainRecords.Record[i].RecordID == params.Get("RecordId") { 81 | recordSelected = records.DomainRecords.Record[i] 82 | } 83 | } 84 | } 85 | // 存在,更新 86 | ali.modify(recordSelected, domain, recordType, ipAddr) 87 | } else { 88 | // 不存在,创建 89 | ali.create(domain, recordType, ipAddr) 90 | } 91 | } 92 | 93 | // 创建 94 | func (ali *Alidns) create(domain *ddnscore.Domain, recordType string, ipAddr string) { 95 | params := domain.GetCustomParams() 96 | params.Set("Action", "AddDomainRecord") 97 | params.Set("DomainName", domain.DomainName) 98 | params.Set("RR", domain.GetSubDomain()) 99 | params.Set("Type", recordType) 100 | params.Set("Value", ipAddr) 101 | params.Set("TTL", ali.TTL) 102 | 103 | var result AlidnsResp 104 | err := ali.request(params, &result) 105 | 106 | if err == nil && result.RecordID != "" { 107 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 108 | } else { 109 | errMsg := fmt.Sprintf("创建域名失败:\n%v\n", result) 110 | if err != nil { 111 | errMsg += err.Error() 112 | } 113 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg) 114 | } 115 | } 116 | 117 | // 修改 118 | func (ali *Alidns) modify(recordSelected AlidnsRecord, domain *ddnscore.Domain, recordType string, ipAddr string) { 119 | 120 | // 相同不修改 121 | if recordSelected.Value == ipAddr { 122 | if domain.UpdateStatus == ddnscore.UpdatedFailed { 123 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 124 | } else { 125 | domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "") 126 | } 127 | return 128 | } 129 | 130 | params := domain.GetCustomParams() 131 | params.Set("Action", "UpdateDomainRecord") 132 | params.Set("RR", domain.GetSubDomain()) 133 | params.Set("RecordId", recordSelected.RecordID) 134 | params.Set("Type", recordType) 135 | params.Set("Value", ipAddr) 136 | params.Set("TTL", ali.TTL) 137 | 138 | var result AlidnsResp 139 | err := ali.request(params, &result) 140 | 141 | if err == nil && result.RecordID != "" { 142 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 143 | } else { 144 | errMsg := fmt.Sprintf("更新域名解析失败:%v\n", result) 145 | if err != nil { 146 | errMsg += err.Error() 147 | } 148 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg) 149 | } 150 | } 151 | 152 | // request 统一请求接口 153 | func (ali *Alidns) request(params url.Values, result interface{}) (err error) { 154 | 155 | util.AliyunSigner(ali.task.DNS.ID, ali.task.DNS.Secret, ¶ms) 156 | 157 | req, err := http.NewRequest( 158 | "GET", 159 | alidnsEndpoint, 160 | bytes.NewBuffer(nil), 161 | ) 162 | req.URL.RawQuery = params.Encode() 163 | 164 | if err != nil { 165 | log.Println("http.NewRequest失败. Error: ", err) 166 | return 167 | } 168 | 169 | client, err := ali.CreateHTTPClient() 170 | if err != nil { 171 | return err 172 | } 173 | 174 | resp, err := client.Do(req) 175 | if err != nil { 176 | return err 177 | } 178 | 179 | return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result) 180 | } 181 | -------------------------------------------------------------------------------- /ddns/callback.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | "time" 8 | 9 | "github.com/gdy666/lucky/config" 10 | "github.com/gdy666/lucky/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | ) 13 | 14 | type Callback struct { 15 | DNSCommon 16 | TTL string 17 | } 18 | 19 | // Init 初始化 20 | func (cb *Callback) Init(task *ddnscore.DDNSTaskInfo) { 21 | cb.DNSCommon.Init(task) 22 | 23 | if task.TTL == "" { 24 | // 默认600 25 | cb.TTL = "600" 26 | } else { 27 | cb.TTL = task.TTL 28 | } 29 | cb.SetCreateUpdateDomainFunc(cb.createUpdateDomain) 30 | } 31 | 32 | func CopyHeadersMap(sm map[string]string) map[string]string { 33 | dm := make(map[string]string) 34 | 35 | for k, v := range sm { 36 | dm[k] = v 37 | } 38 | 39 | return dm 40 | } 41 | 42 | func (cb *Callback) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 43 | 44 | url := replacePara(cb.task.DNS.Callback.URL, ipAddr, domain, recordType, cb.TTL) 45 | requestBody := replacePara(cb.task.DNS.Callback.RequestBody, ipAddr, domain, recordType, cb.TTL) 46 | 47 | //headersStr := cb.task.DNS.Callback.Headers 48 | var headerStrList []string 49 | for i := range cb.task.DNS.Callback.Headers { 50 | header := replacePara(cb.task.DNS.Callback.Headers[i], ipAddr, domain, recordType, cb.TTL) 51 | headerStrList = append(headerStrList, header) 52 | } 53 | 54 | headers := httputils.CreateHeadersMap(headerStrList) 55 | 56 | succcssCotentList := []string{} 57 | for i := range cb.task.DNS.Callback.CallbackSuccessContent { 58 | content := replacePara(cb.task.DNS.Callback.CallbackSuccessContent[i], ipAddr, domain, recordType, cb.TTL) 59 | succcssCotentList = append(succcssCotentList, content) 60 | } 61 | 62 | callErr := cb.CallbackHttpClientDo(cb.task.DNS.Callback.Method, url, requestBody, headers, succcssCotentList) 63 | 64 | if callErr != nil { 65 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, callErr.Error()) 66 | return 67 | } 68 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 69 | } 70 | 71 | // replacePara 替换参数 72 | func replacePara(orgPara, ipAddr string, domain *ddnscore.Domain, recordType string, ttl string) (newPara string) { 73 | orgPara = strings.ReplaceAll(orgPara, "#{ip}", ipAddr) 74 | orgPara = strings.ReplaceAll(orgPara, "#{domain}", domain.String()) 75 | orgPara = strings.ReplaceAll(orgPara, "#{recordType}", recordType) 76 | orgPara = strings.ReplaceAll(orgPara, "#{ttl}", ttl) 77 | 78 | for k, v := range domain.GetCustomParams() { 79 | if len(v) == 1 { 80 | orgPara = strings.ReplaceAll(orgPara, "#{"+k+"}", v[0]) 81 | } 82 | } 83 | 84 | return orgPara 85 | } 86 | 87 | func (cb *Callback) CallbackHttpClientDo(method, url, requestBody string, headers map[string]string, callbackSuccessContent []string) error { 88 | 89 | globalDDNSConf := config.GetDDNSConfigure() 90 | dnsConf := cb.task.DNS 91 | statusCode, respStr, err := httputils.GetStringGoutDoHttpRequest( 92 | "tcp", 93 | "", 94 | method, 95 | url, 96 | requestBody, 97 | dnsConf.HttpClientProxyType, 98 | dnsConf.HttpClientProxyAddr, 99 | dnsConf.HttpClientProxyUser, 100 | dnsConf.HttpClientProxyPassword, 101 | headers, 102 | !globalDDNSConf.HttpClientSecureVerify, 103 | time.Duration(cb.task.HttpClientTimeout)*time.Second) 104 | if err != nil { 105 | return fmt.Errorf("Callback 调用接口[%s]出错:%s", url, err.Error()) 106 | } 107 | 108 | if cb.task.DNS.Callback.DisableCallbackSuccessContentCheck { 109 | if statusCode == http.StatusOK { 110 | return nil 111 | } 112 | return fmt.Errorf("调用接口失败:\n statusCode:%d\n%s", statusCode, respStr) 113 | } 114 | 115 | //log.Printf("接口[%s]调用响应:%s\n", url, respStr) 116 | 117 | //fmt.Printf("statusCode:%d\n", statusCode) 118 | 119 | for _, successContent := range callbackSuccessContent { 120 | if strings.Contains(respStr, successContent) { 121 | return nil 122 | } 123 | } 124 | 125 | return fmt.Errorf("调用接口失败:\n%s", respStr) 126 | } 127 | -------------------------------------------------------------------------------- /ddns/dns.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import "github.com/gdy666/lucky/ddnscore.go" 4 | 5 | // DNS interface 6 | type DNS interface { 7 | Init(task *ddnscore.DDNSTaskInfo) 8 | // 添加或更新IPv4/IPv6记录 9 | AddUpdateDomainRecords() string 10 | } 11 | -------------------------------------------------------------------------------- /ddns/godaddy.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/gdy666/lucky/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | ) 13 | 14 | type godaddyRecord struct { 15 | Data string `json:"data"` 16 | Name string `json:"name"` 17 | TTL int `json:"ttl"` 18 | Type string `json:"type"` 19 | } 20 | 21 | type godaddyRecords []godaddyRecord 22 | 23 | type GoDaddy struct { 24 | DNSCommon 25 | TTL int 26 | header http.Header 27 | client *http.Client 28 | } 29 | 30 | // Init 初始化 31 | func (gd *GoDaddy) Init(task *ddnscore.DDNSTaskInfo) { 32 | gd.DNSCommon.Init(task) 33 | // if task.TTL == "" { 34 | // // 默认600s 35 | // gd.TTL = 600 36 | // } else { 37 | // gd.TTL = task.TTL 38 | // } 39 | if task.TTL == "" { 40 | // 默认300s 41 | gd.TTL = 600 42 | } else { 43 | ttl, err := strconv.Atoi(task.TTL) 44 | if err != nil { 45 | gd.TTL = 600 46 | } else { 47 | gd.TTL = ttl 48 | } 49 | } 50 | gd.header = map[string][]string{ 51 | "Authorization": {fmt.Sprintf("sso-key %s:%s", task.DNS.ID, task.DNS.Secret)}, 52 | "Content-Type": {"application/json"}, 53 | } 54 | //g.throttle, _ = util.GetThrottle(55) 55 | gd.client, _ = gd.CreateHTTPClient() 56 | 57 | gd.SetCreateUpdateDomainFunc(gd.createUpdateDomain) 58 | } 59 | 60 | func (gd *GoDaddy) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 61 | 62 | _, err := gd.sendReq(http.MethodPut, recordType, domain, &godaddyRecords{godaddyRecord{ 63 | Data: ipAddr, 64 | Name: domain.SubDomain, 65 | TTL: gd.TTL, 66 | Type: recordType, 67 | }}) 68 | if err != nil { 69 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error()) 70 | } 71 | } 72 | 73 | func (gd *GoDaddy) sendReq(method string, rType string, domain *ddnscore.Domain, data any) (*godaddyRecords, error) { 74 | 75 | var body *bytes.Buffer 76 | if data != nil { 77 | if buffer, err := json.Marshal(data); err != nil { 78 | return nil, err 79 | } else { 80 | body = bytes.NewBuffer(buffer) 81 | } 82 | } 83 | path := fmt.Sprintf("https://api.godaddy.com/v1/domains/%s/records/%s/%s", 84 | domain.DomainName, rType, domain.SubDomain) 85 | req, err := http.NewRequest(method, path, body) 86 | if err != nil { 87 | return nil, err 88 | } 89 | req.Header = gd.header 90 | 91 | resp, err := gd.client.Do(req) 92 | if err != nil { 93 | return nil, err 94 | } 95 | result := &godaddyRecords{} 96 | 97 | httputils.GetAndParseJSONResponseFromHttpResponse(resp, result) 98 | 99 | return result, nil 100 | } 101 | -------------------------------------------------------------------------------- /ddns/worker.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import ( 4 | "log" 5 | "runtime/debug" 6 | "sync" 7 | "time" 8 | 9 | "github.com/gdy666/lucky/config" 10 | "github.com/gdy666/lucky/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/service" 12 | ) 13 | 14 | var DDNSService *service.Service 15 | 16 | func init() { 17 | DDNSService, _ = service.NewService("ddns") 18 | DDNSService.SetTimerFunc(syncAllDomainsOnce) 19 | DDNSService.SetEventFunc(syncTaskDomainsOnce) 20 | } 21 | 22 | // Run 定时运行 23 | func Run(firstDelay time.Duration, delay time.Duration) { 24 | 25 | log.Printf("DDNS 第一次运行将等待 %d 秒后运行 (等待网络)", int(firstDelay.Seconds())) 26 | <-time.After(firstDelay) 27 | DDNSService.Start() 28 | } 29 | 30 | var wg sync.WaitGroup 31 | 32 | // RunOnce RunOnce 33 | func syncAllDomainsOnce(params ...any) { 34 | ddnsTaskList := ddnscore.GetDDNSTaskInfoList() 35 | ddnscore.CleanIPUrlAddrMap() 36 | ddnsConf := config.GetDDNSConfigure() 37 | 38 | //log.Printf("批量执行DDNS任务") 39 | taskBeginTime := time.Now() 40 | 41 | //fmt.Printf("ddnsTaskList:%v\n", ddnsTaskList) 42 | 43 | for index := range ddnsTaskList { 44 | 45 | task := ddnsTaskList[index] 46 | if !task.Enable { 47 | continue 48 | } 49 | 50 | if time.Since(task.TaskState.LastWorkTime) < time.Second*15 { 51 | //log.Printf("[%s]太接近,忽略", task.TaskName) 52 | continue 53 | } 54 | 55 | //log.Printf("task[%s] enable\n", task.TaskName) 56 | 57 | wg.Add(1) 58 | 59 | go func() { 60 | defer func() { 61 | wg.Done() 62 | recoverErr := recover() 63 | if recoverErr == nil { 64 | return 65 | } 66 | log.Printf("syncDDNSTask[%s]panic:\n%v", task.TaskName, recoverErr) 67 | log.Printf("%s", debug.Stack()) 68 | }() 69 | syncDDNSTask(task) 70 | }() 71 | 72 | <-time.After(time.Millisecond * 600) 73 | } 74 | wg.Wait() 75 | 76 | taskEndTime := time.Now() 77 | 78 | usedTime := taskEndTime.Sub(taskBeginTime) 79 | 80 | nextTaskTimer := time.Second*time.Duration(ddnsConf.Intervals) - usedTime 81 | 82 | //debug.FreeOSMemory() 83 | //log.Printf("syncAllDomainsOnce 任务完成") 84 | DDNSService.Timer = time.NewTimer(nextTaskTimer) 85 | } 86 | 87 | func syncTaskDomainsOnce(params ...any) { 88 | serverMsg := (params[1]).(service.ServiceMsg) 89 | taskKey := serverMsg.Params[0].(string) 90 | switch serverMsg.Type { 91 | case "syncDDNSTask": 92 | { 93 | //log.Printf("syncTaskDomainsOnce 单DDNS任务更新:%s", taskKey) 94 | ddnscore.CleanIPUrlAddrMap() 95 | task := ddnscore.GetDDNSTaskInfoByKey(taskKey) 96 | syncDDNSTask(task) 97 | } 98 | default: 99 | return 100 | } 101 | 102 | } 103 | 104 | func syncDDNSTask(task *ddnscore.DDNSTaskInfo) { 105 | if task == nil { 106 | return 107 | } 108 | var dnsSelected DNS 109 | switch task.DNS.Name { 110 | case "alidns": 111 | dnsSelected = &Alidns{} 112 | case "dnspod": 113 | dnsSelected = &Dnspod{} 114 | case "cloudflare": 115 | dnsSelected = &Cloudflare{} 116 | case "huaweicloud": 117 | dnsSelected = &Huaweicloud{} 118 | case "callback": 119 | dnsSelected = &Callback{} 120 | case "baiducloud": 121 | dnsSelected = &BaiduCloud{} 122 | case "porkbun": 123 | dnsSelected = &Porkbun{} 124 | default: 125 | return 126 | } 127 | 128 | dnsSelected.Init(task) 129 | 130 | dnsSelected.AddUpdateDomainRecords() 131 | task.ExecWebhook(&task.TaskState) 132 | ddnscore.DDNSTaskInfoMapUpdate(task) 133 | task.TaskState.LastWorkTime = time.Now() //记录最近一次检测时间,防止批量检测和单个检测时间间隔过于接近 134 | 135 | // 136 | } 137 | -------------------------------------------------------------------------------- /ddnscore.go/cache.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/gdy666/lucky/config" 11 | ) 12 | 13 | var taskInfoMap sync.Map 14 | var taskInfoMapMutex sync.RWMutex 15 | var webLastAccessDDNSTaskListLastTime int64 16 | 17 | // 记录最后的前端请求DDNS任务列表时间 18 | func FLushWebLastAccessDDNSTaskListLastTime() { 19 | atomic.StoreInt64(&webLastAccessDDNSTaskListLastTime, time.Now().Unix()) 20 | } 21 | 22 | // webAccessAvalid 判断前端访问是否处于活跃时间内 23 | func webAccessAvalid() bool { 24 | lastTime := atomic.LoadInt64(&webLastAccessDDNSTaskListLastTime) 25 | return time.Now().Unix()-lastTime <= 5 26 | } 27 | 28 | func EnableDDNSTaskByKey(key string, enable bool) error { 29 | taskInfoMapMutex.Lock() 30 | defer taskInfoMapMutex.Unlock() 31 | taskInfo, ok := taskInfoMap.Load(key) 32 | if !ok { 33 | return fmt.Errorf("DDNSTaskInfoMap key[%s] no found", key) 34 | } 35 | if enable { 36 | taskInfo.(*DDNSTaskState).SetDomainUpdateStatus(UpdateWaiting, "") 37 | } else { 38 | taskInfo.(*DDNSTaskState).SetDomainUpdateStatus(UpdateStop, "") 39 | } 40 | return config.EnableDDNSTaskByKey(key, enable) 41 | } 42 | 43 | func DDNSTaskInfoMapUpdate(task *DDNSTaskInfo) { 44 | taskInfoMapMutex.Lock() 45 | defer taskInfoMapMutex.Unlock() 46 | 47 | preInfo, ok := taskInfoMap.Load(task.TaskKey) 48 | if ok { 49 | var checkDomains []Domain 50 | //防止有域名被删除 51 | for i, new := range task.TaskState.Domains { 52 | for _, pre := range preInfo.(*DDNSTaskState).Domains { 53 | if strings.Compare(new.String(), pre.String()) == 0 { 54 | checkDomains = append(checkDomains, task.TaskState.Domains[i]) 55 | break 56 | } 57 | } 58 | } 59 | task.TaskState.Domains = checkDomains 60 | 61 | if len(preInfo.(*DDNSTaskState).Domains) > 0 && preInfo.(*DDNSTaskState).Domains[0].UpdateStatus == UpdateStop { 62 | task.TaskState.SetDomainUpdateStatus(UpdateStop, "") 63 | } 64 | 65 | taskInfoMap.Store(task.TaskKey, &task.TaskState) 66 | return 67 | } 68 | } 69 | 70 | // 即时更新IP相关数据信息 71 | func DDNSTaskInfoMapUpdateIPInfo(task *DDNSTaskInfo) { 72 | if !webAccessAvalid() { 73 | //log.Printf("前端没有访问,不即时更新") 74 | return 75 | } 76 | //log.Printf("前端没有访问,不即时更新") 77 | 78 | taskInfoMapMutex.Lock() 79 | defer taskInfoMapMutex.Unlock() 80 | state, ok := taskInfoMap.Load(task.TaskKey) 81 | if !ok { 82 | return 83 | } 84 | state.(*DDNSTaskState).IpAddr = task.TaskState.IpAddr 85 | state.(*DDNSTaskState).IPAddrHistory = task.TaskState.IPAddrHistory 86 | } 87 | 88 | func DDNSTaskInfoMapUpdateDomainInfo(task *DDNSTaskInfo) { 89 | if !webAccessAvalid() { 90 | //log.Printf("前端没有访问,不即时更新") 91 | return 92 | } 93 | //log.Printf("前端有访问,即时更新") 94 | 95 | taskInfoMapMutex.Lock() 96 | defer taskInfoMapMutex.Unlock() 97 | state, ok := taskInfoMap.Load(task.TaskKey) 98 | if !ok { 99 | return 100 | } 101 | state.(*DDNSTaskState).Domains = task.TaskState.Domains 102 | } 103 | 104 | func DDNSTaskInfoMapDelete(key string) { 105 | taskInfoMapMutex.Lock() 106 | defer taskInfoMapMutex.Unlock() 107 | taskInfoMap.Delete(key) 108 | } 109 | 110 | func UpdateDomainsStateByTaskKey(key, status, message string) { 111 | taskInfoMapMutex.Lock() 112 | defer taskInfoMapMutex.Unlock() 113 | preInfo, ok := taskInfoMap.Load(key) 114 | if !ok { 115 | return 116 | } 117 | preInfo.(*DDNSTaskState).SetDomainUpdateStatus(status, message) 118 | } 119 | 120 | func GetDDNSTaskInfoList() []*DDNSTaskInfo { 121 | taskInfoMapMutex.RLock() 122 | defer taskInfoMapMutex.RUnlock() 123 | ddnsTaskList := config.GetDDNSTaskConfigureList() 124 | var res []*DDNSTaskInfo 125 | for i := range ddnsTaskList { 126 | ti := CreateDDNSTaskInfo(ddnsTaskList[i]) 127 | res = append(res, ti) 128 | } 129 | return res 130 | } 131 | 132 | func GetDDNSTaskInfoByKey(key string) *DDNSTaskInfo { 133 | taskInfoMapMutex.RLock() 134 | defer taskInfoMapMutex.RUnlock() 135 | ddnsConf := config.GetDDNSTaskByKey(key) 136 | if ddnsConf == nil { 137 | return nil 138 | } 139 | info := CreateDDNSTaskInfo(ddnsConf) 140 | return info 141 | } 142 | 143 | func CreateDDNSTaskInfo(task *config.DDNSTask) *DDNSTaskInfo { 144 | var res DDNSTaskInfo 145 | res.DDNSTask = *task 146 | info, ok := taskInfoMap.Load(task.TaskKey) 147 | if ok { 148 | res.TaskState = *info.(*DDNSTaskState) 149 | } else { 150 | res.TaskState.Init(res.Domains) 151 | if task.Enable { 152 | res.TaskState.SetDomainUpdateStatus(UpdateWaiting, "") 153 | } else { 154 | res.TaskState.SetDomainUpdateStatus(UpdateStop, "") 155 | } 156 | taskInfoMap.Store(task.TaskKey, &res.TaskState) 157 | } 158 | return &res 159 | } 160 | -------------------------------------------------------------------------------- /ddnscore.go/domain.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | "net/url" 5 | "time" 6 | ) 7 | 8 | const ( 9 | // UpdatedNothing 未改变 10 | UpdatedNothing string = "域名IP和公网IP一致" 11 | // UpdatedFailed 更新失败 12 | UpdatedFailed = "失败" 13 | // UpdatedSuccess 更新成功 14 | UpdatedSuccess = "成功" 15 | // UpdateStop 暂停 16 | UpdateStop = "停止同步" 17 | //UpdatePause 暂停 获取IP失败时暂停 18 | UpdatePause = "暂停同步" 19 | // UpdateWaiting 20 | UpdateWaiting = "等待更新" 21 | ) 22 | 23 | // Domain 域名实体 24 | type Domain struct { 25 | DomainName string 26 | SubDomain string 27 | CustomParams string 28 | 29 | UpdateStatus string // 更新状态 30 | LastUpdateStatusTime string //最后更新状态的时间 31 | Message string 32 | UpdateHistroy []any 33 | } 34 | 35 | type UpdateHistroyItem struct { 36 | UpdateStatus string 37 | UpdateTime string 38 | } 39 | 40 | func (d *Domain) String() string { 41 | if d.SubDomain != "" { 42 | return d.SubDomain + "." + d.DomainName 43 | } 44 | return d.DomainName 45 | } 46 | 47 | // GetFullDomain 返回完整子域名 48 | func (d *Domain) GetFullDomain() string { 49 | if d.SubDomain != "" { 50 | return d.SubDomain + "." + d.DomainName 51 | } 52 | return "@" + "." + d.DomainName 53 | } 54 | 55 | // GetCustomParams not be nil 56 | func (d *Domain) GetCustomParams() url.Values { 57 | if d.CustomParams != "" { 58 | q, err := url.ParseQuery(d.CustomParams) 59 | if err == nil { 60 | return q 61 | } 62 | } 63 | return url.Values{} 64 | } 65 | 66 | // GetSubDomain 获得子域名,为空返回@ 67 | // 阿里云,dnspod需要 68 | func (d *Domain) GetSubDomain() string { 69 | if d.SubDomain != "" { 70 | return d.SubDomain 71 | } 72 | return "@" 73 | } 74 | 75 | func (d *Domain) SetDomainUpdateStatus(status string, message string) { 76 | 77 | if status != UpdateWaiting { 78 | if status != UpdateStop || d.UpdateStatus != UpdateStop { 79 | d.LastUpdateStatusTime = time.Now().Format("2006-01-02 15:04:05") 80 | // 状态更新历史记录 81 | hi := UpdateHistroyItem{UpdateStatus: string(status), UpdateTime: d.LastUpdateStatusTime} 82 | d.UpdateHistroy = append(d.UpdateHistroy, hi) 83 | if len(d.UpdateHistroy) > 10 { 84 | d.UpdateHistroy = DeleteAnyListlice(d.UpdateHistroy, 0) 85 | } 86 | } 87 | } 88 | d.UpdateStatus = status 89 | d.Message = message 90 | 91 | } 92 | -------------------------------------------------------------------------------- /ddnscore.go/taskstate.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | "log" 5 | "net/url" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | // 固定的主域名 11 | var staticMainDomains = []string{"com.cn", "org.cn", "net.cn", "ac.cn", "eu.org"} 12 | 13 | // 获取ip失败的次数 14 | 15 | // Domains Ipv4/Ipv6 DDNSTaskState 16 | type DDNSTaskState struct { 17 | IpAddr string 18 | Domains []Domain 19 | WebhookCallTime string `json:"WebhookCallTime"` //最后触发时间 20 | WebhookCallResult bool `json:"WebhookCallResult"` //触发结果 21 | WebhookCallErrorMsg string `json:"WebhookCallErrorMsg"` //触发错误信息 22 | LastSyncTime time.Time `json:"-"` //记录最新一次同步操作时间 23 | LastWorkTime time.Time `json:"-"` 24 | 25 | IPAddrHistory []any `json:"IPAddrHistory"` 26 | WebhookCallHistroy []any `json:"WebhookCallHistroy"` 27 | } 28 | 29 | type IPAddrHistoryItem struct { 30 | IPaddr string 31 | RecordTime string 32 | } 33 | 34 | type WebhookCallHistroyItem struct { 35 | CallTime string 36 | CallResult string 37 | } 38 | 39 | func (d *DDNSTaskState) SetIPAddr(ipaddr string) { 40 | if d.IpAddr == ipaddr { 41 | return 42 | } 43 | 44 | d.IpAddr = ipaddr 45 | 46 | hi := IPAddrHistoryItem{IPaddr: ipaddr, RecordTime: time.Now().Local().Format("2006-01-02 15:04:05")} 47 | d.IPAddrHistory = append(d.IPAddrHistory, hi) 48 | 49 | if len(d.IPAddrHistory) > 10 { 50 | d.IPAddrHistory = DeleteAnyListlice(d.IPAddrHistory, 0) 51 | } 52 | } 53 | 54 | func DeleteAnyListlice(a []any, deleteIndex int) []any { 55 | j := 0 56 | for i := range a { 57 | if i != deleteIndex { 58 | a[j] = a[i] 59 | j++ 60 | } 61 | } 62 | return a[:j] 63 | } 64 | 65 | func (d *DDNSTaskState) SetDomainUpdateStatus(status string, message string) { 66 | for i := range d.Domains { 67 | d.Domains[i].SetDomainUpdateStatus(status, message) 68 | } 69 | } 70 | 71 | func (d *DDNSTaskState) SetWebhookResult(result bool, errMsg string) { 72 | d.WebhookCallResult = result 73 | d.WebhookCallErrorMsg = errMsg 74 | d.WebhookCallTime = time.Now().Format("2006-01-02 15:04:05") 75 | 76 | cr := "成功" 77 | if !result { 78 | cr = "出错" 79 | } 80 | 81 | hi := WebhookCallHistroyItem{CallResult: cr, CallTime: time.Now().Local().Format("2006-01-02 15:04:05")} 82 | d.WebhookCallHistroy = append(d.WebhookCallHistroy, hi) 83 | if len(d.WebhookCallHistroy) > 10 { 84 | d.WebhookCallHistroy = DeleteAnyListlice(d.WebhookCallHistroy, 0) 85 | } 86 | } 87 | 88 | func (d *DDNSTaskState) Init(domains []string) { 89 | d.Domains = d.checkParseDomains(domains) 90 | 91 | } 92 | 93 | // checkParseDomains 校验并解析用户输入的域名 94 | func (d *DDNSTaskState) checkParseDomains(domainArr []string) (domains []Domain) { 95 | for _, domainStr := range domainArr { 96 | domainStr = strings.TrimSpace(domainStr) 97 | if domainStr != "" { 98 | domain := &Domain{} 99 | 100 | dp := strings.Split(domainStr, ":") 101 | dplen := len(dp) 102 | if dplen == 1 { // 自动识别域名 103 | sp := strings.Split(domainStr, ".") 104 | length := len(sp) 105 | if length <= 1 { 106 | log.Println(domainStr, "域名不正确") 107 | continue 108 | } 109 | // 处理域名 110 | domain.DomainName = sp[length-2] + "." + sp[length-1] 111 | // 如包含在org.cn等顶级域名下,后三个才为用户主域名 112 | for _, staticMainDomain := range staticMainDomains { 113 | if staticMainDomain == domain.DomainName { 114 | domain.DomainName = sp[length-3] + "." + domain.DomainName 115 | break 116 | } 117 | } 118 | 119 | domainLen := len(domainStr) - len(domain.DomainName) 120 | if domainLen > 0 { 121 | domain.SubDomain = domainStr[:domainLen-1] 122 | } else { 123 | domain.SubDomain = domainStr[:domainLen] 124 | } 125 | 126 | } else if dplen == 2 { // 主机记录:域名 格式 127 | sp := strings.Split(dp[1], ".") 128 | length := len(sp) 129 | if length <= 1 { 130 | log.Println(domainStr, "域名不正确") 131 | continue 132 | } 133 | domain.DomainName = dp[1] 134 | domain.SubDomain = dp[0] 135 | } else { 136 | log.Println(domainStr, "域名不正确") 137 | continue 138 | } 139 | 140 | // 参数条件 141 | if strings.Contains(domain.DomainName, "?") { 142 | u, err := url.Parse("http://" + domain.DomainName) 143 | if err != nil { 144 | log.Println(domainStr, "域名解析失败") 145 | continue 146 | } 147 | domain.DomainName = u.Host 148 | domain.CustomParams = u.Query().Encode() 149 | } 150 | domains = append(domains, *domain) 151 | } 152 | } 153 | return 154 | } 155 | 156 | // Check 检测IP是否有改变 157 | func (state *DDNSTaskState) IPChangeCheck(newAddr string) bool { 158 | if newAddr == "" { 159 | return true 160 | } 161 | // 地址改变 162 | if state.IpAddr != newAddr { 163 | //log.Printf("公网地址改变:[%s]===>[%s]", d.DomainsInfo.IpAddr, newAddr) 164 | //domains.IpAddr = newAddr 165 | return true 166 | } 167 | 168 | return false 169 | } 170 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | //go:build debug 2 | // +build debug 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/gdy666/lucky/thirdlib/gdylib/recoverutil" 10 | ) 11 | 12 | func init() { 13 | defer func() { 14 | recoverErr := recover() 15 | if recoverErr == nil { 16 | return 17 | } 18 | panicFile := fmt.Sprintf("闪退.log") 19 | recoverutil.RecoverHandler(recoverErr, true, true, panicFile) 20 | }() 21 | } 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gdy666/lucky 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/buger/jsonparser v1.1.1 7 | github.com/eclipse/paho.mqtt.golang v1.4.1 8 | github.com/fatedier/golib v0.2.0 9 | github.com/gin-contrib/gzip v0.0.6 10 | github.com/gin-gonic/gin v1.8.1 11 | github.com/golang-jwt/jwt v3.2.2+incompatible 12 | github.com/guonaihong/gout v0.3.1 13 | github.com/miekg/dns v1.1.50 14 | github.com/shirou/gopsutil/v3 v3.22.9 15 | github.com/sirupsen/logrus v1.9.0 16 | golang.org/x/net v0.0.0-20221004154528-8021a29435af 17 | ) 18 | 19 | require ( 20 | github.com/gin-contrib/sse v0.1.0 // indirect 21 | github.com/go-ole/go-ole v1.2.6 // indirect 22 | github.com/go-playground/locales v0.14.0 // indirect 23 | github.com/go-playground/universal-translator v0.18.0 // indirect 24 | github.com/go-playground/validator/v10 v10.11.1 // indirect 25 | github.com/goccy/go-json v0.9.11 // indirect 26 | github.com/gorilla/websocket v1.4.2 // indirect 27 | github.com/json-iterator/go v1.1.12 // indirect 28 | github.com/leodido/go-urn v1.2.1 // indirect 29 | github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect 30 | github.com/mattn/go-isatty v0.0.16 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 32 | github.com/modern-go/reflect2 v1.0.2 // indirect 33 | github.com/pelletier/go-toml/v2 v2.0.5 // indirect 34 | github.com/pkg/errors v0.9.1 // indirect 35 | github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect 36 | github.com/tklauser/go-sysconf v0.3.10 // indirect 37 | github.com/tklauser/numcpus v0.5.0 // indirect 38 | github.com/ugorji/go/codec v1.2.7 // indirect 39 | github.com/yusufpapurcu/wmi v1.2.2 // indirect 40 | golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 // indirect 41 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect 42 | golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect 43 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect 44 | golang.org/x/text v0.3.8 // indirect 45 | golang.org/x/tools v0.1.12 // indirect 46 | google.golang.org/protobuf v1.28.1 // indirect 47 | gopkg.in/yaml.v2 v2.4.0 // indirect 48 | ) 49 | -------------------------------------------------------------------------------- /goports: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/goports -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //Copyright 2022 gdy, 272288813@qq.com 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "log" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/gdy666/lucky/config" 14 | "github.com/gdy666/lucky/ddns" 15 | "github.com/gdy666/lucky/reverseproxy" 16 | "github.com/gdy666/lucky/socketproxy" 17 | ) 18 | 19 | var ( 20 | listenPort = flag.Int("p", 16601, "http Admin Web listen port ") 21 | configureFileURL = flag.String("c", "", "configure file url") 22 | ) 23 | 24 | var ( 25 | runMode = "prod" 26 | version = "dev" 27 | commit = "none" 28 | date = "2022-07-27T17:54:45Z" 29 | ) 30 | 31 | var runTime time.Time 32 | 33 | func init() { 34 | var cstZone = time.FixedZone("CST", 8*3600) // 东八 35 | time.Local = cstZone 36 | } 37 | 38 | func main() { 39 | flag.Parse() 40 | config.InitAppInfo(version, date) 41 | 42 | err := config.Read(*configureFileURL) 43 | if err != nil { 44 | log.Printf("%s", err.Error()) 45 | log.Printf("载入默认配置以及命令行设定的参数") 46 | config.LoadDefault(*listenPort) 47 | if len(*configureFileURL) > 0 { 48 | err = config.Save() 49 | if err != nil { 50 | log.Printf("保存配置到%s出错:%s", *configureFileURL, err.Error()) 51 | } 52 | } 53 | } 54 | 55 | gcf := config.GetConfig() 56 | 57 | config.BlackListInit() 58 | config.WhiteListInit() 59 | config.SSLCertficateListInit() 60 | 61 | //fmt.Printf("*gcf:%v\n", *gcf) 62 | 63 | socketproxy.SetSafeCheck(config.SafeCheck) 64 | //socketproxy.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections) 65 | //socketproxy.SetGlobalMaxProxyCount(gcf.BaseConfigure.ProxyCountLimit) 66 | config.SetRunMode(runMode) 67 | config.SetVersion(version) 68 | log.Printf("RunMode:%s\n", runMode) 69 | log.Printf("version:%s\tcommit %s, built at %s\n", version, commit, date) 70 | 71 | RunAdminWeb(&gcf.BaseConfigure) 72 | 73 | runTime = time.Now() 74 | 75 | //LoadRuleFromConfigFile(gcf) 76 | 77 | config.PortForwardsRuleListInit() 78 | 79 | //config.DDNSTaskListTaskDetailsInit() 80 | config.DDNSTaskListConfigureCheck() 81 | ddnsConf := config.GetDDNSConfigure() 82 | if ddnsConf.Enable { 83 | go ddns.Run(time.Duration(ddnsConf.FirstCheckDelay)*time.Second, time.Duration(ddnsConf.Intervals)*time.Second) 84 | } 85 | 86 | reverseproxy.InitReverseProxyServer() 87 | 88 | //ddns.RunTimer(time.Second, time.Second*30) 89 | 90 | //initProxyList() 91 | 92 | //***************** 93 | // time.Sleep(time.Microsecond * 50) 94 | // cruuentPath, _ := fileutils.GetCurrentDirectory() 95 | 96 | // panicFile := fmt.Sprintf("%s/relayport_panic.log", cruuentPath) 97 | // fileutils.PanicRedirect(panicFile) 98 | //***************** 99 | 100 | //main goroutine wait 101 | sigs := make(chan os.Signal, 1) 102 | exit := make(chan bool, 1) 103 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 104 | go func() { 105 | <-sigs 106 | exit <- true 107 | }() 108 | <-exit 109 | } 110 | 111 | // func LoadRuleFromConfigFile(pc *config.ProgramConfigure) { 112 | // if pc == nil { 113 | // return 114 | // } 115 | // for i := range pc.RelayRuleList { 116 | // relayRule, err := rule.CreateRuleByConfigureAndOptions( 117 | // pc.RelayRuleList[i].Name, 118 | // pc.RelayRuleList[i].Configurestr, 119 | // pc.RelayRuleList[i].Options) 120 | // if err != nil { 121 | // continue 122 | // } 123 | // relayRule.From = "configureFile" //规则来源 124 | // relayRule.IsEnable = pc.RelayRuleList[i].Enable 125 | 126 | // _, e := rule.AddRuleToGlobalRuleList(false, *relayRule) 127 | // if e != nil { 128 | // log.Printf("%s\n", e) 129 | // } 130 | // } 131 | // } 132 | -------------------------------------------------------------------------------- /module/ddns/ddnscore.go/domain.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | "log" 5 | "net/url" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | const ( 11 | // UpdatedNothing 未改变 12 | UpdatedNothing string = "域名IP和公网IP一致" 13 | // UpdatedFailed 更新失败 14 | UpdatedFailed = "失败" 15 | // UpdatedSuccess 更新成功 16 | UpdatedSuccess = "成功" 17 | // UpdateStop 暂停 18 | UpdateStop = "停止同步" 19 | //UpdatePause 暂停 获取IP失败时暂停 20 | UpdatePause = "暂停同步" 21 | // UpdateWaiting 22 | UpdateWaiting = "等待更新" 23 | ) 24 | 25 | // Domain 域名实体 26 | type Domain struct { 27 | RawStr string 28 | DomainName string 29 | SubDomain string 30 | CustomParams string 31 | 32 | UpdateStatus string // 更新状态 33 | LastUpdateStatusTime string //最后更新状态的时间 34 | Message string 35 | UpdateHistroy []any 36 | } 37 | 38 | type UpdateHistroyItem struct { 39 | UpdateStatus string 40 | UpdateTime string 41 | } 42 | 43 | func (d *Domain) String() string { 44 | if d.SubDomain != "" { 45 | return d.SubDomain + "." + d.DomainName 46 | } 47 | return d.DomainName 48 | } 49 | 50 | // GetFullDomain 返回完整子域名 51 | func (d *Domain) GetFullDomain() string { 52 | if d.SubDomain != "" { 53 | return d.SubDomain + "." + d.DomainName 54 | } 55 | return "@" + "." + d.DomainName 56 | } 57 | 58 | // GetCustomParams not be nil 59 | func (d *Domain) GetCustomParams() url.Values { 60 | if d.CustomParams != "" { 61 | q, err := url.ParseQuery(d.CustomParams) 62 | if err == nil { 63 | return q 64 | } 65 | } 66 | return url.Values{} 67 | } 68 | 69 | // GetSubDomain 获得子域名,为空返回@ 70 | // 阿里云,dnspod需要 71 | func (d *Domain) GetSubDomain() string { 72 | if d.SubDomain != "" { 73 | return d.SubDomain 74 | } 75 | return "@" 76 | } 77 | 78 | func (d *Domain) SetDomainUpdateStatus(status string, message string) { 79 | 80 | if status != UpdateWaiting { 81 | if status != UpdateStop || d.UpdateStatus != UpdateStop { 82 | d.LastUpdateStatusTime = time.Now().Format("2006-01-02 15:04:05") 83 | // 状态更新历史记录 84 | hi := UpdateHistroyItem{UpdateStatus: string(status), UpdateTime: d.LastUpdateStatusTime} 85 | d.UpdateHistroy = append(d.UpdateHistroy, hi) 86 | if len(d.UpdateHistroy) > 10 { 87 | d.UpdateHistroy = DeleteAnyListlice(d.UpdateHistroy, 0) 88 | } 89 | } 90 | } 91 | d.UpdateStatus = status 92 | d.Message = message 93 | } 94 | 95 | func checkParseDomains(domainArr []string) (domains []Domain, domainsRawStrList []string) { 96 | for _, domainStr := range domainArr { 97 | domainStr = strings.TrimSpace(domainStr) 98 | 99 | if domainStr == "" { 100 | continue 101 | } 102 | 103 | domain := Domain{} 104 | domain.RawStr = domainStr 105 | dp := strings.Split(domainStr, ":") 106 | dplen := len(dp) 107 | 108 | switch dplen { 109 | case 1: 110 | { 111 | sp := strings.Split(domainStr, ".") 112 | length := len(sp) 113 | if length <= 1 { 114 | log.Println(domainStr, "域名不正确") 115 | continue 116 | } 117 | // 处理域名 118 | domain.DomainName = sp[length-2] + "." + sp[length-1] 119 | // 如包含在org.cn等顶级域名下,后三个才为用户主域名 120 | for _, staticMainDomain := range staticMainDomains { 121 | if staticMainDomain == domain.DomainName { 122 | domain.DomainName = sp[length-3] + "." + domain.DomainName 123 | break 124 | } 125 | } 126 | 127 | domainLen := len(domainStr) - len(domain.DomainName) 128 | if domainLen > 0 { 129 | domain.SubDomain = domainStr[:domainLen-1] 130 | } else { 131 | domain.SubDomain = domainStr[:domainLen] 132 | } 133 | 134 | } 135 | case 2: 136 | { 137 | sp := strings.Split(dp[1], ".") 138 | length := len(sp) 139 | if length <= 1 { 140 | log.Println(domainStr, "域名不正确") 141 | continue 142 | } 143 | domain.DomainName = dp[1] 144 | domain.SubDomain = dp[0] 145 | } 146 | default: 147 | { 148 | log.Println(domainStr, "域名不正确") 149 | continue 150 | } 151 | } 152 | 153 | if strings.Contains(domain.DomainName, "?") { 154 | u, err := url.Parse("http://" + domain.DomainName) 155 | if err != nil { 156 | log.Println(domainStr, "域名解析失败") 157 | continue 158 | } 159 | domain.DomainName = u.Host 160 | domain.CustomParams = u.Query().Encode() 161 | } 162 | domains = append(domains, domain) 163 | domainsRawStrList = append(domainsRawStrList, domainStr) 164 | 165 | } 166 | return 167 | } 168 | -------------------------------------------------------------------------------- /module/ddns/ddnscore.go/taskinfo.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | ddnsconf "github.com/gdy666/lucky/module/ddns/conf" 5 | ) 6 | 7 | type DDNSTaskInfo struct { 8 | ddnsconf.DDNSTask 9 | TaskState DDNSTaskState `json:"TaskState"` 10 | } 11 | 12 | // CheckIPChange 检测公网IP是否改变 13 | func (d *DDNSTaskInfo) CheckIPChange() (ipAddr string, change bool) { 14 | ipAddr = d.GetIpAddr() 15 | checkIPChange := d.TaskState.IPChanged(ipAddr) 16 | if checkIPChange { 17 | return ipAddr, true 18 | } 19 | //IP没变化 20 | return ipAddr, false 21 | } 22 | 23 | func (d *DDNSTaskInfo) SyncDomains() { 24 | if d.ModifyTime == d.TaskState.ModifyTime { 25 | //fmt.Printf("不需要syncDomains\n") 26 | return 27 | } 28 | //fmt.Printf("需要syncDomains\n") 29 | domains, _ := checkParseDomains(d.Domains) 30 | 31 | for i := range domains { 32 | index := getDomainIndex(d.TaskState.Domains, &domains[i]) 33 | if index < 0 { 34 | continue 35 | } 36 | domains[i] = d.TaskState.Domains[index] 37 | } 38 | d.TaskState.Domains = domains 39 | d.TaskState.ModifyTime = d.ModifyTime 40 | taskInfoMap.Store(d.TaskKey, &d.TaskState) 41 | } 42 | 43 | func getDomainIndex(domains []Domain, domain *Domain) (index int) { 44 | index = -1 45 | for i := range domains { 46 | if domains[i].RawStr == domain.RawStr { 47 | index = i 48 | return 49 | } 50 | } 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /module/ddns/ddnscore.go/taskstate.go: -------------------------------------------------------------------------------- 1 | package ddnscore 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // 固定的主域名 8 | var staticMainDomains = []string{"com.cn", "org.cn", "net.cn", "ac.cn", "eu.org"} 9 | 10 | // 获取ip失败的次数 11 | 12 | // Domains Ipv4/Ipv6 DDNSTaskState 13 | type DDNSTaskState struct { 14 | IpAddr string 15 | Domains []Domain 16 | domainsRawStrList []string 17 | WebhookCallTime string `json:"WebhookCallTime"` //最后触发时间 18 | WebhookCallResult bool `json:"WebhookCallResult"` //触发结果 19 | WebhookCallErrorMsg string `json:"WebhookCallErrorMsg"` //触发错误信息 20 | LastSyncTime time.Time `json:"-"` //记录最新一次同步操作时间 21 | LastWorkTime time.Time `json:"-"` 22 | 23 | IPAddrHistory []any `json:"IPAddrHistory"` 24 | WebhookCallHistroy []any `json:"WebhookCallHistroy"` 25 | ModifyTime int64 26 | } 27 | 28 | type IPAddrHistoryItem struct { 29 | IPaddr string 30 | RecordTime string 31 | } 32 | 33 | type WebhookCallHistroyItem struct { 34 | CallTime string 35 | CallResult string 36 | } 37 | 38 | func (d *DDNSTaskState) SetIPAddr(ipaddr string) { 39 | if d.IpAddr == ipaddr { 40 | return 41 | } 42 | 43 | d.IpAddr = ipaddr 44 | 45 | hi := IPAddrHistoryItem{IPaddr: ipaddr, RecordTime: time.Now().Local().Format("2006-01-02 15:04:05")} 46 | d.IPAddrHistory = append(d.IPAddrHistory, hi) 47 | 48 | if len(d.IPAddrHistory) > 10 { 49 | d.IPAddrHistory = DeleteAnyListlice(d.IPAddrHistory, 0) 50 | } 51 | } 52 | 53 | func DeleteAnyListlice(a []any, deleteIndex int) []any { 54 | j := 0 55 | for i := range a { 56 | if i != deleteIndex { 57 | a[j] = a[i] 58 | j++ 59 | } 60 | } 61 | return a[:j] 62 | } 63 | 64 | func (d *DDNSTaskState) SetDomainUpdateStatus(status string, message string) { 65 | for i := range d.Domains { 66 | d.Domains[i].SetDomainUpdateStatus(status, message) 67 | } 68 | } 69 | 70 | func (d *DDNSTaskState) SetWebhookResult(result bool, errMsg string) { 71 | d.WebhookCallResult = result 72 | d.WebhookCallErrorMsg = errMsg 73 | d.WebhookCallTime = time.Now().Format("2006-01-02 15:04:05") 74 | 75 | cr := "成功" 76 | if !result { 77 | cr = "出错" 78 | } 79 | 80 | hi := WebhookCallHistroyItem{CallResult: cr, CallTime: time.Now().Local().Format("2006-01-02 15:04:05")} 81 | d.WebhookCallHistroy = append(d.WebhookCallHistroy, hi) 82 | if len(d.WebhookCallHistroy) > 10 { 83 | d.WebhookCallHistroy = DeleteAnyListlice(d.WebhookCallHistroy, 0) 84 | } 85 | } 86 | 87 | func (d *DDNSTaskState) Init(domains []string, mt int64) { 88 | d.Domains, d.domainsRawStrList = checkParseDomains(domains) 89 | d.ModifyTime = mt 90 | } 91 | 92 | // Check 检测IP是否有改变 93 | func (d *DDNSTaskState) IPChanged(newAddr string) bool { 94 | if newAddr == "" { 95 | return true 96 | } 97 | // 地址改变 98 | if d.IpAddr != newAddr { 99 | //log.Printf("公网地址改变:[%s]===>[%s]", d.DomainsInfo.IpAddr, newAddr) 100 | //domains.IpAddr = newAddr 101 | return true 102 | } 103 | 104 | return false 105 | } 106 | -------------------------------------------------------------------------------- /module/ddns/providers/alidns.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | 10 | "github.com/gdy666/lucky/module/ddns/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | "github.com/gdy666/lucky/thirdlib/jeessy2/ddns-go/util" 13 | ) 14 | 15 | const ( 16 | alidnsEndpoint string = "https://alidns.aliyuncs.com/" 17 | ) 18 | 19 | // https://help.aliyun.com/document_detail/29776.html?spm=a2c4g.11186623.6.672.715a45caji9dMA 20 | // Alidns Alidns 21 | type Alidns struct { 22 | ProviderCommon 23 | TTL string 24 | } 25 | 26 | // AlidnsRecord record 27 | type AlidnsRecord struct { 28 | DomainName string 29 | RecordID string 30 | Value string 31 | } 32 | 33 | // AlidnsSubDomainRecords 记录 34 | type AlidnsSubDomainRecords struct { 35 | TotalCount int 36 | DomainRecords struct { 37 | Record []AlidnsRecord 38 | } 39 | } 40 | 41 | // AlidnsResp 修改/添加返回结果 42 | type AlidnsResp struct { 43 | RecordID string 44 | RequestID string 45 | } 46 | 47 | // Init 初始化 48 | func (ali *Alidns) Init(task *ddnscore.DDNSTaskInfo) { 49 | ali.ProviderCommon.Init(task) 50 | 51 | if task.TTL == "" { 52 | // 默认600s 53 | ali.TTL = "600" 54 | } else { 55 | ali.TTL = task.TTL 56 | } 57 | ali.SetCreateUpdateDomainFunc(ali.createUpdateDomain) 58 | } 59 | 60 | func (ali *Alidns) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 61 | var records AlidnsSubDomainRecords 62 | // 获取当前域名信息 63 | params := domain.GetCustomParams() 64 | params.Set("Action", "DescribeSubDomainRecords") 65 | params.Set("DomainName", domain.DomainName) 66 | params.Set("SubDomain", domain.GetFullDomain()) 67 | params.Set("Type", recordType) 68 | err := ali.request(params, &records) 69 | 70 | if err != nil { 71 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error()) 72 | return 73 | } 74 | 75 | if records.TotalCount > 0 { 76 | // 默认第一个 77 | recordSelected := records.DomainRecords.Record[0] 78 | if params.Has("RecordId") { 79 | for i := 0; i < len(records.DomainRecords.Record); i++ { 80 | if records.DomainRecords.Record[i].RecordID == params.Get("RecordId") { 81 | recordSelected = records.DomainRecords.Record[i] 82 | } 83 | } 84 | } 85 | // 存在,更新 86 | ali.modify(recordSelected, domain, recordType, ipAddr) 87 | } else { 88 | // 不存在,创建 89 | ali.create(domain, recordType, ipAddr) 90 | } 91 | } 92 | 93 | // 创建 94 | func (ali *Alidns) create(domain *ddnscore.Domain, recordType string, ipAddr string) { 95 | params := domain.GetCustomParams() 96 | params.Set("Action", "AddDomainRecord") 97 | params.Set("DomainName", domain.DomainName) 98 | params.Set("RR", domain.GetSubDomain()) 99 | params.Set("Type", recordType) 100 | params.Set("Value", ipAddr) 101 | params.Set("TTL", ali.TTL) 102 | 103 | var result AlidnsResp 104 | err := ali.request(params, &result) 105 | 106 | if err == nil && result.RecordID != "" { 107 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 108 | } else { 109 | errMsg := fmt.Sprintf("创建域名失败:\n%v\n", result) 110 | if err != nil { 111 | errMsg += err.Error() 112 | } 113 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg) 114 | } 115 | } 116 | 117 | // 修改 118 | func (ali *Alidns) modify(recordSelected AlidnsRecord, domain *ddnscore.Domain, recordType string, ipAddr string) { 119 | 120 | // 相同不修改 121 | if recordSelected.Value == ipAddr { 122 | if domain.UpdateStatus == ddnscore.UpdatedFailed { 123 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 124 | } else { 125 | domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "") 126 | } 127 | return 128 | } 129 | 130 | params := domain.GetCustomParams() 131 | params.Set("Action", "UpdateDomainRecord") 132 | params.Set("RR", domain.GetSubDomain()) 133 | params.Set("RecordId", recordSelected.RecordID) 134 | params.Set("Type", recordType) 135 | params.Set("Value", ipAddr) 136 | params.Set("TTL", ali.TTL) 137 | 138 | var result AlidnsResp 139 | err := ali.request(params, &result) 140 | 141 | if err == nil && result.RecordID != "" { 142 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 143 | } else { 144 | errMsg := fmt.Sprintf("更新域名解析失败:%v\n", result) 145 | if err != nil { 146 | errMsg += err.Error() 147 | } 148 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg) 149 | } 150 | } 151 | 152 | // request 统一请求接口 153 | func (ali *Alidns) request(params url.Values, result interface{}) (err error) { 154 | 155 | util.AliyunSigner(ali.task.DNS.ID, ali.task.DNS.Secret, ¶ms) 156 | 157 | req, err := http.NewRequest( 158 | "GET", 159 | alidnsEndpoint, 160 | bytes.NewBuffer(nil), 161 | ) 162 | req.URL.RawQuery = params.Encode() 163 | 164 | if err != nil { 165 | log.Println("http.NewRequest失败. Error: ", err) 166 | return 167 | } 168 | 169 | client, err := ali.CreateHTTPClient() 170 | if err != nil { 171 | return err 172 | } 173 | 174 | resp, err := client.Do(req) 175 | if err != nil { 176 | return err 177 | } 178 | 179 | return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result) 180 | } 181 | -------------------------------------------------------------------------------- /module/ddns/providers/callback.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | "time" 8 | 9 | "github.com/gdy666/lucky/module/ddns/ddnscore.go" 10 | "github.com/gdy666/lucky/module/ddns/ddnsgo" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | ) 13 | 14 | type Callback struct { 15 | ProviderCommon 16 | TTL string 17 | } 18 | 19 | // Init 初始化 20 | func (cb *Callback) Init(task *ddnscore.DDNSTaskInfo) { 21 | cb.ProviderCommon.Init(task) 22 | 23 | if task.TTL == "" { 24 | // 默认600 25 | cb.TTL = "600" 26 | } else { 27 | cb.TTL = task.TTL 28 | } 29 | cb.SetCreateUpdateDomainFunc(cb.createUpdateDomain) 30 | } 31 | 32 | func CopyHeadersMap(sm map[string]string) map[string]string { 33 | dm := make(map[string]string) 34 | 35 | for k, v := range sm { 36 | dm[k] = v 37 | } 38 | 39 | return dm 40 | } 41 | 42 | func (cb *Callback) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 43 | 44 | url := replacePara(cb.task.DNS.Callback.URL, ipAddr, domain, recordType, cb.TTL) 45 | requestBody := replacePara(cb.task.DNS.Callback.RequestBody, ipAddr, domain, recordType, cb.TTL) 46 | 47 | //headersStr := cb.task.DNS.Callback.Headers 48 | var headerStrList []string 49 | for i := range cb.task.DNS.Callback.Headers { 50 | header := replacePara(cb.task.DNS.Callback.Headers[i], ipAddr, domain, recordType, cb.TTL) 51 | headerStrList = append(headerStrList, header) 52 | } 53 | 54 | headers := httputils.CreateHeadersMap(headerStrList) 55 | 56 | succcssCotentList := []string{} 57 | for i := range cb.task.DNS.Callback.CallbackSuccessContent { 58 | content := replacePara(cb.task.DNS.Callback.CallbackSuccessContent[i], ipAddr, domain, recordType, cb.TTL) 59 | succcssCotentList = append(succcssCotentList, content) 60 | } 61 | 62 | callErr := cb.CallbackHttpClientDo(cb.task.DNS.Callback.Method, url, requestBody, headers, succcssCotentList) 63 | 64 | if callErr != nil { 65 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, callErr.Error()) 66 | return 67 | } 68 | domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "") 69 | } 70 | 71 | // replacePara 替换参数 72 | func replacePara(orgPara, ipAddr string, domain *ddnscore.Domain, recordType string, ttl string) (newPara string) { 73 | orgPara = strings.ReplaceAll(orgPara, "#{ip}", ipAddr) 74 | orgPara = strings.ReplaceAll(orgPara, "#{domain}", domain.String()) 75 | orgPara = strings.ReplaceAll(orgPara, "#{recordType}", recordType) 76 | orgPara = strings.ReplaceAll(orgPara, "#{ttl}", ttl) 77 | 78 | for k, v := range domain.GetCustomParams() { 79 | if len(v) == 1 { 80 | orgPara = strings.ReplaceAll(orgPara, "#{"+k+"}", v[0]) 81 | } 82 | } 83 | 84 | return orgPara 85 | } 86 | 87 | func (cb *Callback) CallbackHttpClientDo(method, url, requestBody string, headers map[string]string, callbackSuccessContent []string) error { 88 | 89 | globalDDNSConf := ddnsgo.GetDDNSConfigure() 90 | dnsConf := cb.task.DNS 91 | statusCode, respStr, err := httputils.GetStringGoutDoHttpRequest( 92 | "tcp", 93 | "", 94 | method, 95 | url, 96 | requestBody, 97 | dnsConf.HttpClientProxyType, 98 | dnsConf.HttpClientProxyAddr, 99 | dnsConf.HttpClientProxyUser, 100 | dnsConf.HttpClientProxyPassword, 101 | headers, 102 | !globalDDNSConf.HttpClientSecureVerify, 103 | time.Duration(cb.task.HttpClientTimeout)*time.Second) 104 | if err != nil { 105 | return fmt.Errorf("Callback 调用接口[%s]出错:%s", url, err.Error()) 106 | } 107 | 108 | if cb.task.DNS.Callback.DisableCallbackSuccessContentCheck { 109 | if statusCode == http.StatusOK { 110 | return nil 111 | } 112 | return fmt.Errorf("调用接口失败:\n statusCode:%d\n%s", statusCode, respStr) 113 | } 114 | 115 | //log.Printf("接口[%s]调用响应:%s\n", url, respStr) 116 | 117 | //fmt.Printf("statusCode:%d\n", statusCode) 118 | 119 | for _, successContent := range callbackSuccessContent { 120 | if strings.Contains(respStr, successContent) { 121 | return nil 122 | } 123 | } 124 | 125 | return fmt.Errorf("调用接口失败:\n%s", respStr) 126 | } 127 | -------------------------------------------------------------------------------- /module/ddns/providers/godaddy.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/gdy666/lucky/module/ddns/ddnscore.go" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/httputils" 12 | ) 13 | 14 | type godaddyRecord struct { 15 | Data string `json:"data"` 16 | Name string `json:"name"` 17 | TTL int `json:"ttl"` 18 | Type string `json:"type"` 19 | } 20 | 21 | type godaddyRecords []godaddyRecord 22 | 23 | type GoDaddy struct { 24 | ProviderCommon 25 | TTL int 26 | header http.Header 27 | client *http.Client 28 | } 29 | 30 | // Init 初始化 31 | func (gd *GoDaddy) Init(task *ddnscore.DDNSTaskInfo) { 32 | gd.ProviderCommon.Init(task) 33 | // if task.TTL == "" { 34 | // // 默认600s 35 | // gd.TTL = 600 36 | // } else { 37 | // gd.TTL = task.TTL 38 | // } 39 | if task.TTL == "" { 40 | // 默认300s 41 | gd.TTL = 600 42 | } else { 43 | ttl, err := strconv.Atoi(task.TTL) 44 | if err != nil { 45 | gd.TTL = 600 46 | } else { 47 | gd.TTL = ttl 48 | } 49 | } 50 | gd.header = map[string][]string{ 51 | "Authorization": {fmt.Sprintf("sso-key %s:%s", task.DNS.ID, task.DNS.Secret)}, 52 | "Content-Type": {"application/json"}, 53 | } 54 | //g.throttle, _ = util.GetThrottle(55) 55 | gd.client, _ = gd.CreateHTTPClient() 56 | 57 | gd.SetCreateUpdateDomainFunc(gd.createUpdateDomain) 58 | } 59 | 60 | func (gd *GoDaddy) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) { 61 | 62 | _, err := gd.sendReq(http.MethodPut, recordType, domain, &godaddyRecords{godaddyRecord{ 63 | Data: ipAddr, 64 | Name: domain.SubDomain, 65 | TTL: gd.TTL, 66 | Type: recordType, 67 | }}) 68 | if err != nil { 69 | domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error()) 70 | } 71 | } 72 | 73 | func (gd *GoDaddy) sendReq(method string, rType string, domain *ddnscore.Domain, data any) (*godaddyRecords, error) { 74 | 75 | var body *bytes.Buffer 76 | if data != nil { 77 | if buffer, err := json.Marshal(data); err != nil { 78 | return nil, err 79 | } else { 80 | body = bytes.NewBuffer(buffer) 81 | } 82 | } 83 | path := fmt.Sprintf("https://api.godaddy.com/v1/domains/%s/records/%s/%s", 84 | domain.DomainName, rType, domain.SubDomain) 85 | req, err := http.NewRequest(method, path, body) 86 | if err != nil { 87 | return nil, err 88 | } 89 | req.Header = gd.header 90 | 91 | resp, err := gd.client.Do(req) 92 | if err != nil { 93 | return nil, err 94 | } 95 | result := &godaddyRecords{} 96 | 97 | httputils.GetAndParseJSONResponseFromHttpResponse(resp, result) 98 | 99 | return result, nil 100 | } 101 | -------------------------------------------------------------------------------- /module/ddns/providers/provider.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import "github.com/gdy666/lucky/module/ddns/ddnscore.go" 4 | 5 | // Provider interface 6 | type Provider interface { 7 | Init(task *ddnscore.DDNSTaskInfo) 8 | // 添加或更新IPv4/IPv6记录 9 | AddUpdateDomainRecords() string 10 | } 11 | -------------------------------------------------------------------------------- /module/ddns/worker.go: -------------------------------------------------------------------------------- 1 | package ddns 2 | 3 | import ( 4 | "log" 5 | "runtime/debug" 6 | "sync" 7 | "time" 8 | 9 | ddnsconf "github.com/gdy666/lucky/module/ddns/conf" 10 | "github.com/gdy666/lucky/module/ddns/ddnscore.go" 11 | "github.com/gdy666/lucky/module/ddns/ddnsgo" 12 | "github.com/gdy666/lucky/module/ddns/providers" 13 | "github.com/gdy666/lucky/thirdlib/gdylib/service" 14 | ) 15 | 16 | var DDNSService *service.Service 17 | 18 | func init() { 19 | DDNSService, _ = service.NewService("ddns") 20 | DDNSService.SetTimerFunc(syncAllDomainsOnce) 21 | DDNSService.SetEventFunc(syncTaskDomainsOnce) 22 | } 23 | 24 | // Run 定时运行 25 | func Run(firstDelay time.Duration, delay time.Duration) { 26 | 27 | log.Printf("DDNS 第一次运行将等待 %d 秒后运行 (等待网络)", int(firstDelay.Seconds())) 28 | <-time.After(firstDelay) 29 | DDNSService.Start() 30 | } 31 | 32 | var wg sync.WaitGroup 33 | 34 | // RunOnce RunOnce 35 | func syncAllDomainsOnce(params ...any) { 36 | ddnsTaskList := ddnscore.GetDDNSTaskInfoList() 37 | ddnsconf.CleanIPUrlAddrMap() 38 | ddnsConf := ddnsgo.GetDDNSConfigure() 39 | 40 | //log.Printf("批量执行DDNS任务") 41 | taskBeginTime := time.Now() 42 | 43 | //fmt.Printf("ddnsTaskList:%v\n", ddnsTaskList) 44 | 45 | for index := range ddnsTaskList { 46 | 47 | task := ddnsTaskList[index] 48 | if !task.Enable { 49 | continue 50 | } 51 | 52 | if time.Since(task.TaskState.LastWorkTime) < time.Second*15 { 53 | //log.Printf("[%s]太接近,忽略", task.TaskName) 54 | continue 55 | } 56 | 57 | //log.Printf("task[%s] enable\n", task.TaskName) 58 | 59 | wg.Add(1) 60 | 61 | go func() { 62 | defer func() { 63 | wg.Done() 64 | recoverErr := recover() 65 | if recoverErr == nil { 66 | return 67 | } 68 | log.Printf("syncDDNSTask[%s]panic:\n%v", task.TaskName, recoverErr) 69 | log.Printf("%s", debug.Stack()) 70 | }() 71 | syncDDNSTask(task) 72 | }() 73 | 74 | <-time.After(time.Millisecond * 600) 75 | } 76 | wg.Wait() 77 | 78 | taskEndTime := time.Now() 79 | 80 | usedTime := taskEndTime.Sub(taskBeginTime) 81 | 82 | nextTaskTimer := time.Second*time.Duration(ddnsConf.Intervals) - usedTime 83 | 84 | //debug.FreeOSMemory() 85 | //log.Printf("syncAllDomainsOnce 任务完成") 86 | DDNSService.Timer = time.NewTimer(nextTaskTimer) 87 | } 88 | 89 | func syncTaskDomainsOnce(params ...any) { 90 | serverMsg := (params[1]).(service.ServiceMsg) 91 | taskKey := serverMsg.Params[0].(string) 92 | switch serverMsg.Type { 93 | case "syncDDNSTask": 94 | { 95 | //log.Printf("syncTaskDomainsOnce 单DDNS任务更新:%s", taskKey) 96 | ddnsconf.CleanIPUrlAddrMap() 97 | task := ddnscore.GetDDNSTaskInfoByKey(taskKey) 98 | syncDDNSTask(task) 99 | } 100 | default: 101 | return 102 | } 103 | 104 | } 105 | 106 | func syncDDNSTask(task *ddnscore.DDNSTaskInfo) { 107 | if task == nil { 108 | return 109 | } 110 | var dnsSelected providers.Provider 111 | switch task.DNS.Name { 112 | case "alidns": 113 | dnsSelected = &providers.Alidns{} 114 | case "dnspod": 115 | dnsSelected = &providers.Dnspod{} 116 | case "cloudflare": 117 | dnsSelected = &providers.Cloudflare{} 118 | case "huaweicloud": 119 | dnsSelected = &providers.Huaweicloud{} 120 | case "callback": 121 | dnsSelected = &providers.Callback{} 122 | case "baiducloud": 123 | dnsSelected = &providers.BaiduCloud{} 124 | case "porkbun": 125 | dnsSelected = &providers.Porkbun{} 126 | default: 127 | return 128 | } 129 | 130 | dnsSelected.Init(task) 131 | 132 | dnsSelected.AddUpdateDomainRecords() 133 | task.ExecWebhook(&task.TaskState) 134 | // log.Printf("假装耗时10秒\n") 135 | // <-time.After(time.Second * 10) 136 | // log.Printf("耗时完成\n") 137 | ddnscore.DDNSTaskInfoMapUpdate(task) 138 | 139 | //task.TaskState.LastWorkTime = time.Now() //记录最近一次检测时间,防止批量检测和单个检测时间间隔过于接近 140 | 141 | // 142 | } 143 | -------------------------------------------------------------------------------- /module/portforward/socketproxy/baseproxyconf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package socketproxy 3 | 4 | import ( 5 | "sync/atomic" 6 | ) 7 | 8 | type BaseProxyConf struct { 9 | TrafficIn int64 10 | TrafficOut int64 11 | key string 12 | ProxyType string // tcp tcp4 tcp6 udp udp4 udp6 13 | 14 | } 15 | 16 | func (p *BaseProxyConf) GetProxyType() string { 17 | return p.ProxyType 18 | } 19 | 20 | func (p *BaseProxyConf) GetStatus() string { 21 | return p.ProxyType 22 | } 23 | 24 | func (p *BaseProxyConf) ReceiveDataCallback(nw int64) { 25 | atomic.AddInt64(&p.TrafficIn, nw) 26 | } 27 | 28 | func (p *BaseProxyConf) SendDataCallback(nw int64) { 29 | atomic.AddInt64(&p.TrafficOut, nw) 30 | } 31 | 32 | func (p *BaseProxyConf) GetTrafficIn() int64 { 33 | return atomic.LoadInt64(&p.TrafficIn) 34 | } 35 | 36 | func (p *BaseProxyConf) GetTrafficOut() int64 { 37 | return atomic.LoadInt64(&p.TrafficOut) 38 | } 39 | -------------------------------------------------------------------------------- /module/portforward/socketproxy/socketproxy.go: -------------------------------------------------------------------------------- 1 | package socketproxy 2 | -------------------------------------------------------------------------------- /module/reverseproxy/proxy.go: -------------------------------------------------------------------------------- 1 | package reverseproxy 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | reverseproxyconf "github.com/gdy666/lucky/module/reverseproxy/conf" 8 | "github.com/gdy666/lucky/module/weblog" 9 | ) 10 | 11 | func InitReverseProxyServer() { 12 | ruleList := GetReverseProxyRuleList() 13 | for ruleIndex := range ruleList { 14 | if ruleList[ruleIndex].Enable { 15 | startRes := ruleList[ruleIndex].ServerStart() 16 | if startRes == nil { 17 | log.Printf("启动反向代理服务[%s]成功", ruleList[ruleIndex].Addr()) 18 | } else { 19 | log.Printf("启动反向代理服务[%s]失败:%s", ruleList[ruleIndex].Addr(), startRes.Error()) 20 | } 21 | } 22 | } 23 | } 24 | 25 | func EnableRuleByKey(key string, enable bool) error { 26 | 27 | rule := GetReverseProxyRuleByKey(key) 28 | 29 | if rule == nil { 30 | return fmt.Errorf("GetReverseProxyRuleByKey not found:%s", key) 31 | } 32 | 33 | if enable { 34 | err := rule.ServerStart() 35 | if err != nil { 36 | log.Printf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error()) 37 | EnableReverseProxyRuleByKey(key, false) 38 | return fmt.Errorf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error()) 39 | } else { 40 | log.Printf("启用反向代理规则[%s]成功", rule.Addr()) 41 | } 42 | } else { 43 | rule.ServerStop() 44 | log.Printf("停用反向代理规则[%s]成功", rule.Addr()) 45 | } 46 | return EnableReverseProxyRuleByKey(key, enable) 47 | } 48 | 49 | type RuleInfo struct { 50 | reverseproxyconf.ReverseProxyRule 51 | AccessLogs map[string][]any 52 | } 53 | 54 | func GetProxyRuleListInfo() *[]RuleInfo { 55 | ruleList := GetReverseProxyRuleList() 56 | var res []RuleInfo 57 | for i := range ruleList { 58 | //ti := createProxyRuleInfo(nil, ruleList[i]) 59 | var ri RuleInfo 60 | ri.ReverseProxyRule = *ruleList[i] 61 | ri.AccessLogs = ruleList[i].GetLastLogs() 62 | res = append(res, ri) 63 | } 64 | return &res 65 | } 66 | 67 | func GetAccessLogs(ruleKey, proxyKey string, pageSize, page int) (int, []any) { 68 | var res []any 69 | total := 0 70 | 71 | subRule := GetSubRuleByKey(ruleKey, proxyKey) 72 | if subRule == nil { 73 | return 0, res 74 | } 75 | total, res = subRule.GetLogsBuffer().GetLogsByLimit(weblog.WebLogConvert, pageSize, page) 76 | return total, res 77 | } 78 | -------------------------------------------------------------------------------- /module/safe/blacklist.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package safe 3 | 4 | import ( 5 | "fmt" 6 | "net" 7 | "strings" 8 | "time" 9 | 10 | "github.com/gdy666/lucky/config" 11 | safeconf "github.com/gdy666/lucky/module/safe/conf" 12 | ) 13 | 14 | func GetBlackList() []safeconf.BlackListItem { 15 | config.ConfigureMutex.RLock() 16 | defer config.ConfigureMutex.RUnlock() 17 | 18 | BlackListFlush(false) 19 | 20 | var resList []safeconf.BlackListItem 21 | if config.Configure == nil { 22 | return resList 23 | } 24 | for i := range config.Configure.BlackListConfigure.BlackList { 25 | resList = append(resList, config.Configure.BlackListConfigure.BlackList[i]) 26 | } 27 | return resList 28 | } 29 | 30 | func BlackListInit() { 31 | config.ConfigureMutex.RLock() 32 | defer config.ConfigureMutex.RUnlock() 33 | var netIP net.IP 34 | var cidr *net.IPNet 35 | 36 | for i := range config.Configure.BlackListConfigure.BlackList { 37 | netIP = nil 38 | cidr = nil 39 | if strings.Contains(config.Configure.BlackListConfigure.BlackList[i].IP, "/") { 40 | _, cidr, _ = net.ParseCIDR(config.Configure.BlackListConfigure.BlackList[i].IP) 41 | } else { 42 | netIP = net.ParseIP(config.Configure.BlackListConfigure.BlackList[i].IP) 43 | } 44 | config.Configure.BlackListConfigure.BlackList[i].Cidr = cidr 45 | config.Configure.BlackListConfigure.BlackList[i].NetIP = netIP 46 | } 47 | } 48 | 49 | func BlackListAdd(ip string, activelifeDuration int32) (string, error) { 50 | config.ConfigureMutex.Lock() 51 | defer config.ConfigureMutex.Unlock() 52 | 53 | var err error 54 | var netIP net.IP = nil 55 | var cidr *net.IPNet = nil 56 | if strings.Contains(ip, "/") { 57 | _, cidr, err = net.ParseCIDR(ip) 58 | if err != nil { 59 | return "", fmt.Errorf("网段格式有误,转换出错:%s", err.Error()) 60 | } 61 | } else { 62 | netIP = net.ParseIP(ip) 63 | if netIP == nil { 64 | return "", fmt.Errorf("IP格式有误") 65 | } 66 | } 67 | 68 | if activelifeDuration <= 0 { 69 | activelifeDuration = 666666 70 | } 71 | 72 | EffectiveTimeStr := time.Now().Add(time.Hour * time.Duration(activelifeDuration)).Format("2006-01-02 15:04:05") 73 | 74 | for i, ipr := range config.Configure.BlackListConfigure.BlackList { 75 | if ipr.IP == ip { 76 | config.Configure.BlackListConfigure.BlackList[i].EffectiveTime = EffectiveTimeStr 77 | return EffectiveTimeStr, config.Save() 78 | } 79 | } 80 | item := safeconf.BlackListItem{IP: ip, EffectiveTime: EffectiveTimeStr, NetIP: netIP, Cidr: cidr} 81 | config.Configure.BlackListConfigure.BlackList = append(config.Configure.BlackListConfigure.BlackList, item) 82 | return EffectiveTimeStr, config.Save() 83 | } 84 | 85 | func BlackListDelete(ip string) error { 86 | config.ConfigureMutex.Lock() 87 | defer config.ConfigureMutex.Unlock() 88 | 89 | removeCount := 0 90 | CONTINUECHECK: 91 | removeIndex := -1 92 | 93 | for i, ipr := range config.Configure.BlackListConfigure.BlackList { 94 | if ipr.IP == ip { 95 | removeIndex = i 96 | break 97 | } 98 | } 99 | 100 | if removeIndex >= 0 { 101 | removeCount++ 102 | config.Configure.BlackListConfigure.BlackList = DeleteBlackListlice(config.Configure.BlackListConfigure.BlackList, removeIndex) 103 | goto CONTINUECHECK 104 | } 105 | if removeCount == 0 { 106 | return nil 107 | } 108 | return config.Save() 109 | } 110 | 111 | func BlackListFlush(lock bool) error { 112 | if lock { 113 | config.ConfigureMutex.Lock() 114 | defer config.ConfigureMutex.Unlock() 115 | } 116 | 117 | removeCount := 0 118 | 119 | CONTINUECHECK: 120 | removeIndex := -1 121 | 122 | for i, ipr := range config.Configure.BlackListConfigure.BlackList { 123 | ipat, err := time.ParseInLocation("2006-01-02 15:04:05", ipr.EffectiveTime, time.Local) 124 | if err != nil { //有效时间格式有误,当失效处理 125 | removeIndex = i 126 | 127 | break 128 | } 129 | 130 | if time.Since(ipat) > 0 { 131 | removeIndex = i 132 | break 133 | } 134 | } 135 | 136 | if removeIndex >= 0 { 137 | removeCount++ 138 | config.Configure.BlackListConfigure.BlackList = DeleteBlackListlice(config.Configure.BlackListConfigure.BlackList, removeIndex) 139 | goto CONTINUECHECK 140 | } 141 | 142 | if removeCount == 0 { 143 | return nil 144 | } 145 | return config.Save() 146 | } 147 | 148 | func DeleteBlackListlice(a []safeconf.BlackListItem, deleteIndex int) []safeconf.BlackListItem { 149 | j := 0 150 | for i := range a { 151 | if i != deleteIndex { 152 | a[j] = a[i] 153 | j++ 154 | } 155 | } 156 | return a[:j] 157 | } 158 | -------------------------------------------------------------------------------- /module/safe/conf/black.go: -------------------------------------------------------------------------------- 1 | package safeconf 2 | 3 | import "net" 4 | 5 | type BlackListItem WhiteListItem 6 | 7 | type BlackListConfigure struct { 8 | BlackList []BlackListItem `json:"BlackList"` //黑名单列表 9 | } 10 | 11 | func (w *BlackListItem) Contains(ip string) bool { 12 | netIP := net.ParseIP(ip) 13 | if netIP == nil { 14 | return false 15 | } 16 | if w.NetIP != nil { 17 | return w.NetIP.Equal(netIP) 18 | } 19 | 20 | if w.Cidr != nil { 21 | return w.Cidr.Contains(netIP) 22 | } 23 | return false 24 | } 25 | -------------------------------------------------------------------------------- /module/safe/conf/white.go: -------------------------------------------------------------------------------- 1 | package safeconf 2 | 3 | import "net" 4 | 5 | type WhiteListBaseConfigure struct { 6 | URL string `json:"URL"` 7 | ActivelifeDuration int32 `json:"ActivelifeDuration"` //有效期限,小时 8 | BasicAccount string `json:"BasicAccount"` 9 | BasicPassword string `json:"BasicPassword"` 10 | } 11 | 12 | type WhiteListConfigure struct { 13 | BaseConfigure WhiteListBaseConfigure `json:"BaseConfigure"` 14 | WhiteList []WhiteListItem `json:"WhiteList"` //白名单列表 15 | } 16 | 17 | type WhiteListItem struct { 18 | IP string `json:"IP"` 19 | EffectiveTime string `json:"Effectivetime"` //有效时间 20 | NetIP net.IP `json:"-"` 21 | Cidr *net.IPNet `json:"-"` 22 | } 23 | 24 | func (w *WhiteListItem) Contains(ip string) bool { 25 | netIP := net.ParseIP(ip) 26 | if netIP == nil { 27 | return false 28 | } 29 | if w.NetIP != nil { 30 | return w.NetIP.Equal(netIP) 31 | } 32 | 33 | if w.Cidr != nil { 34 | return w.Cidr.Contains(netIP) 35 | } 36 | return false 37 | } 38 | -------------------------------------------------------------------------------- /module/safe/safe.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | func Init() { 4 | BlackListInit() 5 | WhiteListInit() 6 | } 7 | -------------------------------------------------------------------------------- /module/safe/safecheck.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package safe 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/gdy666/lucky/config" 8 | ) 9 | 10 | func SafeCheck(mode, ip string) bool { 11 | switch mode { 12 | case "whitelist": 13 | //log.Printf("whitelist") 14 | return whiteListCheck(ip) 15 | case "blacklist": 16 | //log.Printf("blacklist") 17 | return blackListCheck(ip) 18 | default: 19 | return false 20 | } 21 | } 22 | 23 | func whiteListCheck(ip string) bool { 24 | config.ConfigureMutex.RLock() 25 | defer config.ConfigureMutex.RUnlock() 26 | if config.Configure == nil { 27 | return false 28 | } 29 | 30 | for _, item := range config.Configure.WhiteListConfigure.WhiteList { 31 | 32 | if !item.Contains(ip) { 33 | continue 34 | } 35 | 36 | itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local) 37 | if err != nil { 38 | return false 39 | } 40 | 41 | if time.Since(itemEffectiveTime) < 0 { 42 | //log.Printf("CCC") 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | //log.Printf("DDDD") 49 | return false 50 | } 51 | 52 | func blackListCheck(ip string) bool { 53 | config.ConfigureMutex.RLock() 54 | defer config.ConfigureMutex.RUnlock() 55 | if config.Configure == nil { 56 | return true 57 | } 58 | 59 | for _, item := range config.Configure.BlackListConfigure.BlackList { 60 | if !item.Contains(ip) { 61 | continue 62 | } 63 | itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local) 64 | if err != nil { 65 | return true 66 | } 67 | 68 | if time.Since(itemEffectiveTime) < 0 { 69 | return false 70 | } 71 | return true 72 | } 73 | 74 | return true 75 | } 76 | -------------------------------------------------------------------------------- /module/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "time" 7 | 8 | kservice "github.com/kardianos/service" 9 | ) 10 | 11 | // var globalService Service 12 | var startFunc func() 13 | 14 | var listenPort string 15 | var configureFileURL string 16 | 17 | func RegisterStartFunc(f func()) { 18 | startFunc = f 19 | } 20 | 21 | func SetListenPort(port int) { 22 | listenPort = fmt.Sprintf("%d", port) 23 | } 24 | 25 | func SetConfigureFile(f string) { 26 | configureFileURL = f 27 | } 28 | 29 | type Service struct { 30 | } 31 | 32 | func (p *Service) Start(s kservice.Service) error { 33 | go p.run() 34 | return nil 35 | } 36 | 37 | func (p *Service) run() { 38 | go func() { 39 | <-time.After(time.Second * 2) 40 | startFunc() 41 | }() 42 | } 43 | 44 | func (p *Service) Stop(s kservice.Service) error { 45 | return nil 46 | } 47 | 48 | func GetServiceState() int { 49 | serviceStatus := -1 50 | s, err := GetService() 51 | if err == nil { 52 | status, _ := s.Status() 53 | serviceStatus = int(status) 54 | } 55 | return serviceStatus 56 | } 57 | 58 | func GetService() (kservice.Service, error) { 59 | options := make(kservice.KeyValue) 60 | // if kservice.ChosenSystem().String() == "unix-systemv" { 61 | // options["SysvScript"] = sysvScript 62 | // } 63 | if runtime.GOOS != "windows" { 64 | return nil, fmt.Errorf("仅支持安装卸载windows服务") 65 | } 66 | 67 | svcConfig := &kservice.Config{ 68 | Name: "lucky", 69 | DisplayName: "lucky", 70 | Description: "ipv6端口转发,反向代理,DDNS,网络唤醒...", 71 | Arguments: []string{"-p", listenPort, "-c", configureFileURL}, 72 | Option: options, 73 | } 74 | 75 | prg := &Service{} 76 | s, err := kservice.New(prg, svcConfig) 77 | if err != nil { 78 | return nil, err 79 | } 80 | return s, nil 81 | } 82 | 83 | // 卸载服务 84 | func UninstallService() error { 85 | s, err := GetService() 86 | if err != nil { 87 | return err 88 | } 89 | 90 | return s.Uninstall() 91 | } 92 | 93 | // 安装服务 94 | func InstallService() error { 95 | s, err := GetService() 96 | 97 | if err != nil { 98 | return err 99 | } 100 | 101 | status, err := s.Status() 102 | if err != nil && status == kservice.StatusUnknown { 103 | // 服务未知,创建服务 104 | if err = s.Install(); err == nil { 105 | //s.Start() 106 | //log.Println("安装 lucky 服务成功!") 107 | return nil 108 | } 109 | return fmt.Errorf("安装 lucky 服务失败:%s", err.Error()) 110 | } 111 | return fmt.Errorf("lucky服务已安装,无需再次安装:下一次系统启动lucky会以服务形式启动.") 112 | } 113 | 114 | func Stop() error { 115 | s, err := GetService() 116 | if err != nil { 117 | return err 118 | } 119 | return s.Stop() 120 | } 121 | 122 | func Start() error { 123 | s, err := GetService() 124 | if err != nil { 125 | return err 126 | } 127 | return s.Start() 128 | } 129 | 130 | func Restart() error { 131 | s, err := GetService() 132 | if err != nil { 133 | return err 134 | } 135 | return s.Restart() 136 | } 137 | -------------------------------------------------------------------------------- /module/sslcertficate/conf/sslconf.go: -------------------------------------------------------------------------------- 1 | package sslconf 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "encoding/base64" 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | type SSLCertficate struct { 12 | Key string `json:"Key"` 13 | Enable bool `json:"Enable"` 14 | Remark string `json:"Remark"` //备注 15 | CertBase64 string `json:"CertBase64"` 16 | KeyBase64 string `json:"KeyBase64"` 17 | AddTime string `json:"AddTime"` //添加时间 18 | CertsInfo *[]CertInfo `json:"-"` 19 | //--------------------- 20 | Certificate *tls.Certificate `json:"-"` 21 | } 22 | 23 | type CertInfo struct { 24 | Domains []string 25 | NotBeforeTime string `json:"NotBeforeTime"` //time.Time 26 | NotAfterTime string `json:"NotAfterTime"` //time.Time 27 | } 28 | 29 | func (s *SSLCertficate) Init() error { 30 | tc, err := CreateX509KeyPairByBase64Str(s.CertBase64, s.KeyBase64) 31 | if err != nil { 32 | return fmt.Errorf("CreateX509KeyPairByBase64Str error:%s", err.Error()) 33 | } 34 | domainsInfo, err := GetCertDomainInfo(tc) 35 | if err != nil { 36 | return fmt.Errorf("GetCertDomainInfo error:%s", err.Error()) 37 | } 38 | s.Certificate = tc 39 | s.CertsInfo = domainsInfo 40 | return nil 41 | } 42 | 43 | // GetOnlyDomain 返回证书第一条域名 44 | func (s *SSLCertficate) GetFirstDomain() string { 45 | if s.CertsInfo == nil { 46 | return "" 47 | } 48 | if len(*s.CertsInfo) <= 0 { 49 | return "" 50 | } 51 | if len((*s.CertsInfo)[0].Domains) <= 0 { 52 | return "" 53 | } 54 | return (*s.CertsInfo)[0].Domains[0] 55 | } 56 | 57 | func CreateX509KeyPairByBase64Str(certBase64, keyBase64 string) (*tls.Certificate, error) { 58 | crtBytes, err := base64.StdEncoding.DecodeString(certBase64) 59 | if err != nil { 60 | return nil, fmt.Errorf("certBase64 decode error:%s", err.Error()) 61 | } 62 | 63 | keyBytes, err := base64.StdEncoding.DecodeString(keyBase64) 64 | if err != nil { 65 | return nil, fmt.Errorf("keyBase64 decode error:%s", err.Error()) 66 | } 67 | 68 | cert, err := tls.X509KeyPair(crtBytes, keyBytes) 69 | if err != nil { 70 | return nil, fmt.Errorf("create X509KeyPair error:%s", err.Error()) 71 | } 72 | return &cert, nil 73 | } 74 | 75 | func GetCertDomainInfo(cert *tls.Certificate) (*[]CertInfo, error) { 76 | if cert == nil { 77 | return nil, fmt.Errorf("cert == nil") 78 | } 79 | 80 | var res []CertInfo 81 | 82 | for i := range cert.Certificate { 83 | xx, err := x509.ParseCertificate(cert.Certificate[i]) 84 | if err != nil { 85 | continue 86 | } 87 | 88 | ds := GetDomainsTrimSpace(xx.DNSNames) 89 | if len(ds) == 0 { 90 | continue 91 | } 92 | 93 | info := CertInfo{Domains: ds, NotBeforeTime: xx.NotBefore.Format("2006-01-02 15:04:05"), NotAfterTime: xx.NotAfter.Format("2006-01-02 15:04:05")} 94 | res = append(res, info) 95 | } 96 | return &res, nil 97 | 98 | } 99 | 100 | // 除去空域名 101 | func GetDomainsTrimSpace(dst []string) []string { 102 | var res []string 103 | for i := range dst { 104 | if strings.TrimSpace(dst[i]) == "" { 105 | continue 106 | } 107 | res = append(res, strings.TrimSpace(dst[i])) 108 | } 109 | return res 110 | } 111 | -------------------------------------------------------------------------------- /module/sslcertficate/ssl.go: -------------------------------------------------------------------------------- 1 | package ssl 2 | 3 | func Init() { 4 | SSLCertficateListInit() 5 | } 6 | -------------------------------------------------------------------------------- /module/weblog/weblog.go: -------------------------------------------------------------------------------- 1 | package weblog 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gdy666/lucky/thirdlib/gdylib/logsbuffer" 7 | ) 8 | 9 | type LogItem struct { 10 | ProxyKey string 11 | ClientIP string 12 | LogContent string 13 | LogTime string 14 | } 15 | 16 | // 2006-01-02 15:04:05 17 | func WebLogConvert(lg *logsbuffer.LogItem) any { 18 | l := LogItem{ 19 | LogContent: lg.Content, 20 | LogTime: time.Unix(lg.Timestamp/int64(time.Second), 0).Format("2006-01-02 15:04:05")} 21 | return l 22 | } 23 | -------------------------------------------------------------------------------- /module/wol/client.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "strings" 7 | "sync" 8 | "time" 9 | 10 | wolconf "github.com/gdy666/lucky/module/wol/conf" 11 | websocketcontroller "github.com/gdy666/lucky/thirdlib/gdylib/websocketController" 12 | "github.com/sirupsen/logrus" 13 | ) 14 | 15 | var wolClient *websocketcontroller.Controller 16 | var wolClientMu sync.Mutex 17 | 18 | var clientState string 19 | var clientStateMsg string 20 | 21 | func init() { 22 | wolconf.SetClientInitFunc(WOLClientInit) 23 | wolconf.SetClientDisconnectFunc(ClientDisconnect) 24 | } 25 | 26 | func GetClientState() string { 27 | return clientState 28 | } 29 | 30 | func GetClientStateMsg() string { 31 | return clientStateMsg 32 | } 33 | 34 | func ClientDisconnect() { 35 | wolClientMu.Lock() 36 | defer wolClientMu.Unlock() 37 | if wolClient == nil { 38 | return 39 | } 40 | go wolClient.Disconnect() 41 | } 42 | 43 | func WOLClientInit(logger *logrus.Logger, c *wolconf.WOLClientConfigure) { 44 | 45 | if !c.Enable || c.ServerURL == "" { 46 | return 47 | } 48 | client := websocketcontroller.Controller{} 49 | client.ReceiveMessageCallback = receiveMessageCallback 50 | client.ClientDisconnectedCallback = clientStop 51 | client.ClientReadyCallback = clientReady 52 | client.Logs = logger 53 | client.SetConnectRetry(true) 54 | client.SetConnectRetryInterval(time.Second * 3) 55 | client.SetReadDeadline(time.Second * 5) 56 | client.SetServerURL(fmt.Sprintf("%s/api/wol/service", c.ServerURL)) 57 | client.ScureSkipVerify(!wolconf.GetHttpClientSecureVerify()) 58 | client.SetSendMessageEncryptionFunc(SendMessageEncryptionFunc) 59 | client.SetReceiveMessageDecryptionFunc(ReceiveMessageDecryptionFunc) 60 | 61 | wolClientMu.Lock() 62 | defer wolClientMu.Unlock() 63 | wolClient = &client 64 | 65 | go wolClient.Connect() 66 | } 67 | 68 | func receiveMessageCallback(c *websocketcontroller.Controller, msgBytes []byte) { 69 | rawMsg, err := UnPack(msgBytes) 70 | if err != nil { 71 | return 72 | } 73 | 74 | switch m := rawMsg.(type) { 75 | case *LoginResp: 76 | go handlerLoginResp(m, c) 77 | case *SyncClientConfigure: 78 | go synsClientConfigureToClient(m) 79 | case *ReplyWakeUp: 80 | //fmt.Printf("中继唤醒包:%v\n", m) 81 | c.Logs.Infof("中继唤醒包:%v\n", m) 82 | go wolconf.WakeOnLan(false, m.MacList, m.BroadcastIPs, m.Port, m.Repeat, nil) 83 | case *ShutDown: 84 | 85 | go func() { 86 | conf := GetWOLServiceConfigure() 87 | c.Logs.Infof("执行关机指令:%s\n", conf.Client.PowerOffCMD) 88 | //<-time.After(time.Second * 1) 89 | cmd := strings.Split(conf.Client.PowerOffCMD, " ") 90 | 91 | var output []byte 92 | var err error 93 | //fileutils.OpenProgramOrFile(cmd) 94 | if len(cmd) == 1 { 95 | output, err = exec.Command(cmd[0], []string{}...).Output() 96 | } else { 97 | output, err = exec.Command(cmd[0], cmd[1:]...).Output() 98 | } 99 | 100 | if err != nil { 101 | c.Logs.Errorf("执行关机指令[%s]出错:%s:%s", conf.Client.PowerOffCMD, string(output), err.Error()) 102 | } 103 | 104 | }() 105 | default: 106 | } 107 | } 108 | 109 | func clientStop(cc *websocketcontroller.Controller) { 110 | //fmt.Printf("客户端已断开服务器连接\n") 111 | //clientState = "服务端已断开连接" 112 | //clientStateMsg = "" 113 | } 114 | 115 | func clientReady(cc *websocketcontroller.Controller) { 116 | cc.Logs.Info("WOL 客户端已连接上服务端,发送登录消息...") 117 | logMsg := &Login{} 118 | logMsg.WOLClientConfigure = wolconf.GetWOLServiceConfigure().Client 119 | logMsg.ClientTimeStamp = time.Now().Unix() 120 | SendMessage(cc, logMsg) 121 | } 122 | 123 | func handlerLoginResp(m *LoginResp, c *websocketcontroller.Controller) { 124 | if m.Ret != 0 { 125 | c.Disconnect() 126 | c.Logs.Error("WOl 服务端登录失败:%s", m.Msg) 127 | clientState = "服务端登录失败" 128 | clientStateMsg = m.Msg 129 | return 130 | } 131 | c.Logs.Info("WOL服务端登录成功") 132 | clientState = "服务端登录成功" 133 | clientStateMsg = "" 134 | 135 | } 136 | 137 | func synsClientConfigureToClient(m *SyncClientConfigure) { 138 | //fmt.Printf("处理来自服务端同步配置\n") 139 | conf := GetWOLServiceConfigure() 140 | conf.Client.Relay = m.Relay 141 | conf.Client.DeviceName = m.DeviceName 142 | conf.Client.Mac = m.Mac 143 | conf.Client.BroadcastIP = m.BroadcastIP 144 | conf.Client.Port = m.Port 145 | conf.Client.Repeat = m.Repeat 146 | conf.Client.UpdateTime = m.UpdateTime 147 | AlterWOLClientConfigure(&conf, logger, false) 148 | } 149 | -------------------------------------------------------------------------------- /module/wol/conf/service.go: -------------------------------------------------------------------------------- 1 | package wolconf 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/sirupsen/logrus" 7 | ) 8 | 9 | type WOLServerConfigure struct { 10 | Enable bool 11 | Token string 12 | } 13 | 14 | type WOLClientConfigure struct { 15 | Enable bool //开关 16 | ServerURL string //服务器地址 17 | Token string //验证token 18 | Relay bool //中继唤醒包 19 | Key string 20 | DeviceName string //设备名称 21 | Mac string //网卡物理地址 22 | BroadcastIP string //广播地址 23 | Port int //端口 24 | Repeat int //重复次数 25 | PowerOffCMD string //关机指令 26 | UpdateTime int64 //配置更新时间 27 | 28 | } 29 | 30 | type WOLServiceConfigure struct { 31 | Server WOLServerConfigure 32 | Client WOLClientConfigure 33 | } 34 | 35 | var serviceConfigure *WOLServiceConfigure 36 | var serviceConfigureMu sync.RWMutex 37 | var clientInitFunc func(logger *logrus.Logger, c *WOLClientConfigure) 38 | var clientDisconnectFunc func() 39 | 40 | func SetClientInitFunc(f func(logger *logrus.Logger, c *WOLClientConfigure)) { 41 | clientInitFunc = f 42 | } 43 | 44 | func SetClientDisconnectFunc(f func()) { 45 | clientDisconnectFunc = f 46 | } 47 | 48 | func GetWOLServiceConfigure() WOLServiceConfigure { 49 | serviceConfigureMu.RLock() 50 | defer serviceConfigureMu.RUnlock() 51 | conf := *serviceConfigure 52 | return conf 53 | } 54 | 55 | func StoreWOLServiceConfigure(con *WOLServiceConfigure) { 56 | serviceConfigureMu.Lock() 57 | defer serviceConfigureMu.Unlock() 58 | serviceConfigure = con 59 | } 60 | 61 | func (c *WOLClientConfigure) Init(logger *logrus.Logger) { 62 | if clientInitFunc != nil { 63 | clientInitFunc(logger, c) 64 | } 65 | } 66 | 67 | func (c *WOLClientConfigure) ClientDisconnect() { 68 | if clientDisconnectFunc != nil { 69 | clientDisconnectFunc() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /module/wol/ctl.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | jsonMsg "github.com/gdy666/lucky/thirdlib/fatedier/golib/json" 7 | "github.com/gdy666/lucky/thirdlib/gdylib/stringsp" 8 | ) 9 | 10 | type Message = jsonMsg.Message 11 | 12 | var ( 13 | msgCtl *jsonMsg.MsgCtl 14 | ) 15 | var msgkeyBytes = []byte("lucky666") 16 | 17 | func init() { 18 | msgCtl = jsonMsg.NewMsgCtl() 19 | for typeByte, msg := range msgTypeMap { 20 | msgCtl.RegisterMsg(typeByte, msg) 21 | } 22 | } 23 | 24 | func SendMessageEncryptionFunc(messageBytesPtr []byte) ([]byte, error) { 25 | outs, _ := stringsp.DesEncrypt(messageBytesPtr, msgkeyBytes) //加密 26 | buf := make([]byte, base64.StdEncoding.EncodedLen(len(outs))) 27 | base64.StdEncoding.Encode(buf, outs) 28 | return buf, nil 29 | } 30 | 31 | // receiveMessageDecryptionFunc 自定义接收消息解密函数 32 | func ReceiveMessageDecryptionFunc(messageBytes []byte) ([]byte, error) { 33 | rawEncryptMsgBytes, err := base64.StdEncoding.DecodeString(string(messageBytes)) 34 | if err != nil { 35 | return nil, err 36 | } 37 | rawMsgBytes, err := stringsp.DesDecrypt(rawEncryptMsgBytes, msgkeyBytes) 38 | return rawMsgBytes, err 39 | } 40 | -------------------------------------------------------------------------------- /module/wol/module.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | "github.com/gdy666/lucky/config" 5 | "github.com/sirupsen/logrus" 6 | ) 7 | 8 | func Init(log *logrus.Logger) { 9 | WOLClientConfigureInit(log) 10 | deviceInit(log) 11 | } 12 | 13 | // deviceInit 暂时用于第三方物联网平台部分初始化 14 | func deviceInit(log *logrus.Logger) { 15 | config.ConfigureMutex.RLock() 16 | defer config.ConfigureMutex.RUnlock() 17 | for i := range config.Configure.WOLDeviceList { 18 | config.Configure.WOLDeviceList[i].SetShutDownFunc(ExecShutDown) 19 | go config.Configure.WOLDeviceList[i].DianDengClientStart() 20 | go config.Configure.WOLDeviceList[i].BemfaClientStart() 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /module/wol/msg.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | wolconf "github.com/gdy666/lucky/module/wol/conf" 5 | ) 6 | 7 | const ( 8 | TypeLogin = '0' 9 | TypeLoginResp = '1' 10 | TypeSyncClientConfigure = '2' 11 | TypeReplyWakeUp = '3' 12 | TypeShutDown = '4' 13 | ) 14 | 15 | var ( 16 | msgTypeMap = map[byte]interface{}{ 17 | TypeLogin: Login{}, 18 | TypeLoginResp: LoginResp{}, 19 | TypeSyncClientConfigure: SyncClientConfigure{}, 20 | TypeReplyWakeUp: ReplyWakeUp{}, 21 | TypeShutDown: ShutDown{}, 22 | } 23 | ) 24 | 25 | type Login struct { 26 | wolconf.WOLClientConfigure 27 | ClientTimeStamp int64 28 | } 29 | 30 | // 服务器发送给客户端,登录认证反馈 ,ret 为0成功,其它失败 31 | type LoginResp struct { 32 | Ret int // 33 | Msg string 34 | } 35 | 36 | type SyncClientConfigure struct { 37 | wolconf.WOLClientConfigure 38 | } 39 | 40 | type ReplyWakeUp struct { 41 | MacList []string 42 | BroadcastIPs []string 43 | Port int 44 | Repeat int 45 | } 46 | 47 | type ShutDown struct { 48 | } 49 | -------------------------------------------------------------------------------- /module/wol/websocketcommon.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | "fmt" 5 | 6 | websocketcontroller "github.com/gdy666/lucky/thirdlib/gdylib/websocketController" 7 | ) 8 | 9 | func SendMessage(c *websocketcontroller.Controller, msg any) error { 10 | msgBytes, err := Pack(msg) 11 | if err != nil { 12 | fmt.Printf("pack FUck:%s\n", err.Error()) 13 | return err 14 | } 15 | c.SendMessage(msgBytes) 16 | return nil 17 | } 18 | 19 | func Pack(msg interface{}) ([]byte, error) { 20 | return msgCtl.Pack(msg) 21 | } 22 | 23 | func UnPack(bytes []byte) (msg Message, err error) { 24 | if len(bytes) <= 9 { 25 | err = fmt.Errorf("len(bytes) <= 9") 26 | return 27 | } 28 | return msgCtl.UnPack(bytes[0], bytes[9:]) 29 | } 30 | -------------------------------------------------------------------------------- /previews/ddnslist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/ddnslist.png -------------------------------------------------------------------------------- /previews/domainsync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/domainsync.png -------------------------------------------------------------------------------- /previews/iphistroy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/iphistroy.png -------------------------------------------------------------------------------- /previews/relayrules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/relayrules.png -------------------------------------------------------------------------------- /previews/relayruleset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/relayruleset.png -------------------------------------------------------------------------------- /previews/reverseproxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/reverseproxy.png -------------------------------------------------------------------------------- /previews/webhookhistroy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/webhookhistroy.png -------------------------------------------------------------------------------- /previews/whitelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/whitelist.png -------------------------------------------------------------------------------- /previews/whitelistset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/whitelistset.png -------------------------------------------------------------------------------- /previews/wol001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/wol001.png -------------------------------------------------------------------------------- /previews/wol002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/previews/wol002.png -------------------------------------------------------------------------------- /reverseproxy/proxy.go: -------------------------------------------------------------------------------- 1 | package reverseproxy 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/gdy666/lucky/config" 8 | ) 9 | 10 | func InitReverseProxyServer() { 11 | ruleList := config.GetReverseProxyRuleList() 12 | for ruleIndex := range ruleList { 13 | if ruleList[ruleIndex].Enable { 14 | startRes := ruleList[ruleIndex].ServerStart() 15 | if startRes == nil { 16 | log.Printf("启动反向代理服务[%s]成功", ruleList[ruleIndex].Addr()) 17 | } else { 18 | log.Printf("启动反向代理服务[%s]失败:%s", ruleList[ruleIndex].Addr(), startRes.Error()) 19 | } 20 | } 21 | } 22 | } 23 | 24 | func EnableRuleByKey(key string, enable bool) error { 25 | 26 | rule := config.GetReverseProxyRuleByKey(key) 27 | 28 | if rule == nil { 29 | return fmt.Errorf("GetReverseProxyRuleByKey not found:%s", key) 30 | } 31 | 32 | if enable { 33 | err := rule.ServerStart() 34 | if err != nil { 35 | log.Printf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error()) 36 | config.EnableReverseProxyRuleByKey(key, false) 37 | return fmt.Errorf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error()) 38 | } else { 39 | log.Printf("启用反向代理规则[%s]成功", rule.Addr()) 40 | } 41 | } else { 42 | rule.ServerStop() 43 | log.Printf("停用反向代理规则[%s]成功", rule.Addr()) 44 | } 45 | return config.EnableReverseProxyRuleByKey(key, enable) 46 | } 47 | 48 | type RuleInfo struct { 49 | config.ReverseProxyRule 50 | AccessLogs map[string][]any 51 | } 52 | 53 | func GetProxyRuleListInfo() *[]RuleInfo { 54 | ruleList := config.GetReverseProxyRuleList() 55 | var res []RuleInfo 56 | for i := range ruleList { 57 | //ti := createProxyRuleInfo(nil, ruleList[i]) 58 | var ri RuleInfo 59 | ri.ReverseProxyRule = *ruleList[i] 60 | ri.AccessLogs = ruleList[i].GetLastLogs() 61 | res = append(res, ri) 62 | } 63 | return &res 64 | } 65 | 66 | func GetAccessLogs(ruleKey, proxyKey string, pageSize, page int) (int, []any) { 67 | var res []any 68 | total := 0 69 | 70 | subRule := config.GetSubRuleByKey(ruleKey, proxyKey) 71 | if subRule == nil { 72 | return 0, res 73 | } 74 | total, res = subRule.GetLogsBuffer().GetLogsByLimit(config.WebLogConvert, pageSize, page) 75 | return total, res 76 | } 77 | -------------------------------------------------------------------------------- /scripts/lucky.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=lucky 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=root 8 | ExecStart=/etc/lucky/lucky -c /etc/lucky/lucky.conf >/dev/null 9 | Restart=on-failure 10 | RestartSec=3s 11 | LimitNOFILE=999999 12 | 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /scripts/luckyservice: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # Copyright (C) 2006-2011 OpenWrt.org 3 | 4 | START=99 5 | SERVICE_USE_PID=1 6 | SERVICE_WRITE_PID=1 7 | SERVICE_DAEMONIZE=1 8 | 9 | #获取目录 10 | DIR=$(cat /etc/profile | grep luckydir | awk -F "\"" '{print $2}') 11 | [ -z "$DIR" ] && DIR=$(cat ~/.bashrc | grep luckydir | awk -F "\"" '{print $2}') 12 | [ -z "$BINDIR" ] && BINDIR=$DIR 13 | 14 | 15 | BIN=$BINDIR/lucky 16 | CONF=$BINDIR/lucky.conf 17 | 18 | start() { 19 | service_start $BIN -c $CONF & 20 | } 21 | 22 | stop() { 23 | service_stop $BIN 24 | } 25 | 26 | -------------------------------------------------------------------------------- /scripts/misnap_init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) gdy 3 | 4 | luckydir=/data/lucky.daji 5 | profile=/etc/profile 6 | 7 | sed -i '/alias lucky=*/'d $profile 8 | sed -i '/export luckydir=*/'d $profile 9 | #h初始化环境变量 10 | echo "alias lucky=\"$luckydir/lucky\"" >> $profile 11 | echo "export luckydir=\"$luckydir\"" >> $profile 12 | 13 | #设置init.d服务并启动lucky 14 | ln -sf $luckydir/scripts/luckyservice /etc/init.d/lucky.daji 15 | chmod 755 /etc/init.d/lucky.daji 16 | 17 | log_file=`uci get system.@system[0].log_file` 18 | i=0 19 | while [ "$i" -lt 10 ];do 20 | sleep 3 21 | [ -n "$(grep 'init complete' $log_file)" ] && i=10 || i=$((i+1)) 22 | done 23 | /etc/init.d/lucky.daji enable 24 | /etc/init.d/lucky.daji start 25 | 26 | 27 | -------------------------------------------------------------------------------- /socketproxy/baseproxyconf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package socketproxy 3 | 4 | import ( 5 | "sync/atomic" 6 | ) 7 | 8 | type BaseProxyConf struct { 9 | TrafficIn int64 10 | TrafficOut int64 11 | key string 12 | ProxyType string // tcp tcp4 tcp6 udp udp4 udp6 13 | 14 | } 15 | 16 | func (p *BaseProxyConf) GetProxyType() string { 17 | return p.ProxyType 18 | } 19 | 20 | func (p *BaseProxyConf) GetStatus() string { 21 | return p.ProxyType 22 | } 23 | 24 | func (p *BaseProxyConf) ReceiveDataCallback(nw int64) { 25 | atomic.AddInt64(&p.TrafficIn, nw) 26 | } 27 | 28 | func (p *BaseProxyConf) SendDataCallback(nw int64) { 29 | atomic.AddInt64(&p.TrafficOut, nw) 30 | } 31 | 32 | func (p *BaseProxyConf) GetTrafficIn() int64 { 33 | return atomic.LoadInt64(&p.TrafficIn) 34 | } 35 | 36 | func (p *BaseProxyConf) GetTrafficOut() int64 { 37 | return atomic.LoadInt64(&p.TrafficOut) 38 | } 39 | -------------------------------------------------------------------------------- /socketproxy/tcpproxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 gdy, 272288813@qq.com 2 | package socketproxy 3 | 4 | import ( 5 | "fmt" 6 | "log" 7 | "net" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/sirupsen/logrus" 12 | ) 13 | 14 | type TCPProxy struct { 15 | TCPUDPProxyCommonConf 16 | //TcpSingleProxyMaxConns int64 17 | // tcpCurrentConns int64 18 | listenConn net.Listener 19 | listenConnMutex sync.Mutex 20 | 21 | connMap map[string]net.Conn 22 | connMapMutex sync.Mutex 23 | } 24 | 25 | func CreateTCPProxy(log *logrus.Logger, proxyType, listenIP string, targetAddressList []string, listenPort, targetPort int, options *RelayRuleOptions) *TCPProxy { 26 | p := &TCPProxy{} 27 | p.ProxyType = proxyType 28 | p.listenIP = listenIP 29 | p.listenPort = listenPort 30 | p.targetAddressList = targetAddressList 31 | p.targetPort = targetPort 32 | p.log = log 33 | 34 | p.safeMode = options.SafeMode 35 | 36 | p.SetMaxConnections(options.SingleProxyMaxTCPConnections) 37 | return p 38 | } 39 | 40 | func (p *TCPProxy) GetStatus() string { 41 | return fmt.Sprintf("%s\nactivity connections:[%d]", p.String(), p.GetCurrentConnections()) 42 | } 43 | 44 | func (p *TCPProxy) CheckConnectionsLimit() error { 45 | 46 | if GetGlobalTCPPortForwardConnections() >= GetGlobalTCPPortforwardMaxConnections() { 47 | return fmt.Errorf("超出TCP最大总连接数[%d]限制", GetGlobalTCPPortforwardMaxConnections()) 48 | } 49 | 50 | if p.GetCurrentConnections() >= p.SingleProxyMaxConnections { 51 | return fmt.Errorf("超出单端口TCP最大连接数[%d]限制", p.SingleProxyMaxConnections) 52 | } 53 | 54 | //全局,单端口限制 55 | return nil 56 | } 57 | 58 | func (p *TCPProxy) StartProxy() { 59 | p.listenConnMutex.Lock() 60 | defer p.listenConnMutex.Unlock() 61 | if p.listenConn != nil { 62 | //log.Printf("proxy %s is started", p.String()) 63 | p.log.Warnf("proxy %s is started", p.String()) 64 | return 65 | } 66 | 67 | if p.connMap == nil { 68 | p.connMap = make(map[string]net.Conn) 69 | } 70 | ln, err := net.Listen(p.ProxyType, p.GetListentAddress()) 71 | 72 | if err != nil { 73 | if strings.Contains(err.Error(), "Only one usage of each socket address") { 74 | p.log.Errorf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String()) 75 | } else { 76 | p.log.Errorf("Cannot start proxy[%s]:%s", p.String(), err) 77 | } 78 | return 79 | } 80 | 81 | p.listenConn = ln 82 | 83 | p.log.Infof("[端口转发][开启][%s]", p.String()) 84 | 85 | go func() { 86 | for { 87 | newConn, err := ln.Accept() 88 | 89 | if err != nil { 90 | if strings.Contains(err.Error(), "use of closed network connection") { 91 | break 92 | } 93 | p.log.Errorf(" Cannot accept connection due to error %s", err.Error()) 94 | continue 95 | } 96 | 97 | err = p.CheckConnectionsLimit() 98 | if err != nil { 99 | //p.PrintConnectionsInfo() 100 | p.log.Warnf("[%s]超出最大连接数限制,不再接受新连接:%s", p.GetKey(), err.Error()) 101 | newConn.Close() 102 | continue 103 | } 104 | 105 | newConnAddr := newConn.RemoteAddr().String() 106 | if !p.SafeCheck(newConnAddr) { 107 | p.log.Warnf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr) 108 | newConn.Close() 109 | continue 110 | } 111 | 112 | p.log.Infof("[%s]新连接[%s]安全检查通过", p.GetKey(), newConnAddr) 113 | 114 | p.connMapMutex.Lock() 115 | p.connMap[newConn.RemoteAddr().String()] = newConn 116 | p.connMapMutex.Unlock() 117 | 118 | p.AddCurrentConnections(1) 119 | go p.handle(newConn) 120 | } 121 | }() 122 | 123 | // 124 | //p.test() 125 | 126 | //go p.test() 127 | //go p.tcptest() 128 | 129 | } 130 | 131 | func (p *TCPProxy) StopProxy() { 132 | p.listenConnMutex.Lock() 133 | defer p.listenConnMutex.Unlock() 134 | defer func() { 135 | p.log.Infof("[端口转发][关闭][%s]", p.String()) 136 | }() 137 | if p.listenConn == nil { 138 | return 139 | } 140 | 141 | p.listenConn.Close() 142 | p.listenConn = nil 143 | 144 | p.connMapMutex.Lock() 145 | for _, conn := range p.connMap { 146 | conn.Close() 147 | } 148 | p.connMap = make(map[string]net.Conn) 149 | p.connMapMutex.Unlock() 150 | } 151 | 152 | func (p *TCPProxy) handle(conn net.Conn) { 153 | //dialer := net.Dialer{Timeout: 10 * time.Second} 154 | //targetConn, err := dialer.Dial("tcp", p.TargetAddress) 155 | targetConn, err := net.Dial("tcp", p.GetTargetAddress()) 156 | 157 | defer func() { 158 | if targetConn != nil { 159 | targetConn.Close() 160 | } 161 | defer conn.Close() 162 | p.AddCurrentConnections(-1) 163 | 164 | p.connMapMutex.Lock() 165 | delete(p.connMap, conn.RemoteAddr().String()) 166 | p.log.Infof("[%s]%s 断开连接", p.GetKey(), conn.RemoteAddr().String()) 167 | p.connMapMutex.Unlock() 168 | 169 | }() 170 | 171 | if err != nil { 172 | log.Printf("%s error:%s", p.String(), err.Error()) 173 | return 174 | } 175 | 176 | //targetConn.SetDeadline(time.Now().Add(time.Second * 3)) 177 | 178 | // targetTcpConn, ok := targetConn.(*net.TCPConn) 179 | // if ok { 180 | // targetTcpConn.SetReadBuffer(p.BufferSize * 1024 * 256 * 1024) 181 | // targetTcpConn.SetWriteBuffer(p.BufferSize * 1024 * 256 * 1024) 182 | // } 183 | 184 | p.relayData(targetConn, conn) 185 | 186 | } 187 | -------------------------------------------------------------------------------- /thirdlib/fatedier/golib/json/msg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package json 16 | 17 | import ( 18 | "reflect" 19 | ) 20 | 21 | var ( 22 | defaultMaxMsgLength int64 = 10240 23 | ) 24 | 25 | // Message wraps socket packages for communicating between frpc and frps. 26 | type Message interface{} 27 | 28 | type MsgCtl struct { 29 | typeMap map[byte]reflect.Type 30 | typeByteMap map[reflect.Type]byte 31 | 32 | maxMsgLength int64 33 | } 34 | 35 | func NewMsgCtl() *MsgCtl { 36 | return &MsgCtl{ 37 | typeMap: make(map[byte]reflect.Type), 38 | typeByteMap: make(map[reflect.Type]byte), 39 | maxMsgLength: defaultMaxMsgLength, 40 | } 41 | } 42 | 43 | func (msgCtl *MsgCtl) RegisterMsg(typeByte byte, msg interface{}) { 44 | msgCtl.typeMap[typeByte] = reflect.TypeOf(msg) 45 | msgCtl.typeByteMap[reflect.TypeOf(msg)] = typeByte 46 | } 47 | 48 | func (msgCtl *MsgCtl) SetMaxMsgLength(length int64) { 49 | msgCtl.maxMsgLength = length 50 | } 51 | -------------------------------------------------------------------------------- /thirdlib/fatedier/golib/json/pack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package json 16 | 17 | import ( 18 | "bytes" 19 | "encoding/binary" 20 | "encoding/json" 21 | "reflect" 22 | ) 23 | 24 | func (msgCtl *MsgCtl) unpack(typeByte byte, buffer []byte, msgIn Message) (msg Message, err error) { 25 | if msgIn == nil { 26 | t, ok := msgCtl.typeMap[typeByte] 27 | if !ok { 28 | err = ErrMsgType 29 | return 30 | } 31 | 32 | msg = reflect.New(t).Interface().(Message) 33 | } else { 34 | msg = msgIn 35 | } 36 | 37 | err = json.Unmarshal(buffer, &msg) 38 | return 39 | } 40 | 41 | func (msgCtl *MsgCtl) UnPackInto(buffer []byte, msg Message) (err error) { 42 | _, err = msgCtl.unpack(' ', buffer, msg) 43 | return 44 | } 45 | 46 | func (msgCtl *MsgCtl) UnPack(typeByte byte, buffer []byte) (msg Message, err error) { 47 | return msgCtl.unpack(typeByte, buffer, nil) 48 | } 49 | 50 | func (msgCtl *MsgCtl) Pack(msg Message) ([]byte, error) { 51 | typeByte, ok := msgCtl.typeByteMap[reflect.TypeOf(msg).Elem()] 52 | if !ok { 53 | return nil, ErrMsgType 54 | } 55 | 56 | content, err := json.Marshal(msg) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | buffer := bytes.NewBuffer(nil) 62 | buffer.WriteByte(typeByte) 63 | binary.Write(buffer, binary.BigEndian, int64(len(content))) 64 | buffer.Write(content) 65 | return buffer.Bytes(), nil 66 | } 67 | -------------------------------------------------------------------------------- /thirdlib/fatedier/golib/json/process.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package json 16 | 17 | import ( 18 | "encoding/binary" 19 | "errors" 20 | "io" 21 | ) 22 | 23 | var ( 24 | ErrMsgType = errors.New("message type error") 25 | ErrMaxMsgLength = errors.New("message length exceed the limit") 26 | ErrMsgLength = errors.New("message length error") 27 | ErrMsgFormat = errors.New("message format error") 28 | ) 29 | 30 | func (msgCtl *MsgCtl) readMsg(c io.Reader) (typeByte byte, buffer []byte, err error) { 31 | buffer = make([]byte, 1) 32 | _, err = c.Read(buffer) 33 | if err != nil { 34 | return 35 | } 36 | typeByte = buffer[0] 37 | if _, ok := msgCtl.typeMap[typeByte]; !ok { 38 | err = ErrMsgType 39 | return 40 | } 41 | 42 | var length int64 43 | err = binary.Read(c, binary.BigEndian, &length) 44 | if err != nil { 45 | return 46 | } 47 | if length > msgCtl.maxMsgLength { 48 | err = ErrMaxMsgLength 49 | return 50 | } else if length < 0 { 51 | err = ErrMsgLength 52 | return 53 | } 54 | 55 | buffer = make([]byte, length) 56 | n, err := io.ReadFull(c, buffer) 57 | if err != nil { 58 | return 59 | } 60 | 61 | if int64(n) != length { 62 | err = ErrMsgFormat 63 | } 64 | return 65 | } 66 | 67 | func (msgCtl *MsgCtl) ReadMsg(c io.Reader) (msg Message, err error) { 68 | typeByte, buffer, err := msgCtl.readMsg(c) 69 | if err != nil { 70 | return 71 | } 72 | return msgCtl.UnPack(typeByte, buffer) 73 | } 74 | 75 | func (msgCtl *MsgCtl) ReadMsgInto(c io.Reader, msg Message) (err error) { 76 | _, buffer, err := msgCtl.readMsg(c) 77 | if err != nil { 78 | return 79 | } 80 | return msgCtl.UnPackInto(buffer, msg) 81 | } 82 | 83 | func (msgCtl *MsgCtl) WriteMsg(c io.Writer, msg interface{}) (err error) { 84 | buffer, err := msgCtl.Pack(msg) 85 | if err != nil { 86 | return 87 | } 88 | 89 | if _, err = c.Write(buffer); err != nil { 90 | return 91 | } 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /thirdlib/gdylib/bemfa/global.go: -------------------------------------------------------------------------------- 1 | package bemfa 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var bemfaStore sync.Map 10 | var bemfaStroeMu sync.Mutex 11 | 12 | func GetBemfaDevice(secretKey string, httpClientSecureVerify bool, httpClientTimeout int) (*Device, error) { 13 | bemfaStroeMu.Lock() 14 | defer bemfaStroeMu.Unlock() 15 | device, deviceOk := bemfaStore.Load(secretKey) 16 | if deviceOk { 17 | d := device.(*Device) 18 | if d.OnLine() { 19 | return d, nil 20 | } 21 | 22 | if d.IsDisconnected() { 23 | 24 | d.Stop() 25 | 26 | err := d.Login() 27 | if err != nil { 28 | return nil, fmt.Errorf("bemfa Device login error:%s", err.Error()) 29 | } 30 | 31 | i := 0 32 | for { 33 | <-time.After(time.Second * 200) 34 | i++ 35 | if d.OnLine() { 36 | break 37 | } 38 | if i > 26 { 39 | break 40 | } 41 | } 42 | 43 | if d.OnLine() { 44 | return d, nil 45 | } 46 | 47 | return nil, fmt.Errorf("blinker drvice 连接服务器失败") 48 | } 49 | 50 | return device.(*Device), nil 51 | } 52 | d := CreateDevice(secretKey, httpClientSecureVerify, httpClientTimeout) 53 | 54 | err := d.Login() 55 | if err != nil { 56 | return nil, fmt.Errorf("bemfa Device Login error:%s", err.Error()) 57 | } 58 | 59 | i := 0 60 | for { 61 | <-time.After(time.Millisecond * 100) 62 | i++ 63 | if d.OnLine() { 64 | break 65 | } 66 | if i > 51 { 67 | break 68 | } 69 | } 70 | 71 | //fmt.Printf("在线\n") 72 | 73 | if d.OnLine() { 74 | bemfaStore.Store(secretKey, d) 75 | return d, nil 76 | } 77 | 78 | return nil, fmt.Errorf("bemfa drvice 连接服务器失败") 79 | } 80 | 81 | func UnRegisterPowerChangeCallback(d *Device, topic, key string) { 82 | bemfaStroeMu.Lock() 83 | defer bemfaStroeMu.Unlock() 84 | d.UnRegisterPowerChangeCallbackFunc(topic, key) 85 | 86 | if len(d.powerChangeCallbackMap) != 0 { 87 | return 88 | } 89 | 90 | d.Stop() 91 | bemfaStore.Delete(d.secretKey) 92 | 93 | } 94 | -------------------------------------------------------------------------------- /thirdlib/gdylib/blinker/VoiceAssistant.go: -------------------------------------------------------------------------------- 1 | package blinker 2 | 3 | import "fmt" 4 | 5 | const ( 6 | VA_TYPE_LIGHT = "light" 7 | VA_TYPE_OUTLET = "outlet" 8 | VA_TYPE_MULTI_OUTLET = "multi_outlet" 9 | VA_TYPE_SENSOR = "sensor" 10 | VA_TYPE_FAN = "fan" 11 | VA_TYPE_AIRCONDITION = "aircondition" 12 | ) 13 | 14 | type VoiceAssistant struct { 15 | DeviceType string //语言助手类型 (设备类型). 16 | VAType string //语言助手类型 MIOT AliGenie DuerOS 17 | Device *BlinkerDevice 18 | topic string 19 | } 20 | 21 | func (v *VoiceAssistant) GetSKey() string { 22 | switch v.VAType { 23 | case "MIOT": 24 | return "miType" 25 | case "AliGenie": 26 | return "aliType" 27 | case "DuerOS": 28 | return "duerType" 29 | default: 30 | return "" 31 | } 32 | } 33 | 34 | func (v *VoiceAssistant) PowerChangeReply(msgid, st string) { 35 | state := "off" 36 | 37 | if st == "true" { 38 | state = "on" 39 | } 40 | 41 | // if v.VAType == "MIOT" { 42 | // if state == "on" { 43 | // state = "true" 44 | // } else { 45 | // state = "false" 46 | // } 47 | // } 48 | 49 | data := map[string]string{"pState": state} 50 | v.Device.SendMessage("vAssistant", v.GetToDevice(), msgid, data) 51 | } 52 | 53 | func (v *VoiceAssistant) QueryDeviceState(msgid string) { 54 | state := v.Device.state 55 | // if v.VAType == "MIOT" { 56 | // if state == "on" { 57 | // state = "true" 58 | // } else { 59 | // state = "false" 60 | // } 61 | // } 62 | data := map[string]string{"pState": state} 63 | v.Device.SendMessage("vAssistant", v.GetToDevice(), msgid, data) 64 | } 65 | 66 | func (v *VoiceAssistant) GetToDevice() string { 67 | // if v.Device.DetailInfo.Broker == "blinker" { 68 | // return "ServerReceiver" 69 | // } 70 | return v.topic 71 | } 72 | 73 | func CreateVoiceAssistant(deviceType, vaType string) *VoiceAssistant { 74 | switch vaType { 75 | case "MIOT": 76 | return &VoiceAssistant{DeviceType: deviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)} 77 | case "AliGenie": 78 | return &VoiceAssistant{DeviceType: deviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)} 79 | case "DuerOS": 80 | { 81 | newDeviceType := "" 82 | switch deviceType { 83 | case VA_TYPE_LIGHT: 84 | newDeviceType = "LIGHT" 85 | case VA_TYPE_OUTLET: 86 | newDeviceType = "SOCKET" 87 | case VA_TYPE_MULTI_OUTLET: 88 | newDeviceType = "MULTI_SOCKET" 89 | case VA_TYPE_SENSOR: 90 | newDeviceType = "AIR_MONITOR" 91 | default: 92 | } 93 | if newDeviceType == "" { 94 | return nil 95 | } 96 | return &VoiceAssistant{DeviceType: newDeviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)} 97 | } 98 | default: 99 | return nil 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /thirdlib/gdylib/blinker/global.go: -------------------------------------------------------------------------------- 1 | package blinker 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var blinkerDeviceStore sync.Map 10 | var blinkerdeviceStroeMu sync.Mutex 11 | 12 | func GetBlinkerDevice(authKey string, httpClientSecureVerify bool, httpClientTimeout int) (*Device, error) { 13 | blinkerdeviceStroeMu.Lock() 14 | defer blinkerdeviceStroeMu.Unlock() 15 | device, deviceOk := blinkerDeviceStore.Load(authKey) 16 | if deviceOk { 17 | d := device.(*Device) 18 | if d.OnLine() { 19 | return d, nil 20 | } 21 | 22 | if d.IsDisconnected() { 23 | err := d.Init() 24 | if err != nil { 25 | return nil, fmt.Errorf("blinker Device init error:%s", err.Error()) 26 | } 27 | 28 | d.Stop() 29 | 30 | err = d.Login() 31 | if err != nil { 32 | return nil, fmt.Errorf("blinker Device login error:%s", err.Error()) 33 | } 34 | 35 | i := 0 36 | for { 37 | <-time.After(time.Second * 200) 38 | i++ 39 | if d.OnLine() { 40 | break 41 | } 42 | if i > 26 { 43 | break 44 | } 45 | } 46 | 47 | if d.OnLine() { 48 | return d, nil 49 | } 50 | 51 | return nil, fmt.Errorf("blinker drvice 连接服务器失败") 52 | } 53 | 54 | return device.(*Device), nil 55 | } 56 | d := CreateDevice(authKey, httpClientSecureVerify, httpClientTimeout) 57 | d.AddVoiceAssistant(CreateVoiceAssistant(VA_TYPE_OUTLET, "MIOT")) 58 | d.AddVoiceAssistant(CreateVoiceAssistant(VA_TYPE_OUTLET, "AliGenie")) 59 | d.AddVoiceAssistant(CreateVoiceAssistant(VA_TYPE_OUTLET, "DuerOS")) 60 | err := d.Init() 61 | if err != nil { 62 | return nil, fmt.Errorf("blinker Device init error:%s", err.Error()) 63 | } 64 | 65 | err = d.Login() 66 | if err != nil { 67 | return nil, fmt.Errorf("blinker Device Login error:%s", err.Error()) 68 | } 69 | 70 | i := 0 71 | for { 72 | <-time.After(time.Millisecond * 100) 73 | i++ 74 | if d.OnLine() { 75 | break 76 | } 77 | if i > 51 { 78 | break 79 | } 80 | } 81 | 82 | //fmt.Printf("在线\n") 83 | 84 | if d.OnLine() { 85 | blinkerDeviceStore.Store(authKey, d) 86 | return d, nil 87 | } 88 | 89 | return nil, fmt.Errorf("blinker drvice 连接服务器失败") 90 | } 91 | 92 | // func RegisterPowerChangeCallback(authkey, key string, cb func(string)) (*Device, error) { 93 | // d, err := GetBlinkerDevice(authkey) 94 | // if err != nil { 95 | // return nil, err 96 | // } 97 | // d.RegisterPowerChangeCallbackFunc(key, cb) 98 | // return d, nil 99 | // } 100 | 101 | func UnRegisterPowerChangeCallback(d *Device, key string) { 102 | blinkerdeviceStroeMu.Lock() 103 | defer blinkerdeviceStroeMu.Unlock() 104 | d.UnRegisterPowerChangeCallbackFunc(key) 105 | 106 | isEmpty := true 107 | d.powerChangeCallbackMap.Range(func(key any, val any) bool { 108 | isEmpty = false 109 | return false 110 | }) 111 | 112 | if isEmpty { 113 | d.Stop() 114 | blinkerDeviceStore.Delete(d.authKey) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /thirdlib/gdylib/dnsutils/resolve.go: -------------------------------------------------------------------------------- 1 | package dnsutils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/gdy666/lucky/thirdlib/gdylib/stringsp" 8 | "github.com/miekg/dns" 9 | "golang.org/x/net/idna" 10 | ) 11 | 12 | var DefaultIPv6DNSServerList = []string{ 13 | "[2001:4860:4860::8888]:53", //谷歌 14 | "[2001:4860:4860::8844]:53", //谷歌 15 | "[2606:4700:4700::64]:53", //cloudflare 16 | "[2606:4700:4700::6400]:53", //cloudflare 17 | "[240C::6666]:53", //下一代互联网北京研究中心 18 | "[240C::6644]:53", //下一代互联网北京研究中心 19 | "[2402:4e00::]:53", //dnspod 20 | //"[2400:3200::1]:53", //阿里 21 | // "[2400:3200:baba::1]:53", //阿里 22 | "[240e:4c:4008::1]:53", //中国电信 23 | "[240e:4c:4808::1]:53", //中国电信 24 | "[2408:8899::8]:53", //中国联通 25 | "[2408:8888::8]:53", //中国联通 26 | "[2409:8088::a]:53", //中国移动 27 | "[2409:8088::b]:53", //中国移动 28 | "[2001:dc7:1000::1]:53", //CNNIC 29 | "[2400:da00::6666]:53", //百度 30 | } 31 | 32 | var DefaultIPv4DNSServerList = []string{ 33 | "1.1.1.1:53", 34 | "1.2.4.8:53", 35 | "8.8.8.8:53", 36 | "9.9.9.9:53", 37 | "8.8.4.4:53", 38 | "114.114.114.114:53", 39 | "223.5.5.5:53", 40 | "223.6.6.6:53", 41 | "101.226.4.6:53", 42 | "218.30.118.6:53", 43 | "119.28.28.28:53", 44 | } 45 | 46 | func ResolveDomainAtServerList(queryType, domain string, dnsServerList []string) (string, error) { 47 | 48 | if len(dnsServerList) == 0 { 49 | if queryType == "AAAA" { 50 | dnsServerList = DefaultIPv6DNSServerList 51 | } else { 52 | dnsServerList = DefaultIPv4DNSServerList 53 | } 54 | } 55 | 56 | //some name that ought to exist, does not exist (NXDOMAIN) 57 | 58 | querytype, querytypeOk := dns.StringToType[strings.ToUpper(queryType)] 59 | if !querytypeOk { 60 | return "", fmt.Errorf("queryType error:%s", queryType) 61 | } 62 | 63 | if strings.HasPrefix(domain, "*.") { 64 | randomStr := stringsp.GetRandomString(8) 65 | domain = strings.Replace(domain, "*", randomStr, 1) 66 | } 67 | 68 | domain = dns.Fqdn(domain) 69 | domain, err := idna.ToASCII(domain) 70 | if err != nil { 71 | return "", fmt.Errorf(` idna.ToASCII(domain) error:%s`, err.Error()) 72 | } 73 | 74 | m := new(dns.Msg) 75 | m.SetQuestion(domain, querytype) 76 | m.MsgHdr.RecursionDesired = true 77 | 78 | c := new(dns.Client) 79 | noExistTimes := 0 80 | for _, dnsServer := range dnsServerList { 81 | c.Net = "" 82 | ipaddr, err := resolveDomain(m, c, dnsServer) 83 | if err != nil { 84 | //log.Printf("[%s]===>[%s][%s] ResolveDomain error:%s", dnsServer, queryType, domain, err.Error()) 85 | if strings.Contains(err.Error(), "some name that ought to exist, does not exist (NXDOMAIN)") { 86 | noExistTimes++ 87 | if noExistTimes >= 4 { 88 | return "", fmt.Errorf("解析域名[%s][%s]IP失败:noExistTimes", queryType, domain) 89 | } 90 | } 91 | continue 92 | } 93 | return ipaddr, nil 94 | } 95 | 96 | return "", fmt.Errorf("解析域名[%s][%s]IP失败", queryType, domain) 97 | } 98 | 99 | func resolveDomain(msg *dns.Msg, client *dns.Client, dnsServer string) (string, error) { 100 | 101 | Redo: 102 | if in, _, err := client.Exchange(msg, dnsServer); err == nil { // Second return value is RTT, not used for now 103 | if in.MsgHdr.Truncated { 104 | client.Net = "tcp" 105 | goto Redo 106 | } 107 | 108 | switch in.MsgHdr.Rcode { 109 | case dns.RcodeServerFailure: 110 | return "", fmt.Errorf("the name server encountered an internal failure while processing this request (SERVFAIL)") 111 | case dns.RcodeNameError: 112 | return "", fmt.Errorf("some name that ought to exist, does not exist (NXDOMAIN)") 113 | case dns.RcodeRefused: 114 | return "", fmt.Errorf("the name server refuses to perform the specified operation for policy or security reasons (REFUSED)") 115 | default: 116 | //fmt.Printf("in.Answer len:%d\n", len(in.Answer)) 117 | for _, rr := range in.Answer { 118 | //fmt.Printf("rr.String :%s\n", rr.String()) 119 | return strings.Replace(rr.String(), rr.Header().String(), "", -1), nil 120 | } 121 | } 122 | } 123 | return "", fmt.Errorf("DNS server could not be reached") 124 | } 125 | -------------------------------------------------------------------------------- /thirdlib/gdylib/fileutils/fileutils.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | //获取当前路径 13 | func GetCurrentDirectory() string { 14 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | return dir 19 | } 20 | 21 | //保存base64为文件,一般用于保存图片 22 | func SaveBase64AsFile(base64Str *string, fileURL string) (err error) { 23 | decodeStr, _ := base64.StdEncoding.DecodeString(*base64Str) //把base64写入缓存 24 | err = ioutil.WriteFile(fileURL, decodeStr, 0666) //buffer输出到jpg文件中(不做处理,直接写到文件) 25 | return 26 | } 27 | 28 | //判断文件或文件夹是否存在 29 | func FileExists(path string) (bool, error) { 30 | _, err := os.Stat(path) 31 | if err == nil { 32 | return true, nil 33 | } 34 | if os.IsNotExist(err) { 35 | return false, nil 36 | } 37 | return false, err 38 | } 39 | 40 | //保存Text到文本 41 | func SaveTextToFile(text, fileURL string) error { 42 | dstFile, err := os.Create(fileURL) 43 | if err != nil { 44 | fmt.Println(err.Error()) 45 | return err 46 | } 47 | defer dstFile.Close() 48 | dstFile.WriteString(text + "\n") 49 | return nil 50 | } 51 | 52 | //ReadTextFromFile 从文本读取内容 53 | func ReadTextFromFile(path string) (string, error) { 54 | fi, err := os.Open(path) 55 | if err != nil { 56 | return "", err 57 | } 58 | defer fi.Close() 59 | fd, err := ioutil.ReadAll(fi) 60 | return string(fd), nil 61 | } 62 | -------------------------------------------------------------------------------- /thirdlib/gdylib/fileutils/run_linux.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | //OpenProgramOrFile 启动程序 8 | func OpenProgramOrFile(argv []string) error { 9 | 10 | var startArgvs []string 11 | 12 | for i := range argv { 13 | if i == 0 { 14 | continue 15 | } 16 | startArgvs = append(startArgvs, argv[i]) 17 | } 18 | //startArgvs = append(startArgvs, "-c") 19 | //startArgvs = append(startArgvs, argv...) 20 | 21 | //fmt.Printf("fuck...%v \n", startArgvs) 22 | 23 | //cmd := exec.Command("/bin/bash", startArgvs...) 24 | cmd := exec.Command(argv[0], startArgvs...) 25 | return cmd.Start() 26 | } 27 | -------------------------------------------------------------------------------- /thirdlib/gdylib/fileutils/run_windows.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import "os/exec" 4 | 5 | //OpenProgramOrFile 启动程序 6 | func OpenProgramOrFile(argv []string) error { 7 | 8 | var startArgvs []string 9 | 10 | startArgvs = append(startArgvs, "/C") 11 | startArgvs = append(startArgvs, "start") 12 | 13 | startArgvs = append(startArgvs, argv...) 14 | 15 | cmd := exec.Command("cmd.exe", startArgvs...) 16 | return cmd.Start() 17 | } 18 | -------------------------------------------------------------------------------- /thirdlib/gdylib/ginutils/basicAuth.go: -------------------------------------------------------------------------------- 1 | package ginutils 2 | 3 | import ( 4 | "crypto/subtle" 5 | "encoding/base64" 6 | "unsafe" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type BasicAuthPair struct { 12 | value string 13 | user string 14 | } 15 | 16 | type BasicAuthPairs []BasicAuthPair 17 | 18 | func ProcessAccounts(accounts gin.Accounts) BasicAuthPairs { 19 | length := len(accounts) 20 | assert1(length > 0, "Empty list of authorized credentials") 21 | pairs := make(BasicAuthPairs, 0, length) 22 | for user, password := range accounts { 23 | assert1(user != "", "User can not be empty") 24 | value := authorizationHeader(user, password) 25 | pairs = append(pairs, BasicAuthPair{ 26 | value: value, 27 | user: user, 28 | }) 29 | } 30 | return pairs 31 | } 32 | 33 | func authorizationHeader(user, password string) string { 34 | base := user + ":" + password 35 | return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base)) 36 | } 37 | 38 | func assert1(guard bool, text string) { 39 | if !guard { 40 | panic(text) 41 | } 42 | } 43 | 44 | func StringToBytes(s string) []byte { 45 | return *(*[]byte)(unsafe.Pointer( 46 | &struct { 47 | string 48 | Cap int 49 | }{s, len(s)}, 50 | )) 51 | } 52 | 53 | func (a BasicAuthPairs) SearchCredential(authValue string) (string, bool) { 54 | if authValue == "" { 55 | return "", false 56 | } 57 | for _, pair := range a { 58 | if subtle.ConstantTimeCompare(StringToBytes(pair.value), StringToBytes(authValue)) == 1 { 59 | return pair.user, true 60 | } 61 | } 62 | return "", false 63 | } 64 | -------------------------------------------------------------------------------- /thirdlib/gdylib/ginutils/jwt.go: -------------------------------------------------------------------------------- 1 | package ginutils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/golang-jwt/jwt" 9 | ) 10 | 11 | func GetJWTToken(tokenString, tokenKey string) (t *jwt.Token, e error) { 12 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 13 | return []byte(tokenKey), nil 14 | }) 15 | if err != nil { 16 | //beego.Error("Parse token:", err) 17 | if ve, ok := err.(*jwt.ValidationError); ok { 18 | if ve.Errors&jwt.ValidationErrorMalformed != 0 { 19 | // That's not even a token 20 | return nil, errors.New("errInputData") 21 | } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { 22 | // Token is either expired or not active yet 23 | return nil, errors.New("errExpired") 24 | } else { 25 | // Couldn't handle this token 26 | return nil, errors.New("errInputData") 27 | } 28 | } else { 29 | // Couldn't handle this token 30 | return nil, errors.New("errInputData") 31 | } 32 | } 33 | if !token.Valid { 34 | //beego.Error("Token invalid:", tokenString) 35 | return nil, errors.New("errInputData") 36 | } 37 | 38 | return token, nil 39 | } 40 | 41 | // info 存储的信息 42 | // key 加密的key 43 | // exp 有效期 44 | // GetJWTTokenString 获取Token字符串 45 | func GetJWTTokenString(info map[string]interface{}, key string, exp time.Duration) (string, error) { 46 | claims := make(jwt.MapClaims) 47 | claims["exp"] = time.Now().Add(exp).Unix() //token 24小时有效期 48 | for k := range info { 49 | claims[k] = info[k] 50 | } 51 | 52 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 53 | tokenString, err := token.SignedString([]byte(key)) 54 | if err != nil { 55 | return "", fmt.Errorf("生成TokenString出错:%s", err.Error()) 56 | } 57 | 58 | return tokenString, nil 59 | } 60 | -------------------------------------------------------------------------------- /thirdlib/gdylib/ginutils/staticFilesHandler.go: -------------------------------------------------------------------------------- 1 | package ginutils 2 | 3 | import ( 4 | "io/fs" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func HandlerStaticFiles(files fs.FS) gin.HandlerFunc { 12 | fileServer := http.FileServer(http.FS(files)) 13 | return func(c *gin.Context) { 14 | staticFile := isStaticFile(http.FS(files), c.Request.URL.Path, true) 15 | if staticFile { 16 | fileServer.ServeHTTP(c.Writer, c.Request) 17 | c.Abort() 18 | return 19 | } 20 | c.Next() 21 | } 22 | } 23 | 24 | func isStaticFile(fs http.FileSystem, name string, redirect bool) (isFile bool) { 25 | const indexPage = "/index.html" 26 | if strings.HasSuffix(name, indexPage) { 27 | return true 28 | } 29 | f, err := fs.Open(name) 30 | if err != nil { 31 | return false 32 | } 33 | defer f.Close() 34 | _, err = f.Stat() 35 | return err == nil 36 | } 37 | -------------------------------------------------------------------------------- /thirdlib/gdylib/ginutils/utils.go: -------------------------------------------------------------------------------- 1 | package ginutils 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // Cors 处理跨域请求,支持options访问 11 | func Cors(params ...interface{}) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | method := c.Request.Method 14 | 15 | c.Header("Access-Control-Allow-Origin", "*") 16 | c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,access_token") 17 | c.Header("Access-Control-Allow-Methods", "POST, GET, PUT,OPTIONS,DELETE") 18 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") 19 | c.Header("Access-Control-Allow-Credentials", "true") 20 | 21 | //放行所有OPTIONS方法 22 | if method == "OPTIONS" { 23 | c.AbortWithStatus(http.StatusNoContent) 24 | } 25 | // 处理请求 26 | c.Next() 27 | } 28 | } 29 | 30 | // GetChildDomain 获取子域名部分 31 | func GetChildDomain(host string) string { 32 | hostSplitList := strings.Split(host, ".") 33 | listLen := len(hostSplitList) 34 | var resBuilder strings.Builder 35 | 36 | for i := range hostSplitList { 37 | if i >= listLen-2 { 38 | break 39 | } 40 | if resBuilder.Len() > 0 { 41 | resBuilder.WriteString(".") 42 | } 43 | resBuilder.WriteString(hostSplitList[i]) 44 | } 45 | 46 | return resBuilder.String() 47 | } 48 | -------------------------------------------------------------------------------- /thirdlib/gdylib/httputils/goututils.go: -------------------------------------------------------------------------------- 1 | package httputils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | "time" 8 | 9 | "github.com/guonaihong/gout" 10 | "github.com/guonaihong/gout/dataflow" 11 | ) 12 | 13 | func NewGout(transportNetwork, localAddr string, secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*dataflow.Gout, error) { 14 | httpClient, err := CreateHttpClient(transportNetwork, localAddr, secureSkipVerify, proxyType, proxyUrl, user, passwd, timeout) 15 | if err != nil { 16 | return nil, fmt.Errorf("CreateHttpClient error:%s", err.Error()) 17 | } 18 | 19 | return gout.New(httpClient), nil 20 | } 21 | 22 | func GetAndParseJSONResponseFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string, 23 | headers map[string]string, secureSkipVerify bool, timeout time.Duration, result interface{}) error { 24 | _, bytes, err := GetBytesFromGoutDoHttpRequest( 25 | transportNetwork, 26 | localAddr, 27 | method, 28 | url, 29 | requestBody, 30 | proxyType, 31 | proxyUrl, user, passwd, headers, secureSkipVerify, timeout) 32 | if err != nil { 33 | return fmt.Errorf("GetBytesFromHttpResponse err:%s", err.Error()) 34 | } 35 | if len(bytes) > 0 { 36 | err = json.Unmarshal(bytes, &result) 37 | if err != nil { 38 | return fmt.Errorf("GetAndParseJSONResponseFromHttpResponse 解析JSON结果出错:%s", err.Error()) 39 | } 40 | } 41 | return nil 42 | } 43 | 44 | func GetStringGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string, 45 | headers map[string]string, secureSkipVerify bool, timeout time.Duration) (int, string, error) { 46 | statusCode, bytes, err := GetBytesFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd, headers, secureSkipVerify, timeout) 47 | if err != nil { 48 | return 0, "", err 49 | } 50 | return statusCode, string(bytes), nil 51 | } 52 | 53 | func GetBytesFromGoutDoHttpRequest(transportNetwork, localAddr, method, url, requestBody, proxyType, proxyUrl, user, passwd string, 54 | headers map[string]string, secureSkipVerify bool, timeout time.Duration) (int, []byte, error) { 55 | gout, err := NewGout( 56 | transportNetwork, 57 | localAddr, 58 | secureSkipVerify, 59 | proxyType, 60 | proxyUrl, 61 | user, 62 | passwd, timeout) 63 | if err != nil { 64 | return 0, []byte{}, fmt.Errorf("GoutDoHttpRequest err:%s", err.Error()) 65 | } 66 | 67 | switch strings.ToLower(method) { 68 | case "get": 69 | gout.GET(url) 70 | case "post": 71 | gout.POST(url) 72 | case "put": 73 | gout.PUT(url) 74 | case "delete": 75 | gout.DELETE(url) 76 | default: 77 | return 0, []byte{}, fmt.Errorf("未支持的Callback请求方法:%s", method) 78 | } 79 | 80 | basicAuthUserName, BasicAuthUserNameOk := headers["BasicAuthUserName"] 81 | basicAuthPassword, BasicAuthPasswordOk := headers["BasicAuthPassword"] 82 | if BasicAuthUserNameOk && BasicAuthPasswordOk { 83 | gout.SetBasicAuth(basicAuthUserName, basicAuthPassword) 84 | } 85 | delete(headers, "BasicAuthUserName") 86 | delete(headers, "BasicAuthPassword") 87 | 88 | if len(requestBody) > 0 && method != "get" { 89 | if json.Valid([]byte(requestBody)) { 90 | gout.SetJSON(requestBody) 91 | } else { 92 | gout.SetWWWForm(requestBody) 93 | } 94 | } 95 | 96 | gout.SetHeader(headers) 97 | //gout.SetTimeout(timeout) 98 | 99 | resp, err := gout.Response() 100 | if err != nil { 101 | return 0, []byte{}, fmt.Errorf("gout.Response() error:%s", err.Error()) 102 | } 103 | respByte, respErr := GetBytesFromHttpResponse(resp) 104 | 105 | return resp.StatusCode, respByte, respErr 106 | } 107 | -------------------------------------------------------------------------------- /thirdlib/gdylib/httputils/httpclient.go: -------------------------------------------------------------------------------- 1 | package httputils 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | ) 7 | 8 | func CreateHttpClient(transportNetwork, localAddr string, secureSkipVerify bool, proxyType, proxyUrl, user, passwd string, timeout time.Duration) (*http.Client, error) { 9 | 10 | transport, err := NewTransport(transportNetwork, localAddr, secureSkipVerify, proxyType, proxyUrl, user, passwd) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | httpClient := &http.Client{ 16 | Timeout: timeout, 17 | Transport: transport} 18 | 19 | return httpClient, nil 20 | } 21 | -------------------------------------------------------------------------------- /thirdlib/gdylib/pool/buf.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var ( 8 | bufPool sync.Pool 9 | bufPool1k sync.Pool 10 | bufPool2k sync.Pool 11 | bufPool4k sync.Pool 12 | bufPool8k sync.Pool 13 | bufPool16k sync.Pool 14 | ) 15 | 16 | const ( 17 | k16 = 16 * 1024 18 | k8 = 8 * 1024 19 | k4 = 4 * 1024 20 | k2 = 2 * 1024 21 | k1 = 1024 22 | ) 23 | 24 | func GetBuf(size int) []byte { 25 | var x interface{} 26 | switch { 27 | case size >= k16: 28 | x = bufPool16k.Get() 29 | case size >= k8: 30 | x = bufPool8k.Get() 31 | case size >= k4: 32 | x = bufPool4k.Get() 33 | case size >= k2: 34 | x = bufPool2k.Get() 35 | case size >= k1: 36 | x = bufPool1k.Get() 37 | default: 38 | x = bufPool.Get() 39 | } 40 | if x == nil { 41 | return make([]byte, size) 42 | } 43 | buf := x.([]byte) 44 | if cap(buf) < size { 45 | return make([]byte, size) 46 | } 47 | return buf[:size] 48 | } 49 | 50 | func PutBuf(buf interface{}) { 51 | size := cap(buf.([]byte)) 52 | switch { 53 | case size >= k16: 54 | bufPool16k.Put(buf) 55 | case size >= k8: 56 | bufPool8k.Put(buf) 57 | case size >= k4: 58 | bufPool4k.Put(buf) 59 | 60 | case size >= k2: 61 | bufPool2k.Put(buf) 62 | 63 | case size >= k1: 64 | bufPool1k.Put(buf) 65 | default: 66 | bufPool.Put(buf) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /thirdlib/gdylib/recoverutil/recoverutil.go: -------------------------------------------------------------------------------- 1 | package recoverutil 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime/debug" 7 | "strings" 8 | "time" 9 | 10 | "github.com/gdy666/lucky/thirdlib/gdylib/fileutils" 11 | "github.com/gdy666/lucky/thirdlib/gdylib/stderrredirect" 12 | ) 13 | 14 | //RecoverHandler 恢复处理 15 | func RecoverHandler(recoverErr interface{}, exit, reboot bool, panicFileURL string) { 16 | if recoverErr == nil { 17 | return 18 | } 19 | 20 | outputPanicV2(panicFileURL, recoverErr) 21 | 22 | if reboot { 23 | 24 | var argvsBuilder strings.Builder 25 | 26 | for i := range os.Args { 27 | if i == 0 { 28 | continue 29 | } 30 | if argvsBuilder.Len() == 0 { 31 | argvsBuilder.WriteString(os.Args[i]) 32 | } else { 33 | argvsBuilder.WriteString(" ") 34 | argvsBuilder.WriteString(os.Args[i]) 35 | } 36 | } 37 | 38 | fileutils.OpenProgramOrFile(os.Args) //重启程序 39 | //fileutil.OpenProgramOrFile(restartURIBuilder.String()) 40 | } 41 | if exit { 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func outputPanic(panicFileURL string, recoverErr interface{}) { 47 | exeName := os.Args[0] //获取程序名称 48 | now := time.Now() //获取当前时间 49 | pid := os.Getpid() //获取进程ID 50 | 51 | if !strings.Contains(panicFileURL, ":") && !strings.HasPrefix(panicFileURL, "/") { //相对路径 52 | panicFileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), panicFileURL) 53 | } 54 | 55 | fileDir := "" 56 | lastIndex := strings.LastIndex(panicFileURL, string(os.PathSeparator)) 57 | if lastIndex > 0 { 58 | fileDir = panicFileURL[:lastIndex] 59 | } 60 | 61 | if err := os.MkdirAll(fileDir, 0755); err != nil { 62 | panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error())) 63 | } 64 | 65 | file, err := os.OpenFile(panicFileURL, os.O_CREATE|os.O_APPEND, 0666) 66 | if err != nil { 67 | panic(fmt.Sprintf("panic重定向出错:%s", err.Error())) 68 | } 69 | 70 | defer file.Close() 71 | 72 | timeStr := now.Format("2006-01-02 15:04:05") //设定时间格式 73 | file.WriteString("\n\n\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n") 74 | file.WriteString(fmt.Sprintf("%s-%d-%s dump LOG\r\n", exeName, pid, timeStr)) 75 | file.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息 76 | file.WriteString(string(debug.Stack())) //输出堆栈信息 77 | file.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n") 78 | 79 | // timeStr := now.Format("20060102150405") //设定时间格式 80 | // fname := fmt.Sprintf("%s-%d-%s-dump.log", exeName, pid, timeStr) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) 81 | // fmt.Println("dump to file ", fname) 82 | 83 | // f, err := os.Create(fname) 84 | // if err != nil { 85 | // return 86 | // } 87 | // defer f.Close() 88 | 89 | // if recoverErr != nil { 90 | // f.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息 91 | // f.WriteString("========\r\n") 92 | // } 93 | 94 | // f.WriteString(string(debug.Stack())) //输出堆栈信息 95 | } 96 | 97 | func outputPanicV2(panicFileURL string, recoverErr interface{}) { 98 | exeName := os.Args[0] //获取程序名称 99 | now := time.Now() //获取当前时间 100 | pid := os.Getpid() //获取进程ID 101 | setPanicRedirect := true 102 | if panicFileURL == "" { //空路径不设置 103 | setPanicRedirect = false 104 | } 105 | if !strings.Contains(panicFileURL, ":") && !strings.HasPrefix(panicFileURL, "/") { //相对路径 106 | panicFileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), panicFileURL) 107 | } 108 | 109 | // fileDir := "" 110 | // lastIndex := strings.LastIndex(panicFileURL, string(os.PathSeparator)) 111 | // if lastIndex > 0 { 112 | // fileDir = panicFileURL[:lastIndex] 113 | // } 114 | 115 | // if err := os.MkdirAll(fileDir, 0755); err != nil { 116 | // panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error())) 117 | // } 118 | 119 | if setPanicRedirect { 120 | stderrredirect.PanicRedirect(panicFileURL) 121 | } 122 | 123 | timeStr := now.Format("2006-01-02 15:04:05") //设定时间格式 124 | stderrredirect.PanicFile.WriteString("\n\n\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n") 125 | stderrredirect.PanicFile.WriteString(fmt.Sprintf("%s-%d-%s dump LOG\r\n", exeName, pid, timeStr)) 126 | // stderrredirect.PanicFile.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息 127 | stderrredirect.PanicFile.WriteString(string(debug.Stack())) //输出堆栈信息 128 | stderrredirect.PanicFile.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n") 129 | 130 | // file, err := os.OpenFile(panicFileURL, os.O_CREATE|os.O_APPEND, 0666) 131 | // if err != nil { 132 | // panic(fmt.Sprintf("panic重定向出错:%s", err.Error())) 133 | // } 134 | 135 | // defer file.Close() 136 | } 137 | -------------------------------------------------------------------------------- /thirdlib/gdylib/slice/options.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | func DeleteAnyListlice(a []any, deleteIndex int) []any { 4 | j := 0 5 | for i := range a { 6 | if i != deleteIndex { 7 | a[j] = a[i] 8 | j++ 9 | } 10 | } 11 | return a[:j] 12 | } 13 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stderrredirect/stderrredirect_linux.go: -------------------------------------------------------------------------------- 1 | package stderrredirect 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "sync" 8 | "syscall" 9 | 10 | "github.com/gdy666/lucky/thirdlib/gdylib/fileutils" 11 | ) 12 | 13 | var PanicFile *os.File 14 | 15 | var doOnce sync.Once 16 | 17 | //PanicRedirect panic重定向 18 | func PanicRedirect(fileURL string) { 19 | 20 | doOnce.Do(func() { 21 | if !strings.HasPrefix(fileURL, "/") { //相对路径 22 | fileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), fileURL) 23 | } 24 | 25 | lastIndex := strings.LastIndex(fileURL, string(os.PathSeparator)) 26 | 27 | fileDir := "" 28 | 29 | if lastIndex > 0 { 30 | fileDir = fileURL[:lastIndex] 31 | } 32 | 33 | if err := os.MkdirAll(fileDir, 0755); err != nil { 34 | panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error())) 35 | } 36 | 37 | file, err := os.OpenFile(fileURL, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 38 | PanicFile = file 39 | if err != nil { 40 | return 41 | } 42 | if err = syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil { 43 | return 44 | } 45 | return 46 | }) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stderrredirect/stderrredirect_windows.go: -------------------------------------------------------------------------------- 1 | package stderrredirect 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "sync" 8 | "syscall" 9 | 10 | "github.com/gdy666/lucky/thirdlib/gdylib/fileutils" 11 | ) 12 | 13 | //错误输出重定向,用于捕获闪退信息 14 | 15 | //PanicFile 记录闪退的文件 16 | var PanicFile *os.File 17 | 18 | var doOnce sync.Once 19 | 20 | //PanicRedirect panic重定向 21 | func PanicRedirect(fileURL string) { 22 | 23 | doOnce.Do(func() { 24 | if !strings.Contains(fileURL, ":") { //相对路径 25 | fileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), fileURL) 26 | } 27 | 28 | //fmt.Printf("FileURL:%s\n", fileURL) 29 | 30 | lastIndex := strings.LastIndex(fileURL, string(os.PathSeparator)) 31 | 32 | fileDir := "" 33 | 34 | if lastIndex > 0 { 35 | fileDir = fileURL[:lastIndex] 36 | } 37 | 38 | if err := os.MkdirAll(fileDir, 0755); err != nil { 39 | panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error())) 40 | } 41 | 42 | //fileDir := strings.LastIndex(fileURL, string(os.PathSeparator)) 43 | 44 | file, err := os.OpenFile(fileURL, os.O_CREATE|os.O_APPEND, 0666) 45 | PanicFile = file 46 | if err != nil { 47 | panic(fmt.Sprintf("panic重定向出错:%s", err.Error())) 48 | } 49 | kernel32 := syscall.NewLazyDLL("kernel32.dll") 50 | setStdHandle := kernel32.NewProc("SetStdHandle") 51 | sh := syscall.STD_ERROR_HANDLE 52 | v, _, err := setStdHandle.Call(uintptr(sh), uintptr(file.Fd())) 53 | if v == 0 { 54 | return 55 | } 56 | }) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stringsp/binary.go: -------------------------------------------------------------------------------- 1 | package stringsp 2 | 3 | import "fmt" 4 | 5 | const ( 6 | KB = uint(1024) 7 | MB = uint64(1024 * 1024) 8 | GB = uint64(1024 * 1024 * 1024) 9 | TB = uint64(1024 * 1024 * 1024 * 1024) 10 | EB = uint64(1024 * 1024 * 1024 * 1024 * 1024) 11 | ) 12 | 13 | func BinaryUnitToStr(binaryUnit uint64) (size string) { 14 | switch { 15 | case binaryUnit < 1024: 16 | return fmt.Sprintf("%.2fB", float64(binaryUnit)/float64(1)) 17 | case binaryUnit < MB: //1024*1024 18 | return fmt.Sprintf("%.2fKB", float64(binaryUnit)/float64(KB)) 19 | case binaryUnit < GB: //1024 * 1024 * 1024 20 | return fmt.Sprintf("%.2fMB", float64(binaryUnit)/float64(MB)) 21 | case binaryUnit < TB: //1024 * 1024 * 1024 * 1024 22 | return fmt.Sprintf("%.2fGB", float64(binaryUnit)/float64(GB)) 23 | case binaryUnit < EB: //1024 * 1024 * 1024 * 1024 * 1024 24 | return fmt.Sprintf("%.2fTB", float64(binaryUnit)/float64(TB)) 25 | default: 26 | return fmt.Sprintf("%.2fEB", float64(binaryUnit)/float64(EB)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stringsp/randomutils.go: -------------------------------------------------------------------------------- 1 | package stringsp 2 | 3 | import ( 4 | "math/rand" 5 | "strings" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var strModel = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | var stringsBytes = []byte(strModel) 12 | var strModelLength = len(strModel) 13 | 14 | var numStrModel = "0123456789" 15 | var numStrBytes = []byte(numStrModel) 16 | var numStrLength = len(numStrModel) 17 | 18 | var randVar = rand.New(rand.NewSource(time.Now().UnixNano())) 19 | 20 | //GetRandomString 生成随机字符串 21 | func GetRandomString(len int) string { 22 | var resBuf strings.Builder 23 | for i := 0; i < len; i++ { 24 | resBuf.WriteByte(stringsBytes[randVar.Intn(strModelLength)]) 25 | } 26 | return resBuf.String() 27 | } 28 | 29 | //GetRandomStringNum 生成随机数字字符串 30 | func GetRandomStringNum(len int) string { 31 | var resBuf strings.Builder 32 | for i := 0; i < len; i++ { 33 | resBuf.WriteByte(numStrBytes[randVar.Intn(numStrLength)]) 34 | } 35 | return resBuf.String() 36 | 37 | } 38 | 39 | var timeStampIDMutex sync.Mutex 40 | var pretimeStampID int64 = 0 41 | 42 | //GetTimeStampID 获取时间戳ID 43 | func GetTimeStampID() int64 { 44 | timeStampIDMutex.Lock() 45 | defer timeStampIDMutex.Unlock() 46 | id := time.Now().UnixNano() 47 | 48 | CHECK: 49 | if id == pretimeStampID || id < pretimeStampID { 50 | if id < pretimeStampID { 51 | id = pretimeStampID + 1 52 | } else { 53 | id++ 54 | } 55 | 56 | goto CHECK 57 | } 58 | 59 | pretimeStampID = id 60 | return id 61 | } 62 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stringsp/stringsp.go: -------------------------------------------------------------------------------- 1 | package stringsp 2 | 3 | func StrIsInList(str string, strList []string) bool { 4 | checkMap := make(map[string]uint8) 5 | for i := range strList { 6 | checkMap[strList[i]] = 1 7 | } 8 | if _, ok := checkMap[str]; ok { 9 | return true 10 | } 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /thirdlib/gdylib/stringsp/url.go: -------------------------------------------------------------------------------- 1 | package stringsp 2 | 3 | import ( 4 | "net/url" 5 | "strings" 6 | ) 7 | 8 | func GetHostAndPathFromURL(urlstr string) (string, string, string, string, error) { 9 | if !strings.HasPrefix(urlstr, "http") { 10 | urlstr = "http://" + urlstr 11 | } 12 | u, err := url.Parse(urlstr) 13 | if err != nil { 14 | return "", "", "", "", err 15 | } 16 | return u.Scheme, u.Hostname(), u.Port(), u.Path, nil 17 | } 18 | -------------------------------------------------------------------------------- /thirdlib/go-wol/magic_packet.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | import ( 6 | "bytes" 7 | "encoding/binary" 8 | "fmt" 9 | "net" 10 | "regexp" 11 | ) 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | var ( 16 | delims = ":-" 17 | reMAC = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`) 18 | ) 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // MACAddress represents a 6 byte network mac address. 23 | type MACAddress [6]byte 24 | 25 | // MagicPacket is constituted of 6 bytes of 0xFF followed by 16-groups of the 26 | // destination MAC address. 27 | type MagicPacket struct { 28 | header [6]byte 29 | payload [16]MACAddress 30 | } 31 | 32 | // New returns a magic packet based on a mac address string. 33 | func New(mac string) (*MagicPacket, error) { 34 | var packet MagicPacket 35 | var macAddr MACAddress 36 | 37 | hwAddr, err := net.ParseMAC(mac) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | // We only support 6 byte MAC addresses since it is much harder to use the 43 | // binary.Write(...) interface when the size of the MagicPacket is dynamic. 44 | if !reMAC.MatchString(mac) { 45 | return nil, fmt.Errorf("%s is not a IEEE 802 MAC-48 address", mac) 46 | } 47 | 48 | // Copy bytes from the returned HardwareAddr -> a fixed size MACAddress. 49 | for idx := range macAddr { 50 | macAddr[idx] = hwAddr[idx] 51 | } 52 | 53 | // Setup the header which is 6 repetitions of 0xFF. 54 | for idx := range packet.header { 55 | packet.header[idx] = 0xFF 56 | } 57 | 58 | // Setup the payload which is 16 repetitions of the MAC addr. 59 | for idx := range packet.payload { 60 | packet.payload[idx] = macAddr 61 | } 62 | 63 | return &packet, nil 64 | } 65 | 66 | // Marshal serializes the magic packet structure into a 102 byte slice. 67 | func (mp *MagicPacket) Marshal() ([]byte, error) { 68 | var buf bytes.Buffer 69 | if err := binary.Write(&buf, binary.BigEndian, mp); err != nil { 70 | return nil, err 71 | } 72 | 73 | return buf.Bytes(), nil 74 | } 75 | -------------------------------------------------------------------------------- /thirdlib/go-wol/wol.go: -------------------------------------------------------------------------------- 1 | package wol 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func WakeUpRepeat(macAddr, broadcastIP, bcastInterface string, port, repeat int) { 9 | i := 0 10 | for { 11 | WakeUp(macAddr, broadcastIP, bcastInterface, port) 12 | i++ 13 | if i >= repeat { 14 | return 15 | } 16 | } 17 | } 18 | 19 | func WakeUp(macAddr, broadcastIP, bcastInterface string, port int) error { 20 | var localAddr *net.UDPAddr 21 | var err error 22 | if bcastInterface != "" { 23 | localAddr, err = ipFromInterface(bcastInterface) 24 | if err != nil { 25 | return err 26 | } 27 | } 28 | 29 | bcastAddr := fmt.Sprintf("%s:%d", broadcastIP, port) 30 | udpAddr, err := net.ResolveUDPAddr("udp", bcastAddr) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | // Build the magic packet. 36 | mp, err := New(macAddr) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | bs, err := mp.Marshal() 42 | if err != nil { 43 | return err 44 | } 45 | 46 | conn, err := net.DialUDP("udp", localAddr, udpAddr) 47 | if err != nil { 48 | return err 49 | } 50 | defer conn.Close() 51 | 52 | //fmt.Printf("Attempting to send a magic packet to MAC %s\n", macAddr) 53 | //fmt.Printf("... Broadcasting to: %s\n", bcastAddr) 54 | n, err := conn.Write(bs) 55 | if err == nil && n != 102 { 56 | err = fmt.Errorf("magic packet sent was %d bytes (expected 102 bytes sent)", n) 57 | } 58 | if err != nil { 59 | return err 60 | } 61 | 62 | //fmt.Printf("Magic packet sent successfully to %s\n", macAddr) 63 | return nil 64 | 65 | } 66 | 67 | // ipFromInterface returns a `*net.UDPAddr` from a network interface name. 68 | func ipFromInterface(iface string) (*net.UDPAddr, error) { 69 | ief, err := net.InterfaceByName(iface) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | addrs, err := ief.Addrs() 75 | if err == nil && len(addrs) <= 0 { 76 | err = fmt.Errorf("no address associated with interface %s", iface) 77 | } 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | // Validate that one of the addrs is a valid network IP address. 83 | for _, addr := range addrs { 84 | switch ip := addr.(type) { 85 | case *net.IPNet: 86 | if !ip.IP.IsLoopback() && ip.IP.To4() != nil { 87 | return &net.UDPAddr{ 88 | IP: ip.IP, 89 | }, nil 90 | } 91 | } 92 | } 93 | return nil, fmt.Errorf("no address associated with interface %s", iface) 94 | } 95 | -------------------------------------------------------------------------------- /thirdlib/jeessy2/ddns-go/util/aliyun_signer.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" 6 | "crypto/sha1" 7 | "crypto/sha256" 8 | "encoding/base64" 9 | "fmt" 10 | "hash" 11 | "io" 12 | "net/url" 13 | ) 14 | 15 | // https://github.com/rosbit/aliyun-sign/blob/master/aliyun-sign.go 16 | 17 | var ( 18 | signMethodMap = map[string]func() hash.Hash{ 19 | "HMAC-SHA1": sha1.New, 20 | "HMAC-SHA256": sha256.New, 21 | "HMAC-MD5": md5.New, 22 | } 23 | ) 24 | 25 | func HmacSign(signMethod string, httpMethod string, appKeySecret string, vals url.Values) (signature []byte) { 26 | key := []byte(appKeySecret + "&") 27 | 28 | var h hash.Hash 29 | if method, ok := signMethodMap[signMethod]; ok { 30 | h = hmac.New(method, key) 31 | } else { 32 | h = hmac.New(sha1.New, key) 33 | } 34 | makeDataToSign(h, httpMethod, vals) 35 | return h.Sum(nil) 36 | } 37 | 38 | func HmacSignToB64(signMethod string, httpMethod string, appKeySecret string, vals url.Values) (signature string) { 39 | return base64.StdEncoding.EncodeToString(HmacSign(signMethod, httpMethod, appKeySecret, vals)) 40 | } 41 | 42 | type strToEnc struct { 43 | s string 44 | e bool 45 | } 46 | 47 | func makeDataToSign(w io.Writer, httpMethod string, vals url.Values) { 48 | in := make(chan *strToEnc) 49 | go func() { 50 | in <- &strToEnc{s: httpMethod} 51 | in <- &strToEnc{s: "&"} 52 | in <- &strToEnc{s: "/", e: true} 53 | in <- &strToEnc{s: "&"} 54 | in <- &strToEnc{s: vals.Encode(), e: true} 55 | close(in) 56 | }() 57 | 58 | specialUrlEncode(in, w) 59 | } 60 | 61 | var ( 62 | encTilde = "%7E" // '~' -> "%7E" 63 | encBlank = []byte("%20") // ' ' -> "%20" 64 | tilde = []byte("~") 65 | ) 66 | 67 | func specialUrlEncode(in <-chan *strToEnc, w io.Writer) { 68 | for s := range in { 69 | if !s.e { 70 | io.WriteString(w, s.s) 71 | continue 72 | } 73 | 74 | l := len(s.s) 75 | for i := 0; i < l; { 76 | ch := s.s[i] 77 | 78 | switch ch { 79 | case '%': 80 | if encTilde == s.s[i:i+3] { 81 | w.Write(tilde) 82 | i += 3 83 | continue 84 | } 85 | fallthrough 86 | case '*', '/', '&', '=': 87 | fmt.Fprintf(w, "%%%02X", ch) 88 | case '+': 89 | w.Write(encBlank) 90 | default: 91 | fmt.Fprintf(w, "%c", ch) 92 | } 93 | 94 | i += 1 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /thirdlib/jeessy2/ddns-go/util/aliyun_signer_util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "net/url" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | // AliyunSigner AliyunSigner 10 | func AliyunSigner(accessKeyID, accessSecret string, params *url.Values) { 11 | // 公共参数 12 | params.Set("SignatureMethod", "HMAC-SHA1") 13 | params.Set("SignatureNonce", strconv.FormatInt(time.Now().UnixNano(), 10)) 14 | params.Set("AccessKeyId", accessKeyID) 15 | params.Set("SignatureVersion", "1.0") 16 | params.Set("Timestamp", time.Now().UTC().Format("2006-01-02T15:04:05Z")) 17 | params.Set("Format", "JSON") 18 | params.Set("Version", "2015-01-09") 19 | params.Set("Signature", HmacSignToB64("HMAC-SHA1", "GET", accessSecret, *params)) 20 | } 21 | -------------------------------------------------------------------------------- /thirdlib/jeessy2/ddns-go/util/baidu_signer.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "fmt" 8 | "net/http" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // https://cloud.baidu.com/doc/Reference/s/Njwvz1wot 14 | 15 | const ( 16 | BaiduDateFormat = "2006-01-02T15:04:05Z" 17 | expirationPeriod = "1800" 18 | ) 19 | 20 | func HmacSha256Hex(secret, message string) string { 21 | key := []byte(secret) 22 | 23 | h := hmac.New(sha256.New, key) 24 | h.Write([]byte(message)) 25 | sha := hex.EncodeToString(h.Sum(nil)) 26 | return sha 27 | } 28 | 29 | func BaiduCanonicalURI(r *http.Request) string { 30 | pattens := strings.Split(r.URL.Path, "/") 31 | var uri []string 32 | for _, v := range pattens { 33 | uri = append(uri, escape(v)) 34 | } 35 | urlpath := strings.Join(uri, "/") 36 | if len(urlpath) == 0 || urlpath[len(urlpath)-1] != '/' { 37 | urlpath = urlpath + "/" 38 | } 39 | return urlpath[0 : len(urlpath)-1] 40 | } 41 | 42 | // BaiduSigner set Authorization header 43 | func BaiduSigner(accessKeyID, accessSecret string, r *http.Request) { 44 | //format: bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds} 45 | authStringPrefix := "bce-auth-v1/" + accessKeyID + "/" + time.Now().UTC().Format(BaiduDateFormat) + "/" + expirationPeriod 46 | baiduCanonicalURL := BaiduCanonicalURI(r) 47 | 48 | //format: HTTP Method + "\n" + CanonicalURI + "\n" + CanonicalQueryString + "\n" + CanonicalHeaders 49 | //由于仅仅调用三个POST接口且不会更改,这里CanonicalQueryString和CanonicalHeaders直接写死 50 | CanonicalReq := fmt.Sprintf("%s\n%s\n%s\n%s", r.Method, baiduCanonicalURL, "", "host:bcd.baidubce.com") 51 | 52 | signingKey := HmacSha256Hex(accessSecret, authStringPrefix) 53 | signature := HmacSha256Hex(signingKey, CanonicalReq) 54 | 55 | //format: authStringPrefix/{signedHeaders}/{signature} 56 | authString := authStringPrefix + "/host/" + signature 57 | r.Header.Set(HeaderAuthorization, authString) 58 | } 59 | -------------------------------------------------------------------------------- /thirdlib/jeessy2/ddns-go/util/escape.go: -------------------------------------------------------------------------------- 1 | // based on https://github.com/golang/go/blob/master/src/net/url/url.go 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package util 7 | 8 | func shouldEscape(c byte) bool { 9 | if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '~' || c == '.' { 10 | return false 11 | } 12 | return true 13 | } 14 | func escape(s string) string { 15 | hexCount := 0 16 | for i := 0; i < len(s); i++ { 17 | c := s[i] 18 | if shouldEscape(c) { 19 | hexCount++ 20 | } 21 | } 22 | 23 | if hexCount == 0 { 24 | return s 25 | } 26 | 27 | t := make([]byte, len(s)+2*hexCount) 28 | j := 0 29 | for i := 0; i < len(s); i++ { 30 | switch c := s[i]; { 31 | case shouldEscape(c): 32 | t[j] = '%' 33 | t[j+1] = "0123456789ABCDEF"[c>>4] 34 | t[j+2] = "0123456789ABCDEF"[c&15] 35 | j += 3 36 | default: 37 | t[j] = s[i] 38 | j++ 39 | } 40 | } 41 | return string(t) 42 | } 43 | -------------------------------------------------------------------------------- /thirdlib/jeessy2/ddns-go/util/net.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | // IsPrivateNetwork 是否为私有地址 9 | // https://en.wikipedia.org/wiki/Private_network 10 | func IsPrivateNetwork(remoteAddr string) bool { 11 | lastIndex := strings.LastIndex(remoteAddr, ":") 12 | if lastIndex < 1 { 13 | return false 14 | } 15 | 16 | remoteAddr = remoteAddr[:lastIndex] 17 | 18 | // ipv6 19 | if strings.HasPrefix(remoteAddr, "[") && strings.HasSuffix(remoteAddr, "]") { 20 | remoteAddr = remoteAddr[1 : len(remoteAddr)-1] 21 | } 22 | 23 | if ip := net.ParseIP(remoteAddr); ip != nil { 24 | return ip.IsLoopback() || // 127/8, ::1 25 | ip.IsPrivate() || // 10/8, 172.16/12, 192.168/16, fc00::/7 26 | ip.IsLinkLocalUnicast() // 169.254/16, fe80::/10 27 | } 28 | 29 | // localhost 30 | if remoteAddr == "localhost" { 31 | return true 32 | } 33 | 34 | return false 35 | } 36 | -------------------------------------------------------------------------------- /web.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gdy666/lucky/config" 5 | "github.com/gdy666/lucky/web" 6 | ) 7 | 8 | func RunAdminWeb(conf *config.BaseConfigure) { 9 | //listen := fmt.Sprintf(":%d", listenPort) 10 | go web.RunAdminWeb(conf) 11 | } 12 | -------------------------------------------------------------------------------- /web/adminviews/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /web/adminviews/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /web/adminviews/README.md: -------------------------------------------------------------------------------- 1 | # lucky-adminviews 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 8 | 9 | ## Customize configuration 10 | 11 | See [Vite Configuration Reference](https://vitejs.dev/config/). 12 | 13 | ## Project Setup 14 | 15 | ```sh 16 | npm install 17 | ``` 18 | 19 | ### Compile and Hot-Reload for Development 20 | 21 | ```sh 22 | npm run dev 23 | ``` 24 | 25 | ### Compile and Minify for Production 26 | 27 | ```sh 28 | npm run build 29 | ``` 30 | -------------------------------------------------------------------------------- /web/adminviews/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | export {} 3 | declare global { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /web/adminviews/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/core/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | declare module '@vue/runtime-core' { 7 | export interface GlobalComponents { 8 | About: typeof import('./src/components/About.vue')['default'] 9 | BlackLists: typeof import('./src/components/BlackLists.vue')['default'] 10 | DDNS: typeof import('./src/components/DDNS.vue')['default'] 11 | DDNSSet: typeof import('./src/components/DDNSSet.vue')['default'] 12 | ElAffix: typeof import('element-plus/es')['ElAffix'] 13 | ElButton: typeof import('element-plus/es')['ElButton'] 14 | ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] 15 | ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] 16 | ElCollapse: typeof import('element-plus/es')['ElCollapse'] 17 | ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] 18 | ElContainer: typeof import('element-plus/es')['ElContainer'] 19 | ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] 20 | ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] 21 | ElDialog: typeof import('element-plus/es')['ElDialog'] 22 | ElDivider: typeof import('element-plus/es')['ElDivider'] 23 | ElForm: typeof import('element-plus/es')['ElForm'] 24 | ElFormItem: typeof import('element-plus/es')['ElFormItem'] 25 | ElHeader: typeof import('element-plus/es')['ElHeader'] 26 | ElIcon: typeof import('element-plus/es')['ElIcon'] 27 | ElInput: typeof import('element-plus/es')['ElInput'] 28 | ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] 29 | ElLink: typeof import('element-plus/es')['ElLink'] 30 | ElMain: typeof import('element-plus/es')['ElMain'] 31 | ElMenu: typeof import('element-plus/es')['ElMenu'] 32 | ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] 33 | ElOption: typeof import('element-plus/es')['ElOption'] 34 | ElPagination: typeof import('element-plus/es')['ElPagination'] 35 | ElRadio: typeof import('element-plus/es')['ElRadio'] 36 | ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] 37 | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] 38 | ElSelect: typeof import('element-plus/es')['ElSelect'] 39 | ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] 40 | ElSwitch: typeof import('element-plus/es')['ElSwitch'] 41 | ElTable: typeof import('element-plus/es')['ElTable'] 42 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] 43 | ElTooltip: typeof import('element-plus/es')['ElTooltip'] 44 | ElUpload: typeof import('element-plus/es')['ElUpload'] 45 | Loading: typeof import('element-plus/es')['ElLoadingDirective'] 46 | Log: typeof import('./src/components/Log.vue')['default'] 47 | Login: typeof import('./src/components/Login.vue')['default'] 48 | Pmenu: typeof import('./src/components/Pmenu.vue')['default'] 49 | PortForward: typeof import('./src/components/PortForward.vue')['default'] 50 | PortForwardSet: typeof import('./src/components/PortForwardSet.vue')['default'] 51 | PSet: typeof import('./src/components/PSet.vue')['default'] 52 | ReverseProxy: typeof import('./src/components/ReverseProxy.vue')['default'] 53 | SSL: typeof import('./src/components/SSL.vue')['default'] 54 | Status: typeof import('./src/components/Status.vue')['default'] 55 | WhiteLists: typeof import('./src/components/WhiteLists.vue')['default'] 56 | WhiteListSet: typeof import('./src/components/WhiteListSet.vue')['default'] 57 | WOL: typeof import('./src/components/tools/WOL.vue')['default'] 58 | } 59 | } 60 | 61 | export {} 62 | -------------------------------------------------------------------------------- /web/adminviews/dist/diandeng_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/web/adminviews/dist/diandeng_log.png -------------------------------------------------------------------------------- /web/adminviews/dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/web/adminviews/dist/favicon.ico -------------------------------------------------------------------------------- /web/adminviews/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Lucky 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /web/adminviews/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Lucky 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web/adminviews/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lucky-adminviews", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview --port 4173" 8 | }, 9 | "dependencies": { 10 | "@element-plus/icons-vue": "^2.0.4", 11 | "axios": "^0.27.2", 12 | "element-plus": "^2.2.2", 13 | "file-saver": "^2.0.5", 14 | "vue": "^3.2.36", 15 | "vue-clipboard3": "^2.0.0" 16 | }, 17 | "devDependencies": { 18 | "@vitejs/plugin-vue": "^2.3.3", 19 | "@vue/cli-plugin-typescript": "~5.0.0", 20 | "babel-plugin-import": "^1.13.5", 21 | "sass": "^1.52.1", 22 | "unplugin-auto-import": "^0.8.6", 23 | "unplugin-vue-components": "^0.19.6", 24 | "vite": "^2.9.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/adminviews/public/diandeng_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/web/adminviews/public/diandeng_log.png -------------------------------------------------------------------------------- /web/adminviews/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdy666/lucky/247a744082c7b85e946eff777455356d0321a086/web/adminviews/public/favicon.ico -------------------------------------------------------------------------------- /web/adminviews/src/App.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | 93 | 94 | 95 | 96 | 97 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /web/adminviews/src/apis/storage.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 使用方式 4 | * * * main.js挂载全局 5 | * import storage from './utils/storage'; 6 | const app = createApp(App); 7 | app.config.globalProperties.$storage = storage; 8 | * * * vue3.0 全局使用 vue实例上的数据 9 | * import { defineComponent, reactive, getCurrentInstance } from "vue"; 10 | * let { appContext } = getCurrentInstance(); 11 | appContext.config.globalProperties.$storage.setItem("username","aaa"); 12 | appContext.config.globalProperties.$storage.setItem("age",20); 13 | appContext.config.globalProperties.$storage.clearItem("age"); 14 | appContext.config.globalProperties.$storage.clearAll(); 15 | */ 16 | export default { 17 | getStorage () { // 先获取该项目的 命名存储空间 下的storage数据 maneger 18 | return JSON.parse(window.localStorage.getItem("lucky") || "{}"); 19 | }, 20 | setItem (key, val) { 21 | let storage = this.getStorage() 22 | // console.log("setItem", storage); 23 | storage[key] = val; // 为当前对象添加 需要存储的值 24 | window.localStorage.setItem("lucky", JSON.stringify(storage)) // 保存到本地 25 | }, 26 | getItem (key) { 27 | return this.getStorage()[key] 28 | }, 29 | // 清空 当前的项目下命名存储的空间 该key项的 Storage 数据 30 | clearItem (key) { 31 | let storage = this.getStorage() 32 | delete storage[key] 33 | window.localStorage.setItem(config.namespace, JSON.stringify(storage)) // 保存到本地 34 | }, 35 | // 清空所有的 当前的项目下命名存储的空间 Storage 数据 36 | clearAll () { 37 | window.localStorage.clear(); 38 | } 39 | } -------------------------------------------------------------------------------- /web/adminviews/src/assets/appbase.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PageRadius { 4 | height: 90vh; 5 | width: 100%; 6 | max-width: 1300px; 7 | border: 1px solid var(--el-border-color); 8 | border-radius: 0; 9 | margin: 20px 10 | } -------------------------------------------------------------------------------- /web/adminviews/src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /web/adminviews/src/assets/common-layout.scss: -------------------------------------------------------------------------------- 1 | .common-layout { 2 | .el-header, 3 | .el-footer, 4 | .el-main, 5 | .el-aside { 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | } 10 | 11 | .el-header, 12 | .el-footer { 13 | background-color: var(--el-color-primary-light-7); 14 | color: var(--el-text-color-primary); 15 | text-align: center; 16 | } 17 | 18 | .el-aside { 19 | background-color: var(--el-color-primary-light-8); 20 | color: var(--el-text-color-primary); 21 | text-align: center; 22 | } 23 | 24 | .el-main { 25 | background-color: var(--el-color-primary-light-9); 26 | color: var(--el-text-color-primary); 27 | text-align: center; 28 | 29 | height: 150px; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/adminviews/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/adminviews/src/components/About.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 50 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /web/adminviews/src/components/Log.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 87 | 88 | 117 | 118 | -------------------------------------------------------------------------------- /web/adminviews/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 128 | 129 | -------------------------------------------------------------------------------- /web/adminviews/src/components/Status.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 37 | 38 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /web/adminviews/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp,ref } from 'vue' 2 | import App from './App.vue' 3 | import './assets/common-layout.scss' 4 | import './assets/appbase.css' 5 | import * as ElIcon from '@element-plus/icons-vue' 6 | import 'element-plus/theme-chalk/el-notification.css' 7 | import 'element-plus/theme-chalk/el-menu.css' 8 | import 'element-plus/theme-chalk/el-loading.css' 9 | import 'element-plus/theme-chalk/el-message.css' 10 | import 'element-plus/theme-chalk/el-message-box.css' 11 | import 'element-plus/theme-chalk/el-button.css' 12 | import storage from './apis/storage.js' 13 | import {apiLogout} from './apis/utils.js' 14 | import {PageExist,CurrentPage} from './utils/utils' 15 | 16 | 17 | const app = createApp(App) 18 | for (let iconName in ElIcon){ 19 | app.component(iconName, ElIcon[iconName]) 20 | } 21 | 22 | 23 | app.config.globalProperties.$storage = storage; 24 | 25 | 26 | 27 | if(!PageExist(location.hash)){ 28 | location.hash="#status" 29 | } 30 | 31 | //配置全局变量 32 | //默认页面 33 | var currentPage = ref(location.hash) 34 | 35 | 36 | 37 | // if (process.env.NODE_ENV=="development"){ 38 | // currentPage.value="#relayset" 39 | // location.hash="#relayset" 40 | // } 41 | 42 | app.provide('global',{ 43 | currentPage, 44 | storage, 45 | }) 46 | 47 | window.onpopstate = function (event){ 48 | currentPage.value=location.hash 49 | CurrentPage.value = location.hash 50 | 51 | if(location.hash == "#logout"){//注销登录 52 | apiLogout().then((res) => { 53 | }).catch((error) => { 54 | }) 55 | storage.setItem("token","") 56 | location.hash ="#login" 57 | return 58 | } 59 | 60 | if(!PageExist(location.hash)){ 61 | console.log("location.hash["+location.hash +"]no exist") 62 | location.hash ="#login" 63 | return 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | 71 | app.mount('#app') 72 | 73 | -------------------------------------------------------------------------------- /web/adminviews/src/request/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | console.log("vue run mode "+process.env.NODE_ENV) 4 | 5 | var baseURL = "/" // 6 | if (process.env.NODE_ENV=="development"){ 7 | //开发环境下这个改为自己的接口地址 8 | baseURL = 'http://192.168.31.70:16601' 9 | } 10 | 11 | 12 | //var fuck = storage.getItem("cookies") 13 | //console.log("fuck:"+fuck) 14 | 15 | //console.log("baseURL: "+ baseURL) 16 | 17 | // 创建一个 axios 实例 18 | const service = axios.create({ 19 | baseURL: baseURL, // 所有的请求地址前缀部分 20 | timeout: 5000, // 请求超时时间毫秒 21 | withCredentials: false, // 异步请求携带cookie 22 | headers: { 23 | // 设置后端需要的传参类型 24 | 'Content-Type': 'application/json', 25 | //'X-Requested-With': 'XMLHttpRequest', 26 | }, 27 | }) 28 | 29 | // 添加请求拦截器 30 | service.interceptors.request.use( 31 | function (config) { 32 | // 在发送请求之前做些什么 33 | return config 34 | }, 35 | function (error) { 36 | // 对请求错误做些什么 37 | console.log(error) 38 | return Promise.reject(error) 39 | } 40 | ) 41 | 42 | // 添加响应拦截器 43 | service.interceptors.response.use( 44 | function (response) { 45 | //console.log(response) 46 | // 2xx 范围内的状态码都会触发该函数。 47 | // 对响应数据做点什么 48 | // dataAxios 是 axios 返回数据中的 data 49 | const dataAxios = response.data 50 | // 这个状态码是和后端约定的 51 | const code = dataAxios.reset 52 | 53 | //console.log("dataAxios data: "+JSON.stringify(dataAxios)) 54 | //console.log("ret: "+dataAxios.ret) 55 | if (dataAxios.ret!=undefined&& dataAxios.ret==-1){ 56 | //global.currentPage.value="set" 57 | console.log("登录失效") 58 | //window.location.href="/" 59 | location.hash ="#login" 60 | //var currentPage = ref("#login") 61 | 62 | } 63 | return dataAxios 64 | }, 65 | function (error) { 66 | // 超出 2xx 范围的状态码都会触发该函数。 67 | // 对响应错误做点什么 68 | console.log(error) 69 | return Promise.reject(error) 70 | } 71 | ) 72 | 73 | export default service 74 | -------------------------------------------------------------------------------- /web/adminviews/src/utils/ui.ts: -------------------------------------------------------------------------------- 1 | import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' 2 | 3 | 4 | // ElMessageBox.alert(message, { 5 | // confirmButtonText: '好的', 6 | // callback: () => { 7 | // }, 8 | // }) 9 | 10 | 11 | export function ShowMessageBox(message: string) { 12 | ElMessageBox.alert(message, { 13 | confirmButtonText: '好的', 14 | callback: () => { 15 | }, 16 | }) 17 | } 18 | 19 | export function MessageShow(type:any,message: string) { 20 | ElMessage({ 21 | message: message, 22 | type: type, 23 | }) 24 | } 25 | 26 | 27 | export function Notification (type, message, duration) { 28 | ElNotification({ 29 | title: type.substring(0, 1).toUpperCase() + type.substring(1), 30 | message: message, 31 | type: type, 32 | duration: duration, 33 | }) 34 | } -------------------------------------------------------------------------------- /web/adminviews/src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import useClipboard from 'vue-clipboard3' 2 | import { ref } from 'vue' 3 | 4 | export function CopyTotoClipboard(data: string) { 5 | useClipboard().toClipboard(data) 6 | } 7 | 8 | const ipReg = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^::([\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){2}:([\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){4}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$|^:((:[\da-fA-F]{1,4}){1,6}|:)$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?$|^([\da-fA-F]{1,4}:){6}:$/ 9 | 10 | export function isIP(ip :string){ 11 | return ipReg.test(ip) 12 | } 13 | 14 | const MenuIndexList = ["#status", 15 | "#log","#whitelistset", 16 | "#whitelists","#blacklists","#set", 17 | "#login","#ddns","#ddnstasklist","#ddnsset", 18 | "#about","#reverseproxylist","#ssl","#portforward","#portforwardset","#wol"] 19 | 20 | export function PageExist(page:string) { 21 | for(let i in MenuIndexList){ 22 | if (MenuIndexList[i]==page){ 23 | return true 24 | } 25 | } 26 | return false 27 | } 28 | 29 | export const CurrentPage = ref("") 30 | 31 | 32 | export function StringToArrayList(str : string){ 33 | let rawlist = str.split("\n") 34 | let resList = new Array() 35 | for (let i in rawlist) { 36 | let item = rawlist[i].replace(/^\s+|\s+$/g, '').replace(/<\/?.+?>/g, "").replace(/[\r\n]/g, "") 37 | if (item==""){ 38 | continue 39 | } 40 | resList.push(item) 41 | } 42 | return resList 43 | } 44 | 45 | export function StrArrayListToBrHtml( strList : string[]){ 46 | var resHtml = "" 47 | for ( let i in strList){ 48 | resHtml += strList[i] + '
' 49 | } 50 | return resHtml 51 | } 52 | 53 | export function StrArrayListToArea(strList : string[]){ 54 | var res = "" 55 | for ( let i in strList){ 56 | if(i!="0"){ 57 | res +='\n' 58 | } 59 | res += strList[i] 60 | // res += strList[i] + '\n' 61 | } 62 | return res 63 | } 64 | 65 | export const LogLevelList = [ 66 | { 67 | value: 2, 68 | label: 'Error', 69 | }, 70 | { 71 | value: 3, 72 | label: 'Warn', 73 | }, 74 | { 75 | value: 4, 76 | label: 'Info', 77 | }, 78 | { 79 | value: 5, 80 | label: 'Debug', 81 | }, 82 | { 83 | value: 6, 84 | label: 'Trace', 85 | }, 86 | ] 87 | 88 | export const bytesToSize = (bytes) => { 89 | if (bytes === 0) return '0 B'; 90 | var k = 1000, // or 1024 91 | sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 92 | i = Math.floor(Math.log(bytes) / Math.log(k)); 93 | 94 | return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]; 95 | } -------------------------------------------------------------------------------- /web/adminviews/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | import AutoImport from 'unplugin-auto-import/vite' 6 | import Components from 'unplugin-vue-components/vite' 7 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 8 | 9 | // https://vitejs.dev/config/ 10 | export default defineConfig({ 11 | plugins: [ 12 | vue(), 13 | AutoImport({ 14 | resolvers: [ElementPlusResolver()], 15 | }), 16 | Components({ 17 | resolvers: [ElementPlusResolver()], 18 | })], 19 | resolve: { 20 | alias: { 21 | '@': fileURLToPath(new URL('./src', import.meta.url)) 22 | } 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /web/blackwhitelist.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/gdy666/lucky/config" 10 | "github.com/gdy666/lucky/thirdlib/gdylib/ginutils" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func whitelistConfigure(c *gin.Context) { 15 | c.JSON(http.StatusOK, gin.H{"ret": 0, "data": config.GetWhiteListBaseConfigure()}) 16 | } 17 | 18 | func alterWhitelistConfigure(c *gin.Context) { 19 | var requestObj config.WhiteListBaseConfigure 20 | err := c.BindJSON(&requestObj) 21 | if err != nil { 22 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "修改请求解析出错"}) 23 | return 24 | } 25 | 26 | requestObj.BasicAccount = strings.TrimSpace(requestObj.BasicAccount) 27 | if len(requestObj.BasicAccount) == 0 || len(requestObj.BasicPassword) == 0 { 28 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "账号或密码不能为空"}) 29 | return 30 | } 31 | 32 | err = config.SetWhiteListBaseConfigure(requestObj.ActivelifeDuration, requestObj.URL, requestObj.BasicAccount, requestObj.BasicPassword) 33 | 34 | if err != nil { 35 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "保存白名单配置出错"}) 36 | return 37 | } 38 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 39 | } 40 | 41 | func querywhitelist(c *gin.Context) { 42 | resList := config.GetWhiteList() 43 | c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList}) 44 | } 45 | 46 | func deleteblacklist(c *gin.Context) { 47 | ip := c.Query("ip") 48 | err := config.BlackListDelete(ip) 49 | if err != nil { 50 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除黑名单指定IP出错"}) 51 | return 52 | } 53 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 54 | } 55 | 56 | func deletewhitelist(c *gin.Context) { 57 | ip := c.Query("ip") 58 | err := config.WhiteListDelete(ip) 59 | if err != nil { 60 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除白名单指定IP出错"}) 61 | return 62 | } 63 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 64 | } 65 | 66 | func flushblacklist(c *gin.Context) { 67 | ip := c.Query("ip") 68 | activelifeDurationStr := c.Query("life") 69 | life, _ := strconv.Atoi(activelifeDurationStr) 70 | 71 | newTime, err := config.BlackListAdd(ip, int32(life)) 72 | if err != nil { 73 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("刷新IP有效期出错:%s", err.Error())}) 74 | return 75 | } 76 | c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime}) 77 | } 78 | 79 | func flushwhitelist(c *gin.Context) { 80 | ip := c.Query("ip") 81 | activelifeDurationStr := c.Query("life") 82 | life, _ := strconv.Atoi(activelifeDurationStr) 83 | 84 | newTime, err := config.WhiteListAdd(ip, int32(life)) 85 | if err != nil { 86 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("刷新IP有效期出错:%s", err.Error())}) 87 | return 88 | } 89 | c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime}) 90 | } 91 | 92 | func queryblacklist(c *gin.Context) { 93 | resList := config.GetBlackList() 94 | c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList}) 95 | } 96 | 97 | func whitelistBasicAuth(c *gin.Context) { 98 | bc := config.GetWhiteListBaseConfigure() 99 | whilelistURL := c.Param("url") 100 | if (c.Request.RequestURI == "/wl" && bc.URL != "") || whilelistURL != bc.URL { 101 | c.AbortWithStatus(http.StatusNotFound) 102 | return 103 | } 104 | 105 | realm := "Basic realm=" + strconv.Quote("Authorization Required") 106 | pairs := ginutils.ProcessAccounts(gin.Accounts{bc.BasicAccount: bc.BasicPassword}) 107 | user, found := pairs.SearchCredential(c.GetHeader("Authorization")) 108 | if !found { 109 | // Credentials doesn't match, we return 401 and abort handlers chain. 110 | c.Header("WWW-Authenticate", realm) 111 | c.AbortWithStatus(http.StatusUnauthorized) 112 | return 113 | } 114 | c.Set("user", user) 115 | } 116 | 117 | func whilelistAdd(c *gin.Context) { 118 | 119 | lifeTime, err := config.WhiteListAdd(c.ClientIP(), 0) 120 | if err != nil { 121 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "记录白名单IP出错"}) 122 | return 123 | } 124 | 125 | c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "IP已记录进白名单", "ip": c.ClientIP(), " effective_time": lifeTime}) 126 | } 127 | -------------------------------------------------------------------------------- /web/common.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func getFileBase64(c *gin.Context) { 13 | file, err := c.FormFile("file") 14 | if err != nil { 15 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("c.FormFile err:%s", err.Error())}) 16 | return 17 | } 18 | 19 | src, err := file.Open() 20 | if err != nil { 21 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("file.Open err:%s", err.Error())}) 22 | return 23 | } 24 | defer src.Close() 25 | 26 | fileBytes, err := io.ReadAll(src) 27 | if err != nil { 28 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("ioutil.ReadAll err:%s", err.Error())}) 29 | return 30 | } 31 | 32 | fileBytesBase64Str := base64.StdEncoding.EncodeToString(fileBytes) 33 | 34 | c.JSON(http.StatusOK, gin.H{"ret": 0, "file": file.Filename, "base64": fileBytesBase64Str}) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /web/configure.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "sync" 10 | "time" 11 | 12 | "github.com/gdy666/lucky/config" 13 | "github.com/gdy666/lucky/thirdlib/gdylib/fileutils" 14 | "github.com/gdy666/lucky/thirdlib/gdylib/stringsp" 15 | "github.com/gin-gonic/gin" 16 | ) 17 | 18 | func configure(c *gin.Context) { 19 | //c config.GetConfig() 20 | configureBytes := config.GetConfigureBytes() 21 | //c.Header("Content-Type", "application/json") 22 | 23 | //c.Data(http.StatusOK, "application/json", configureBytes) 24 | c.JSON(http.StatusOK, 25 | gin.H{ 26 | "ret": 0, 27 | "time": time.Now().Format("060102150405"), 28 | "configure": string(configureBytes)}, 29 | ) 30 | } 31 | 32 | var restoreConfigureVar *config.ProgramConfigure 33 | var restoreConfigureKey string 34 | var restoreConfigureMutex sync.Mutex 35 | 36 | func restoreConfigureConfirm(c *gin.Context) { 37 | restoreConfigureMutex.Lock() 38 | defer restoreConfigureMutex.Unlock() 39 | key := c.Query("key") 40 | if key != restoreConfigureKey { 41 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "key不一致"}) 42 | return 43 | } 44 | 45 | err := config.SetConfig(restoreConfigureVar) 46 | if err != nil { 47 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "还原配置出错"}) 48 | return 49 | } 50 | 51 | rebootOnce.Do(func() { 52 | go func() { 53 | fileutils.OpenProgramOrFile(os.Args) 54 | os.Exit(0) 55 | }() 56 | }) 57 | 58 | c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "还原配置成功", "port": restoreConfigureVar.BaseConfigure.AdminWebListenPort}) 59 | 60 | } 61 | 62 | func restoreConfigure(c *gin.Context) { 63 | file, err := c.FormFile("file") 64 | if err != nil { 65 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("c.FormFile err:%s", err.Error())}) 66 | return 67 | } 68 | 69 | src, err := file.Open() 70 | if err != nil { 71 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("file.Open err:%s", err.Error())}) 72 | return 73 | } 74 | defer src.Close() 75 | 76 | fileBytes, err := io.ReadAll(src) 77 | if err != nil { 78 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("ioutil.ReadAll err:%s", err.Error())}) 79 | return 80 | } 81 | //log.Printf("file:%s\n", string(fileBytes)) 82 | 83 | var conf config.ProgramConfigure 84 | 85 | err = json.Unmarshal(fileBytes, &conf) 86 | if err != nil { 87 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("配置文件[%s]有误", file.Filename)}) 88 | return 89 | } 90 | 91 | if conf.BaseConfigure.AdminAccount == "" || 92 | conf.BaseConfigure.AdminPassword == "" || 93 | conf.BaseConfigure.AdminWebListenPort <= 0 || 94 | conf.BaseConfigure.AdminWebListenPort >= 65536 { 95 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("配置文件[%s]参数有误", file.Filename)}) 96 | return 97 | } 98 | 99 | restoreConfigureMutex.Lock() 100 | defer restoreConfigureMutex.Unlock() 101 | restoreConfigureVar = &conf 102 | restoreConfigureKey = stringsp.GetRandomStringNum(16) 103 | 104 | c.JSON(http.StatusOK, gin.H{"ret": 0, "file": file.Filename, "restoreConfigureKey": restoreConfigureKey}) 105 | 106 | } 107 | -------------------------------------------------------------------------------- /web/ssl.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gdy666/lucky/config" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func addSSL(c *gin.Context) { 13 | var requestObj config.SSLCertficate 14 | err := c.BindJSON(&requestObj) 15 | if err != nil { 16 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"}) 17 | return 18 | } 19 | 20 | err = requestObj.Init() 21 | if err != nil { 22 | log.Printf("addSSL requestObj.Init() error:%s", err.Error()) 23 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "证书和Key有误!"}) 24 | return 25 | } 26 | 27 | //fmt.Printf("CertsInfo:%v\n", *requestObj.CertsInfo) 28 | err = config.SSLCertficateListAdd(&requestObj) 29 | if err != nil { 30 | log.Printf("config.SSLCertficateListAdd error:%s", err.Error()) 31 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加SSL证书出错!:%s", err.Error())}) 32 | return 33 | } 34 | 35 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 36 | 37 | } 38 | 39 | type sslResInfo struct { 40 | Key string `json:"Key"` 41 | Remark string `json:"Remark"` 42 | Enable bool `json:"Enable"` 43 | AddTime string `json:"AddTime"` 44 | CertsInfo *[]config.CertInfo `json:"CertsInfo"` 45 | } 46 | 47 | func getSSLCertficateList(c *gin.Context) { 48 | rawList := config.GetSSLCertficateList() 49 | var res []sslResInfo 50 | for i := range rawList { 51 | info := sslResInfo{ 52 | Key: rawList[i].Key, 53 | Remark: rawList[i].Remark, 54 | Enable: rawList[i].Enable, 55 | AddTime: rawList[i].AddTime, 56 | CertsInfo: rawList[i].CertsInfo, 57 | } 58 | 59 | res = append(res, info) 60 | } 61 | c.JSON(http.StatusOK, gin.H{"ret": 0, "list": res}) 62 | } 63 | 64 | func alterSSLCertficate(c *gin.Context) { 65 | key := c.Query("key") 66 | field := c.Query("field") 67 | value := c.Query("value") 68 | var err error 69 | switch field { 70 | case "enable": 71 | { 72 | enable := false 73 | 74 | if value == "true" { 75 | enable = true 76 | } 77 | err = config.SSLCertficateEnable(key, enable) 78 | } 79 | case "remark": 80 | { 81 | err = config.SSLCertficateAlterRemark(key, value) 82 | } 83 | default: 84 | err = fmt.Errorf("不支持修改的字段:%s", field) 85 | } 86 | 87 | if err != nil { 88 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()}) 89 | return 90 | } 91 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 92 | } 93 | 94 | func deleteSSLCertficate(c *gin.Context) { 95 | key := c.Query("key") 96 | err := config.SSLCertficateListDelete(key) 97 | if err != nil { 98 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()}) 99 | return 100 | } 101 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 102 | } 103 | -------------------------------------------------------------------------------- /web/wol.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gdy666/lucky/config" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func addWOLDevice(c *gin.Context) { 13 | var requestObj config.WOLDevice 14 | err := c.BindJSON(&requestObj) 15 | if err != nil { 16 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"}) 17 | return 18 | } 19 | 20 | err = checkWolDevice(&requestObj) 21 | if err != nil { 22 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加网络唤醒设备出错:%s", err.Error())}) 23 | return 24 | } 25 | 26 | err = config.WOLDeviceListAdd(&requestObj) 27 | if err != nil { 28 | log.Printf("config.WOLDeviceListAdd error:%s", err.Error()) 29 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加网络唤醒设备出错!:%s", err.Error())}) 30 | return 31 | } 32 | 33 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 34 | } 35 | 36 | func getWOLDeviceList(c *gin.Context) { 37 | list := config.GetWOLDeviceList() 38 | c.JSON(http.StatusOK, gin.H{"ret": 0, "list": list}) 39 | } 40 | 41 | func alterWOLDevice(c *gin.Context) { 42 | var requestObj config.WOLDevice 43 | err := c.BindJSON(&requestObj) 44 | if err != nil { 45 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"}) 46 | return 47 | } 48 | 49 | err = checkWolDevice(&requestObj) 50 | if err != nil { 51 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("修改网络唤醒设备出错:%s", err.Error())}) 52 | return 53 | } 54 | 55 | err = config.WOLDeviceListAlter(&requestObj) 56 | if err != nil { 57 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改网络唤醒设备配置失败:%s", err.Error())}) 58 | return 59 | } 60 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 61 | } 62 | 63 | func deleteWOLDevice(c *gin.Context) { 64 | key := c.Query("key") 65 | err := config.WOLDeviceListDelete(key) 66 | if err != nil { 67 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除网络唤醒设备失败:%s", err.Error())}) 68 | return 69 | } 70 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 71 | } 72 | 73 | func WOLDeviceWakeUp(c *gin.Context) { 74 | key := c.Query("key") 75 | 76 | device := config.GetWOLDeviceByKey(key) 77 | if device == nil { 78 | c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("找不到Key对应的设备,唤醒失败")}) 79 | return 80 | } 81 | err := device.WakeUp() 82 | if err != nil { 83 | c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "唤醒失败"}) 84 | return 85 | } 86 | c.JSON(http.StatusOK, gin.H{"ret": 0}) 87 | } 88 | 89 | func checkWolDevice(d *config.WOLDevice) error { 90 | 91 | // if strings.TrimSpace(d.DeviceName) == "" { 92 | // return fmt.Errorf("设备名称不能为空") 93 | // } 94 | 95 | if d.Port <= 0 || d.Port > 065535 { 96 | d.Port = 9 97 | } 98 | 99 | if d.Repeat <= 0 || d.Repeat > 10 { 100 | d.Repeat = 5 101 | } 102 | return nil 103 | } 104 | --------------------------------------------------------------------------------