package server import ( "be-vpn/internal/dto" "be-vpn/internal/model" "be-vpn/internal/storage" "fmt" "github.com/gin-gonic/gin" "log" "math/rand" "net/http" "sort" "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 } var node *model.Node if len(healthNodes()) > 0 { node = healthNodes()[0] } freeTrialDuration := uint64(0) if usedDuration <= totalFreeDuration { freeTrialDuration = totalFreeDuration - usedDuration } c.JSON(http.StatusOK, dto.ConfigResponse{ Response: dto.NewOkResponse(), Data: dto.ConfigResult{ FreeTrialDuration: freeTrialDuration, Timestamp: time.Now().Unix(), Node: convert2DtoNode(node, 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 { freeTrialDuration := totalFreeDuration - existed if freeTrialDuration > totalFreeDuration || freeTrialDuration < 0 { freeTrialDuration = 0 } c.JSON(http.StatusOK, dto.ConfigResponse{ Response: dto.NewOkResponse(), Data: dto.ConfigResult{ FreeTrialDuration: freeTrialDuration, Timestamp: time.Now().Unix(), Node: convert2DtoNode(healthNodes()[0], 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.City = request.City 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() nodes := healthNodes() sort.SliceStable(nodes, func(i, j int) bool { return nodes[i].CountryCode > nodes[j].CountryCode }) countryLabelSeqs := make(map[string]int) dtoNodes := make([]*dto.Node, 0) for _, node := range nodes { seq, ok := countryLabelSeqs[node.CountryCode] if !ok { countryLabelSeqs[node.CountryCode] = 0 } else { countryLabelSeqs[node.CountryCode] = seq + 1 } dtoNodes = append(dtoNodes, convert2DtoNode(node, countryLabelSeqs[node.CountryCode])) } 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, seq int) *dto.Node { if node == nil { return nil } icons := map[string]string{ "BR": "http://v.starttransfernow.com/static/BR.jpg", "DE": "http://v.starttransfernow.com/static/DE.jpg", "HK": "http://v.starttransfernow.com/static/HK.jpg", "JP": "http://v.starttransfernow.com/static/JP.jpg", "US": "http://v.starttransfernow.com/static/US.jpg", } countryLabels := map[string]string{ "BR": "Brazil", "DE": "Germany", "HK": "Hong Kong", "JP": "Japan", "US": "United States", } return &dto.Node{ Ip: node.Ip, CountryCode: node.CountryCode, CountryName: node.CountryName, City: node.City, CountryLabel: fmt.Sprintf("%s - %d", countryLabels[node.CountryCode], seq+1), Icon: icons[node.CountryCode], } }