pls pull #1
					 13 changed files with 442 additions and 1 deletions
				
			
		
							
								
								
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -10,4 +10,7 @@
 | 
			
		|||
*.test
 | 
			
		||||
 | 
			
		||||
# Output of the go coverage tool, specifically when used with LiteIDE
 | 
			
		||||
*.out
 | 
			
		||||
*.out
 | 
			
		||||
 | 
			
		||||
# Custom Blacklist
 | 
			
		||||
config.json
 | 
			
		||||
							
								
								
									
										63
									
								
								config/config.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								config/config.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	IP   string `json:"ip"`
 | 
			
		||||
	Port uint16 `json:"port"`
 | 
			
		||||
	User User   `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	Name     string `json:"name"`
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FileName = "config.json"
 | 
			
		||||
 | 
			
		||||
func New() (*Config, error) {
 | 
			
		||||
	config := defaults()
 | 
			
		||||
 | 
			
		||||
	configFile, err := os.Open(FileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println("Made it")
 | 
			
		||||
		if err := config.createFile(); err != nil {
 | 
			
		||||
			log.Println("WUT?")
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Println(config)
 | 
			
		||||
		return &config, nil
 | 
			
		||||
	}
 | 
			
		||||
	defer configFile.Close()
 | 
			
		||||
 | 
			
		||||
	jsonParser := json.NewDecoder(configFile)
 | 
			
		||||
	jsonParser.Decode(&config)
 | 
			
		||||
 | 
			
		||||
	return &config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (config Config) createFile() error {
 | 
			
		||||
	configJSON, err := json.MarshalIndent(config, "", "    ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ioutil.WriteFile(FileName, configJSON, 0644)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaults() Config {
 | 
			
		||||
	return Config{
 | 
			
		||||
		IP:   "127.0.0.1",
 | 
			
		||||
		Port: 10011,
 | 
			
		||||
		User: User{
 | 
			
		||||
			Name:     "serveradmin",
 | 
			
		||||
			Password: "",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								features/channel/channel.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								features/channel/channel.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package channel
 | 
			
		||||
 | 
			
		||||
type Service interface {
 | 
			
		||||
	Channel(id int) (*Channel, error)
 | 
			
		||||
	Channels() ([]*Channel, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Channel struct {
 | 
			
		||||
	ID                   int       `json:"id"`
 | 
			
		||||
	Subchannels          []Channel `json:"subchannel"`
 | 
			
		||||
	Name                 string    `json:"name"`
 | 
			
		||||
	TotalClients         int       `json:"totalClients"`
 | 
			
		||||
	NeededSubscribePower int       `json:"neededSubscribePower"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								features/channel/handler.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								features/channel/handler.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
package channel
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/response"
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ChannelHandler(s Service) http.HandlerFunc {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		response.Handler(w, response.HandlerFunc(func() (int, error) {
 | 
			
		||||
			id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusBadRequest, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			c, err := s.Channel(int(id))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusNotFound, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return response.New(c, r).Send(w, http.StatusOK)
 | 
			
		||||
		}))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ChannelsHandler(s Service) http.HandlerFunc {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		response.Handler(w, response.HandlerFunc(func() (int, error) {
 | 
			
		||||
			cc, err := s.Channels()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusBadRequest, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return response.New(cc, r).Send(w, http.StatusOK)
 | 
			
		||||
		}))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								features/channel/routes.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								features/channel/routes.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package channel
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Routes(s Service) *chi.Mux {
 | 
			
		||||
	router := chi.NewRouter()
 | 
			
		||||
 | 
			
		||||
	router.Get("/{id}", ChannelHandler(s))
 | 
			
		||||
	router.Get("/", ChannelsHandler(s))
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/config"
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/channel"
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/service"
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
	"github.com/go-chi/chi/middleware"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Routes(s service.Service) *chi.Mux {
 | 
			
		||||
	router := chi.NewRouter()
 | 
			
		||||
	router.Use(
 | 
			
		||||
		middleware.Logger,
 | 
			
		||||
		middleware.Timeout(5*time.Second),
 | 
			
		||||
		middleware.DefaultCompress,
 | 
			
		||||
		middleware.RedirectSlashes,
 | 
			
		||||
		middleware.Recoverer,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	router.Route("/v1", func(r chi.Router) {
 | 
			
		||||
		r.Mount("/channels", channel.Routes(s))
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	config, err := config.New()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	service, err := service.New(*config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer service.Client.Close()
 | 
			
		||||
 | 
			
		||||
	router := Routes(*service)
 | 
			
		||||
 | 
			
		||||
	log.Fatal("Handler: ", http.ListenAndServe(":8080", router))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								request/meta/meta.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								request/meta/meta.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
package meta
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Meta struct {
 | 
			
		||||
	Pretty   bool `json:"pretty"`
 | 
			
		||||
	Envelope bool `json:"envelope"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFromRequest(r *http.Request) *Meta {
 | 
			
		||||
	meta := Meta{
 | 
			
		||||
		Pretty:   false,
 | 
			
		||||
		Envelope: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pretty, ok := r.URL.Query()["pretty"]
 | 
			
		||||
	if ok && len(pretty) > 0 {
 | 
			
		||||
		prettyBool, err := strconv.ParseBool(pretty[0])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			meta.Pretty = prettyBool
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	envelope, ok := r.URL.Query()["envelope"]
 | 
			
		||||
	if ok && len(envelope) > 0 {
 | 
			
		||||
		envelopeBool, err := strconv.ParseBool(envelope[0])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			meta.Envelope = envelopeBool
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &meta
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								request/routes.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								request/routes.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
package request
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	GET    = "GET"
 | 
			
		||||
	PUT    = "PUT"
 | 
			
		||||
	POST   = "POST"
 | 
			
		||||
	DELETE = "DELETE"
 | 
			
		||||
	LINK   = "LINK"
 | 
			
		||||
	UNLINK = "UNLINK"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										21
									
								
								response/error.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								response/error.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
package response
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// Error data type
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Status    int    `json:"status"`
 | 
			
		||||
	Error     string `json:"error"`
 | 
			
		||||
	Timestamp string `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewError creates a new Response fill with an error
 | 
			
		||||
func NewError(status int, err error) *Response {
 | 
			
		||||
	return &Response{
 | 
			
		||||
		Content: Error{
 | 
			
		||||
			Status:    status,
 | 
			
		||||
			Error:     err.Error(),
 | 
			
		||||
			Timestamp: time.Now().Format("2006-01-02T15:04:05Z"), //.Format("02 Jan 2006, 15:04:05 MST"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								response/response.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								response/response.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,87 @@
 | 
			
		|||
package response
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/request/meta"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type HandlerFunc func() (int, error)
 | 
			
		||||
 | 
			
		||||
// Responder is a service for responses
 | 
			
		||||
type Responder interface {
 | 
			
		||||
	Send(http.ResponseWriter, int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Response data type
 | 
			
		||||
type Response struct {
 | 
			
		||||
	Content interface{}
 | 
			
		||||
	Pretty  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Envelope struct {
 | 
			
		||||
	Data interface{} `json:"data,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new Response
 | 
			
		||||
func New(content interface{}, r *http.Request) *Response {
 | 
			
		||||
	var resp Response
 | 
			
		||||
 | 
			
		||||
	meta := meta.NewFromRequest(r)
 | 
			
		||||
 | 
			
		||||
	if meta.Envelope {
 | 
			
		||||
		resp = Response{
 | 
			
		||||
			Content: Envelope{
 | 
			
		||||
				Data: content,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		resp = Response{
 | 
			
		||||
			Content: content,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.Pretty = meta.Pretty
 | 
			
		||||
 | 
			
		||||
	return &resp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send sends the response
 | 
			
		||||
func (resp Response) Send(w http.ResponseWriter, status int) (int, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		rawJSON []byte
 | 
			
		||||
		err     error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if resp.Pretty {
 | 
			
		||||
		rawJSON, err = json.MarshalIndent(resp.Content, "", "    ")
 | 
			
		||||
	} else {
 | 
			
		||||
		rawJSON, err = json.Marshal(resp.Content)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return http.StatusInternalServerError, err
 | 
			
		||||
	} else if string(rawJSON) == "null" {
 | 
			
		||||
		rawJSON = make([]byte, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	w.WriteHeader(status)
 | 
			
		||||
	w.Write(rawJSON)
 | 
			
		||||
 | 
			
		||||
	return status, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Handler(w http.ResponseWriter, hf HandlerFunc) {
 | 
			
		||||
	status, err := hf()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("HTTP %d: %q", status, err)
 | 
			
		||||
	if status, err = NewError(status, err).Send(w, status); err != nil {
 | 
			
		||||
		http.Error(w, http.StatusText(status), status)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								service/channel.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								service/channel.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/channel"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s Service) Channel(id int) (*channel.Channel, error) {
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Service) Channels() ([]*channel.Channel, error) {
 | 
			
		||||
	cc, err := s.Client.Server.ClientList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range cc {
 | 
			
		||||
		log.Println(*c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								service/service.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								service/service.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/config"
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/stringer"
 | 
			
		||||
	ts3 "github.com/multiplay/go-ts3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Service struct {
 | 
			
		||||
	Client *ts3.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(config config.Config) (*Service, error) {
 | 
			
		||||
	addr, err := stringer.Build(config.IP, ":", strconv.Itoa(int(config.Port)))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client, err := ts3.NewClient(addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := client.Login(config.User.Name, config.User.Password); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Service{
 | 
			
		||||
		Client: client,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								stringer/stringer.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								stringer/stringer.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
package stringer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Build(ss ...string) (string, error) {
 | 
			
		||||
	var builder strings.Builder
 | 
			
		||||
 | 
			
		||||
	for _, s := range ss {
 | 
			
		||||
		if _, err := builder.WriteString(s); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return builder.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ToCamel(str string) string {
 | 
			
		||||
	if len(str) < 1 {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, c := range str[1:] {
 | 
			
		||||
		if c >= 'A' && c <= 'Z' {
 | 
			
		||||
			str = fmt.Sprintf("%s_%c%s", str[:i+1], unicode.ToLower(c), str[i+2:])
 | 
			
		||||
			return ToCamel(str)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ToSnake(str string) string {
 | 
			
		||||
	if len(str) < 1 {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, c := range str[1:] {
 | 
			
		||||
		if c == '_' {
 | 
			
		||||
			str = fmt.Sprintf("%s%c%s", str[:i+1], unicode.ToUpper(rune(str[i+2])), str[i+3:])
 | 
			
		||||
			return ToSnake(str)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue