package server import ( "be-vpn/internal/dto" "be-vpn/internal/model" "be-vpn/internal/storage" "github.com/gin-gonic/gin" "log" "math/rand" "net/http" "strconv" "sync" "time" ) var nodes = make([]*model.Node, 0) var locker = sync.RWMutex{} var totalFreeDuration = uint64((time.Hour * 1).Milliseconds() / 1000) func Config(c *gin.Context) { deviceId := c.Query("deviceId") usedDuration, err := storage.GetUsedDuration(deviceId) if err != nil { dto.Error(c, err) return } c.JSON(http.StatusOK, dto.ConfigResponse{ Response: dto.NewOkResponse(), Data: dto.ConfigResult{ FreeTrialDuration: totalFreeDuration - usedDuration, Timestamp: time.Now().Unix(), Node: convert2DtoNode(healthNodes()[0]), }, }) } func AddUsedDuration(c *gin.Context) { deviceId := c.Query("deviceId") usedDurationStr := c.Query("usedDuration") log.Printf("deviceId: %s, usedDuration: %s", deviceId, usedDurationStr) usedDuration, err := strconv.ParseUint(usedDurationStr, 10, 64) if err != nil { dto.Error(c, err) return } if existed, err := storage.AddUsedDuration(deviceId, usedDuration); err != nil { dto.Error(c, err) return } else { c.JSON(http.StatusOK, dto.ConfigResponse{ Response: dto.NewOkResponse(), Data: dto.ConfigResult{ FreeTrialDuration: totalFreeDuration - existed, Timestamp: time.Now().Unix(), Node: convert2DtoNode(healthNodes()[0]), }, }) } } func Register(c *gin.Context) { locker.Lock() defer locker.Unlock() var request dto.RegisterRequest if err := c.ShouldBindJSON(&request); err != nil { dto.BadRequest(c, err) return } for _, node := range nodes { if node.Ip == request.Ip { node.Ip = request.Ip node.Secret = request.Secret node.CountryCode = request.CountryCode node.CountryName = request.CountryName node.LastUpdateTime = time.Now() c.JSON(http.StatusOK, dto.RegisterResponse{ Response: dto.NewOkResponse(), Data: dto.RegisterResult{ Success: true, }, }) return } } node := &model.Node{ Ip: request.Ip, Secret: request.Secret, LastUpdateTime: time.Now(), } nodes = append(nodes, node) log.Printf("update nodes: %+v", nodes) } func List(c *gin.Context) { locker.RLock() defer locker.RUnlock() dtoNodes := make([]dto.Node, 0) for _, node := range healthNodes() { dtoNodes = append(dtoNodes, convert2DtoNode(node)) } c.JSON(http.StatusOK, dto.ListResponse{ Response: dto.NewOkResponse(), Data: dtoNodes, }) } func SecretRandom(c *gin.Context) { locker.RLock() defer locker.RUnlock() random := rand.Intn(len(nodes)) for i, node := range nodes { if i == random { c.Header("Content-Disposition", "attachment; filename=client.ovpn") c.Data(http.StatusOK, "plain/text", []byte(node.Secret)) return } } c.JSON(http.StatusNotFound, gin.H{ "message": "not found", }) } func Secret(c *gin.Context) { locker.RLock() defer locker.RUnlock() var request dto.DetailRequest if err := c.ShouldBindQuery(&request); err != nil { dto.BadRequest(c, err) return } for _, node := range nodes { if node.Ip == request.Ip { //secret, err := util.AesEncrypt([]byte(node.Secret)) //if err != nil { // dto.Error(c, err) // return //} c.Header("Content-Disposition", "attachment; filename=client.ovpn") c.Data(http.StatusOK, "plain/text", []byte(node.Secret)) return } } c.JSON(http.StatusNotFound, gin.H{ "message": "not found ip", }) } func Health(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "up"}) } func healthNodes() []*model.Node { healthNodes := make([]*model.Node, 0) for _, node := range nodes { if node.LastUpdateTime.Add(10 * time.Second).After(time.Now()) { healthNodes = append(healthNodes, node) } } return healthNodes } func convert2DtoNode(node *model.Node) dto.Node { return dto.Node{ Ip: node.Ip, CountryCode: node.CountryCode, CountryName: node.CountryName, Icon: "https://img.zcool.cn/community/010a5a57dbc2140000018c1b0a9b5f.png", } }