mirror of
				https://github.com/TeamPiped/instances-api.git
				synced 2024-08-14 23:57:19 +00:00 
			
		
		
		
	Merge pull request #44 from Bnyro/main
Parallel instance checks and improved error messages, code cleanup
This commit is contained in:
		
						commit
						bb978ea3a3
					
				
					 1 changed files with 192 additions and 114 deletions
				
			
		
							
								
								
									
										306
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										306
									
								
								main.go
									
										
									
									
									
								
							| 
						 | 
					@ -3,7 +3,8 @@ package main
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"github.com/gofiber/fiber/v2"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
| 
						 | 
					@ -11,8 +12,11 @@ import (
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gofiber/fiber/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gofiber/fiber/v2/middleware/cors"
 | 
						"github.com/gofiber/fiber/v2/middleware/cors"
 | 
				
			||||||
	"github.com/gofiber/fiber/v2/middleware/etag"
 | 
						"github.com/gofiber/fiber/v2/middleware/etag"
 | 
				
			||||||
	"github.com/google/go-github/v53/github"
 | 
						"github.com/google/go-github/v53/github"
 | 
				
			||||||
| 
						 | 
					@ -40,6 +44,157 @@ type FrontendConfig struct {
 | 
				
			||||||
	S3Enabled bool `json:"s3Enabled"`
 | 
						S3Enabled bool `json:"s3Enabled"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var client = http.Client{
 | 
				
			||||||
 | 
						Timeout: 10 * time.Second,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testUrl(url string) (*http.Response, error) {
 | 
				
			||||||
 | 
						resp, err := client.Get(url)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return resp, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.StatusCode != 200 {
 | 
				
			||||||
 | 
							return resp, errors.New(fmt.Sprintf("Invalid response code at %s: %d", url, resp.StatusCode))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testCaching(ApiUrl string) (bool, error) {
 | 
				
			||||||
 | 
						resp, err := testUrl(ApiUrl + "/trending?region=US")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						oldTiming := resp.Header.Get("Server-Timing")
 | 
				
			||||||
 | 
						resp, err = testUrl(ApiUrl + "/trending?region=US")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						newTiming := resp.Header.Get("Server-Timing")
 | 
				
			||||||
 | 
						cacheWorking := oldTiming == newTiming
 | 
				
			||||||
 | 
						return cacheWorking, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getConfig(ApiUrl string) (FrontendConfig, error) {
 | 
				
			||||||
 | 
						resp, err := testUrl(ApiUrl + "/config")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return FrontendConfig{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bytes, err := io.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return FrontendConfig{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var config FrontendConfig
 | 
				
			||||||
 | 
						err = json.Unmarshal(bytes, &config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return FrontendConfig{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return config, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getInstanceDetails(split []string, latest string) (Instance, error) {
 | 
				
			||||||
 | 
						ApiUrl := strings.TrimSpace(split[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wg := sync.WaitGroup{}
 | 
				
			||||||
 | 
						errorChannel := make(chan error)
 | 
				
			||||||
 | 
						// the amount of tests to do
 | 
				
			||||||
 | 
						wg.Add(6)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var lastChecked int64
 | 
				
			||||||
 | 
						var registered int64
 | 
				
			||||||
 | 
						var config FrontendConfig
 | 
				
			||||||
 | 
						var hash string
 | 
				
			||||||
 | 
						var version string
 | 
				
			||||||
 | 
						var cacheWorking bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							wg.Wait()
 | 
				
			||||||
 | 
							close(errorChannel)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							if _, err := testUrl(ApiUrl + "/healthcheck"); err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							lastChecked = time.Now().Unix()
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							resp, err := testUrl(ApiUrl + "/registered/badge")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							registered, err = strconv.ParseInt(number_re.FindString(resp.Request.URL.Path), 10, 32)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							resp, err := testUrl(ApiUrl + "/version")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							buf := new(strings.Builder)
 | 
				
			||||||
 | 
							_, err = io.Copy(buf, resp.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							version = strings.TrimSpace(buf.String())
 | 
				
			||||||
 | 
							version_split := strings.Split(version, "-")
 | 
				
			||||||
 | 
							hash = version_split[len(version_split)-1]
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							config, err = getConfig(ApiUrl)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							cacheWorking, err = testCaching(ApiUrl)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer wg.Done()
 | 
				
			||||||
 | 
							// check if instance can fetch videos
 | 
				
			||||||
 | 
							if _, err := testUrl(ApiUrl + "/streams/jNQXAC9IVRw"); err != nil {
 | 
				
			||||||
 | 
								errorChannel <- err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for err := range errorChannel {
 | 
				
			||||||
 | 
							return Instance{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Instance{
 | 
				
			||||||
 | 
							Name:        strings.TrimSpace(split[0]),
 | 
				
			||||||
 | 
							ApiUrl:      ApiUrl,
 | 
				
			||||||
 | 
							Locations:   strings.TrimSpace(split[2]),
 | 
				
			||||||
 | 
							Cdn:         strings.TrimSpace(split[3]) == "Yes",
 | 
				
			||||||
 | 
							Registered:  int(registered),
 | 
				
			||||||
 | 
							LastChecked: lastChecked,
 | 
				
			||||||
 | 
							Version:     version,
 | 
				
			||||||
 | 
							UpToDate:    strings.Contains(latest, hash),
 | 
				
			||||||
 | 
							Cache:       cacheWorking,
 | 
				
			||||||
 | 
							S3Enabled:   config.S3Enabled,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func monitorInstances() {
 | 
					func monitorInstances() {
 | 
				
			||||||
	ctx := context.Background()
 | 
						ctx := context.Background()
 | 
				
			||||||
	var tc *http.Client
 | 
						var tc *http.Client
 | 
				
			||||||
| 
						 | 
					@ -76,7 +231,6 @@ func monitorInstances() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if resp.StatusCode == 200 {
 | 
							if resp.StatusCode == 200 {
 | 
				
			||||||
 | 
					 | 
				
			||||||
			// parse the response
 | 
								// parse the response
 | 
				
			||||||
			buf := new(strings.Builder)
 | 
								buf := new(strings.Builder)
 | 
				
			||||||
			_, err := io.Copy(buf, resp.Body)
 | 
								_, err := io.Copy(buf, resp.Body)
 | 
				
			||||||
| 
						 | 
					@ -88,124 +242,45 @@ func monitorInstances() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			lines := strings.Split(buf.String(), "\n")
 | 
								lines := strings.Split(buf.String(), "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			instances := []Instance{}
 | 
								instancesMap := make(map[int]Instance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								wg := sync.WaitGroup{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			skipped := 0
 | 
								skipped := 0
 | 
				
			||||||
 | 
								checking := 0
 | 
				
			||||||
			for _, line := range lines {
 | 
								for _, line := range lines {
 | 
				
			||||||
				split := strings.Split(line, "|")
 | 
									split := strings.Split(line, "|")
 | 
				
			||||||
				if len(split) >= 5 {
 | 
					
 | 
				
			||||||
					if skipped < 2 {
 | 
									if len(split) < 5 {
 | 
				
			||||||
						skipped++
 | 
										continue
 | 
				
			||||||
						continue
 | 
									}
 | 
				
			||||||
					}
 | 
					
 | 
				
			||||||
					ApiUrl := strings.TrimSpace(split[1])
 | 
									// skip first two table lines
 | 
				
			||||||
					resp, err := http.Get(ApiUrl + "/healthcheck")
 | 
									if skipped < 2 {
 | 
				
			||||||
					if err != nil {
 | 
										skipped++
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									wg.Add(1)
 | 
				
			||||||
 | 
									go func(i int, split []string) {
 | 
				
			||||||
 | 
										defer wg.Done()
 | 
				
			||||||
 | 
										instance, err := getInstanceDetails(split, latest)
 | 
				
			||||||
 | 
										if err == nil {
 | 
				
			||||||
 | 
											instancesMap[i] = instance
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
						log.Print(err)
 | 
											log.Print(err)
 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					LastChecked := time.Now().Unix()
 | 
									}(checking, split)
 | 
				
			||||||
					if resp.StatusCode != 200 {
 | 
									checking++
 | 
				
			||||||
						continue
 | 
								}
 | 
				
			||||||
					}
 | 
								wg.Wait()
 | 
				
			||||||
					resp, err = http.Get(ApiUrl + "/registered/badge")
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					registered, err := strconv.ParseInt(number_re.FindString(resp.Request.URL.Path), 10, 32)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					resp, err = http.Get(ApiUrl + "/version")
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					buf := new(strings.Builder)
 | 
					 | 
				
			||||||
					_, err = io.Copy(buf, resp.Body)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					version := strings.TrimSpace(buf.String())
 | 
					 | 
				
			||||||
					version_split := strings.Split(version, "-")
 | 
					 | 
				
			||||||
					hash := version_split[len(version_split)-1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					resp, err = http.Get(ApiUrl + "/config")
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					bytes, err := io.ReadAll(resp.Body)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					var config FrontendConfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					err = json.Unmarshal(bytes, &config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					cache_working := false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					resp, err = http.Get(ApiUrl + "/trending?region=US")
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if resp.StatusCode == 200 {
 | 
					 | 
				
			||||||
						old_timing := resp.Header.Get("Server-Timing")
 | 
					 | 
				
			||||||
						resp, err = http.Get(ApiUrl + "/trending?region=US")
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							log.Print(err)
 | 
					 | 
				
			||||||
							continue
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						if resp.StatusCode == 200 {
 | 
					 | 
				
			||||||
							new_timing := resp.Header.Get("Server-Timing")
 | 
					 | 
				
			||||||
							if old_timing == new_timing {
 | 
					 | 
				
			||||||
								cache_working = true
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// check if instance can fetch videos
 | 
					 | 
				
			||||||
					resp, err = http.Get(ApiUrl + "/streams/jNQXAC9IVRw")
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						log.Print(err)
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					instances = append(instances, Instance{
 | 
					 | 
				
			||||||
						Name:        strings.TrimSpace(split[0]),
 | 
					 | 
				
			||||||
						ApiUrl:      ApiUrl,
 | 
					 | 
				
			||||||
						Locations:   strings.TrimSpace(split[2]),
 | 
					 | 
				
			||||||
						Cdn:         strings.TrimSpace(split[3]) == "Yes",
 | 
					 | 
				
			||||||
						Registered:  int(registered),
 | 
					 | 
				
			||||||
						LastChecked: LastChecked,
 | 
					 | 
				
			||||||
						Version:     version,
 | 
					 | 
				
			||||||
						UpToDate:    strings.Contains(latest, hash),
 | 
					 | 
				
			||||||
						Cache:       cache_working,
 | 
					 | 
				
			||||||
						S3Enabled:   config.S3Enabled,
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Map to ordered array
 | 
				
			||||||
 | 
								var instances []Instance
 | 
				
			||||||
 | 
								for i := 0; i < checking; i++ {
 | 
				
			||||||
 | 
									instance, ok := instancesMap[i]
 | 
				
			||||||
 | 
									if ok {
 | 
				
			||||||
 | 
										instances = append(instances, instance)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -228,5 +303,8 @@ func main() {
 | 
				
			||||||
		return c.JSON(monitored_instances)
 | 
							return c.JSON(monitored_instances)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.Listen(":3000")
 | 
						err := app.Listen(":3000")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue