pls pull #1

Merged
haveachin merged 6 commits from haveachin/go-tsviewer:master into master 2019-01-16 22:12:42 +00:00
13 changed files with 442 additions and 1 deletions
Showing only changes of commit 5c2d4d06ed - Show all commits

5
.gitignore vendored
View file

@ -10,4 +10,7 @@
*.test *.test
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Custom Blacklist
config.json

63
config/config.go Normal file
View 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: "",
},
}
}

View 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"`
}

View 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)
}))
})
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}