globallimit.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. package globallimit
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  7. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  8. "github.com/go-nunu/nunu-layout-advanced/internal/repository"
  9. flexCdn2 "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/flexCdn"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
  11. "github.com/go-nunu/nunu-layout-advanced/internal/service"
  12. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn"
  13. waf2 "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf"
  14. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/common"
  15. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/tcp"
  16. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/udp"
  17. "github.com/go-nunu/nunu-layout-advanced/internal/service/api/waf/web"
  18. "github.com/mozillazg/go-pinyin"
  19. "github.com/spf13/viper"
  20. "golang.org/x/sync/errgroup"
  21. "gorm.io/gorm"
  22. "strconv"
  23. "strings"
  24. "time"
  25. )
  26. type GlobalLimitService interface {
  27. GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error)
  28. AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  29. EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  30. DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  31. GlobalLimitRequire(ctx context.Context, req v1.GlobalLimitRequest) (res v1.GlobalLimitRequireResponse, err error)
  32. }
  33. func NewGlobalLimitService(
  34. service *service.Service,
  35. globalLimitRepository waf.GlobalLimitRepository,
  36. duedate service.DuedateService,
  37. crawler service.CrawlerService,
  38. conf *viper.Viper,
  39. required service.RequiredService,
  40. parser service.ParserService,
  41. host service.HostService,
  42. hostRep repository.HostRepository,
  43. cdnService flexCdn.CdnService,
  44. cdnRep flexCdn2.CdnRepository,
  45. tcpforwardingRep waf.TcpforwardingRepository,
  46. udpForWardingRep waf.UdpForWardingRepository,
  47. webForWardingRep waf.WebForwardingRepository,
  48. allowAndDeny common.AllowAndDenyIpService,
  49. allowAndDenyRep waf.AllowAndDenyIpRepository,
  50. tcpforwarding tcp.TcpforwardingService,
  51. udpForWarding udp.UdpForWardingService,
  52. webForWarding web.WebForwardingService,
  53. gatewayIpRep waf.GatewayipRepository,
  54. gatywayIp common.GatewayipService,
  55. bulidAudun waf2.BuildAudunService,
  56. zzyBgp waf2.ZzybgpService,
  57. ) GlobalLimitService {
  58. return &globalLimitService{
  59. Service: service,
  60. globalLimitRepository: globalLimitRepository,
  61. duedate: duedate,
  62. crawler: crawler,
  63. Url: conf.GetString("crawler.Url"),
  64. required: required,
  65. parser: parser,
  66. host: host,
  67. hostRep: hostRep,
  68. cdnService: cdnService,
  69. cdnRep: cdnRep,
  70. tcpforwardingRep: tcpforwardingRep,
  71. udpForWardingRep: udpForWardingRep,
  72. webForWardingRep: webForWardingRep,
  73. allowAndDeny: allowAndDeny,
  74. allowAndDenyRep: allowAndDenyRep,
  75. tcpforwarding: tcpforwarding,
  76. udpForWarding: udpForWarding,
  77. webForWarding: webForWarding,
  78. gatewayIpRep: gatewayIpRep,
  79. gatewayIp: gatywayIp,
  80. bulidAudun: bulidAudun,
  81. zzyBgp: zzyBgp,
  82. }
  83. }
  84. type globalLimitService struct {
  85. *service.Service
  86. globalLimitRepository waf.GlobalLimitRepository
  87. duedate service.DuedateService
  88. crawler service.CrawlerService
  89. Url string
  90. required service.RequiredService
  91. parser service.ParserService
  92. host service.HostService
  93. hostRep repository.HostRepository
  94. cdnService flexCdn.CdnService
  95. cdnRep flexCdn2.CdnRepository
  96. tcpforwardingRep waf.TcpforwardingRepository
  97. udpForWardingRep waf.UdpForWardingRepository
  98. webForWardingRep waf.WebForwardingRepository
  99. allowAndDeny common.AllowAndDenyIpService
  100. allowAndDenyRep waf.AllowAndDenyIpRepository
  101. tcpforwarding tcp.TcpforwardingService
  102. udpForWarding udp.UdpForWardingService
  103. webForWarding web.WebForwardingService
  104. gatewayIpRep waf.GatewayipRepository
  105. gatewayIp common.GatewayipService
  106. bulidAudun waf2.BuildAudunService
  107. zzyBgp waf2.ZzybgpService
  108. }
  109. func (s *globalLimitService) GetCdnUserId(ctx context.Context, uid int64) (int64, error) {
  110. data, err := s.globalLimitRepository.GetGlobalLimitFirst(ctx, uid)
  111. if err != nil {
  112. if !errors.Is(err, gorm.ErrRecordNotFound) {
  113. return 0, err
  114. }
  115. }
  116. if data != nil && data.CdnUid != 0 {
  117. return int64(data.CdnUid), nil
  118. }
  119. userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, uid)
  120. if err != nil {
  121. return 0, err
  122. }
  123. // 中文转拼音
  124. a := pinyin.NewArgs()
  125. a.Style = pinyin.Normal
  126. pinyinSlice := pinyin.LazyPinyin(userInfo.Username, a)
  127. userName := strconv.Itoa(int(uid)) + "_" + strings.Join(pinyinSlice, "_")
  128. // 查询用户是否存在
  129. UserId,err := s.cdnRep.GetUserId(ctx, userName)
  130. if err != nil {
  131. return 0, err
  132. }
  133. if UserId != 0 {
  134. return UserId, nil
  135. }
  136. // 注册用户
  137. userId, err := s.cdnService.AddUser(ctx, v1.User{
  138. Username: userName,
  139. Email: userInfo.Email,
  140. Fullname: userInfo.Username,
  141. Mobile: userInfo.PhoneNumber,
  142. })
  143. if err != nil {
  144. return 0, err
  145. }
  146. return userId, nil
  147. }
  148. func (s *globalLimitService) AddGroupId(ctx context.Context,groupName string) (int64, error) {
  149. groupId, err := s.cdnService.CreateGroup(ctx, v1.Group{
  150. Name: groupName,
  151. })
  152. if err != nil {
  153. return 0, err
  154. }
  155. return groupId, nil
  156. }
  157. func (s *globalLimitService) GlobalLimitRequire(ctx context.Context, req v1.GlobalLimitRequest) (res v1.GlobalLimitRequireResponse, err error) {
  158. res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
  159. if err != nil {
  160. return v1.GlobalLimitRequireResponse{}, err
  161. }
  162. configCount, err := s.host.GetGlobalLimitConfig(ctx, req.HostId)
  163. if err != nil {
  164. return v1.GlobalLimitRequireResponse{}, fmt.Errorf("获取配置限制失败: %w", err)
  165. }
  166. res.MaxBytesMonth = configCount.MaxBytesMonth
  167. res.Operator = configCount.Operator
  168. res.IpCount = configCount.IpCount
  169. res.NodeArea = configCount.NodeArea
  170. res.ConfigMaxProtection = configCount.ConfigMaxProtection
  171. res.IsBanUdp = configCount.IsBanUdp
  172. res.HostId = req.HostId
  173. res.Bps = configCount.Bps
  174. domain, err := s.hostRep.GetDomainById(ctx, req.HostId)
  175. if err != nil {
  176. return v1.GlobalLimitRequireResponse{}, err
  177. }
  178. userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, int64(req.Uid))
  179. if err != nil {
  180. return v1.GlobalLimitRequireResponse{}, err
  181. }
  182. res.GlobalLimitName = strconv.Itoa(req.Uid) + "_" + userInfo.Username + "_" + strconv.Itoa(req.HostId) + "_" + domain
  183. res.HostName, err = s.globalLimitRepository.GetHostName(ctx, int64(req.HostId))
  184. if err != nil {
  185. return v1.GlobalLimitRequireResponse{}, err
  186. }
  187. return res, nil
  188. }
  189. func (s *globalLimitService) GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error) {
  190. return s.globalLimitRepository.GetGlobalLimit(ctx, id)
  191. }
  192. func (s *globalLimitService) ConversionTime(ctx context.Context,req string) (string, error) {
  193. // 2. 将字符串解析成 time.Time 对象
  194. // time.Parse 会根据你提供的布局来理解输入的字符串
  195. t, err := time.Parse("2006-01-02 15:04:05", req)
  196. if err != nil {
  197. // 如果输入的字符串格式和布局不匹配,这里会报错
  198. return "", fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
  199. }
  200. // 3. 定义新的输出格式 "YYYY-MM-DD"
  201. outputLayout := "2006-01-02"
  202. // 4. 将 time.Time 对象格式化为新的字符串
  203. outputTimeStr := t.Format(outputLayout)
  204. return outputTimeStr, nil
  205. }
  206. func (s *globalLimitService) ConversionTimeUnix(ctx context.Context,req string) (int64, error) {
  207. t, err := time.Parse("2006-01-02 15:04:05", req)
  208. if err != nil {
  209. return 0, fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
  210. }
  211. expiredAt := t.Unix()
  212. return expiredAt, nil
  213. }
  214. func (s *globalLimitService) AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  215. isExist, err := s.globalLimitRepository.IsGlobalLimitExistByHostId(ctx, int64(req.HostId))
  216. if err != nil {
  217. return err
  218. }
  219. if isExist {
  220. return fmt.Errorf("配置限制已存在")
  221. }
  222. require, err := s.GlobalLimitRequire(ctx, req)
  223. if err != nil {
  224. return err
  225. }
  226. g, gCtx := errgroup.WithContext(ctx)
  227. var userId int64
  228. var groupId int64
  229. g.Go(func() error {
  230. e := s.gatewayIp.AddIpWhereHostIdNull(gCtx, int64(req.HostId),int64(req.Uid))
  231. if e != nil {
  232. return fmt.Errorf("获取网关组失败: %w", e)
  233. }
  234. return nil
  235. })
  236. g.Go(func() error {
  237. res, e := s.GetCdnUserId(gCtx, int64(req.Uid))
  238. if e != nil {
  239. return fmt.Errorf("获取cdn用户失败: %w", e)
  240. }
  241. if res == 0 {
  242. return fmt.Errorf("获取cdn用户失败")
  243. }
  244. userId = res
  245. return nil
  246. })
  247. g.Go(func() error {
  248. res, e := s.AddGroupId(gCtx, require.GlobalLimitName)
  249. if e != nil {
  250. return fmt.Errorf("创建规则分组失败: %w", e)
  251. }
  252. if res == 0 {
  253. return fmt.Errorf("创建规则分组失败")
  254. }
  255. groupId = res
  256. return nil
  257. })
  258. if err = g.Wait(); err != nil {
  259. return err
  260. }
  261. // 添加防护
  262. err = s.zzyBgp.SetDefense(ctx, int64(req.HostId), 0)
  263. if err != nil {
  264. return err
  265. }
  266. // 添加带宽限制
  267. err = s.bulidAudun.Bandwidth(ctx, int64(req.HostId), "add")
  268. if err != nil {
  269. return err
  270. }
  271. expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
  272. if err != nil {
  273. return err
  274. }
  275. // 如果存在实例,恢复
  276. oldData,err := s.globalLimitRepository.GetGlobalLimitByHostId(ctx, int64(req.HostId))
  277. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  278. return err
  279. }
  280. if oldData!= nil && oldData.Id != 0 {
  281. err = s.globalLimitRepository.UpdateGlobalLimitByHostId(ctx, &model.GlobalLimit{
  282. HostId: req.HostId,
  283. Uid: req.Uid,
  284. Name: require.GlobalLimitName,
  285. GroupId: int(groupId),
  286. CdnUid: int(userId),
  287. Comment: req.Comment,
  288. ExpiredAt: expiredAt,
  289. State: true,
  290. })
  291. if err != nil {
  292. return err
  293. }
  294. return nil
  295. }
  296. // 如果不存在实例,创建
  297. err = s.globalLimitRepository.AddGlobalLimit(ctx, &model.GlobalLimit{
  298. HostId: req.HostId,
  299. Uid: req.Uid,
  300. Name: require.GlobalLimitName,
  301. GroupId: int(groupId),
  302. CdnUid: int(userId),
  303. Comment: req.Comment,
  304. State: true,
  305. ExpiredAt: expiredAt,
  306. })
  307. if err != nil {
  308. return err
  309. }
  310. return nil
  311. }
  312. func (s *globalLimitService) EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  313. require, err := s.GlobalLimitRequire(ctx, req)
  314. if err != nil {
  315. return err
  316. }
  317. // 如果不存在实例,创建
  318. gatewayIp, err := s.gatewayIpRep.GetGatewayipByHostIdAll(ctx, int64(req.HostId))
  319. if err != nil {
  320. return err
  321. }
  322. if gatewayIp == nil {
  323. err = s.gatewayIp.AddIpWhereHostIdNull(ctx, int64(req.HostId), int64(req.Uid))
  324. if err != nil {
  325. return fmt.Errorf("获取网关组失败: %w", err)
  326. }
  327. }
  328. expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
  329. if err != nil {
  330. return err
  331. }
  332. if err := s.globalLimitRepository.UpdateGlobalLimitByHostId(ctx, &model.GlobalLimit{
  333. HostId: req.HostId,
  334. Comment: req.Comment,
  335. ExpiredAt: expiredAt,
  336. }); err != nil {
  337. return err
  338. }
  339. return nil
  340. }
  341. func (s *globalLimitService) DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  342. // 检查是否过期
  343. isExpired, err := s.host.CheckExpired(ctx, int64(req.Uid), int64(req.HostId))
  344. if err != nil {
  345. return err
  346. }
  347. if isExpired {
  348. return fmt.Errorf("实例未过期,无法删除")
  349. }
  350. oldData, err := s.globalLimitRepository.GetGlobalLimitByHostId(ctx, int64(req.HostId))
  351. if err != nil {
  352. if errors.Is(err, gorm.ErrRecordNotFound) {
  353. return fmt.Errorf("实例不存在")
  354. }
  355. return err
  356. }
  357. tcpIds, err := s.tcpforwardingRep.GetTcpForwardingAllIdsByID(ctx,req.HostId)
  358. if err != nil {
  359. return err
  360. }
  361. udpIds, err := s.udpForWardingRep.GetUdpForwardingWafUdpAllIds(ctx,req.HostId)
  362. if err != nil {
  363. return err
  364. }
  365. webIds, err := s.webForWardingRep.GetWebForwardingWafWebAllIds(ctx,req.HostId)
  366. if err != nil {
  367. return err
  368. }
  369. // 重置防护
  370. err = s.zzyBgp.SetDefense(ctx, int64(req.HostId), 10)
  371. if err != nil {
  372. return err
  373. }
  374. // 删除带宽限制
  375. err = s.bulidAudun.Bandwidth(ctx, int64(req.HostId), "del")
  376. if err != nil {
  377. return err
  378. }
  379. // 黑白IP
  380. BwIds, err := s.allowAndDenyRep.GetIpCountListId(ctx, int64(req.HostId))
  381. if err != nil {
  382. return err
  383. }
  384. // 删除网站
  385. g, gCtx := errgroup.WithContext(ctx)
  386. g.Go(func() error {
  387. e := s.tcpforwarding.DeleteTcpForwarding(ctx,v1.DeleteTcpForwardingRequest{
  388. Ids: tcpIds,
  389. Uid: req.Uid,
  390. HostId: req.HostId,
  391. })
  392. if e != nil {
  393. return fmt.Errorf("删除TCP转发失败: %w", e)
  394. }
  395. return nil
  396. })
  397. g.Go(func() error {
  398. e := s.udpForWarding.DeleteUdpForwarding(ctx, v1.DeleteUdpForwardingRequest{
  399. Ids: udpIds,
  400. Uid: req.Uid,
  401. HostId: req.HostId,
  402. })
  403. if e != nil {
  404. return fmt.Errorf("删除UDP转发失败: %w", e)
  405. }
  406. return nil
  407. })
  408. g.Go(func() error {
  409. e := s.webForWarding.DeleteWebForwarding(ctx,v1.DeleteWebForwardingRequest{
  410. Ids: webIds,
  411. Uid: req.Uid,
  412. HostId: req.HostId,
  413. })
  414. if e != nil {
  415. return fmt.Errorf("删除WEB转发失败: %w", e)
  416. }
  417. return nil
  418. })
  419. // 删除网站分组
  420. g.Go(func() error {
  421. e := s.cdnService.DelServerGroup(gCtx, int64(oldData.GroupId))
  422. if e != nil {
  423. return fmt.Errorf("删除网站分组失败: %w", e)
  424. }
  425. return nil
  426. })
  427. if err = g.Wait(); err != nil {
  428. return err
  429. }
  430. if err := s.globalLimitRepository.EditHostState(ctx, int64(req.HostId), false); err != nil {
  431. return err
  432. }
  433. if err := s.gatewayIpRep.CleanIPByHostId(ctx, []int64{int64(req.HostId)}); err != nil {
  434. return err
  435. }
  436. // 删除黑白名单
  437. err = s.allowAndDeny.DeleteAllowAndDenyIps(ctx, v1.DelAllowAndDenyIpRequest{
  438. HostId: req.HostId,
  439. Ids: BwIds,
  440. Uid: req.Uid,
  441. })
  442. if err != nil {
  443. return err
  444. }
  445. return nil
  446. }