Compare commits

...

59 commits

Author SHA1 Message Date
59bd3d7139 changed config to yaml; removed flag support 2019-04-22 02:51:02 +02:00
a454dbc947 Change ReadME 2019-02-07 21:46:45 +01:00
8416fcbdb4 chnages 2019-02-07 15:13:13 +01:00
alpha
cf378679fb Changes in ReadME 2019-02-07 15:08:42 +01:00
d57986721d Merge branch 'master' of 123niel/tsviewer into master
Geprüft
Mit freundlichen Grüßen
Eichhorn
2019-02-07 14:02:28 +00:00
b91c02e815 annonymized readme 2019-02-07 14:58:44 +01:00
bfba32968c updated readme 2019-02-07 14:50:36 +01:00
53b246a6df added Cors-Header 2019-02-07 12:13:32 +01:00
1f523a6b7d Chillin 2019-02-04 01:01:58 +01:00
1a9f7fd72e updated templates && styles 2019-02-04 00:54:14 +01:00
0121873d35 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer
fixed channels route
2019-02-04 00:40:47 +01:00
c3fb6ce53b fixed channels route 2019-02-04 00:40:43 +01:00
fa24055a52 Fix wrong documentation 2019-02-03 21:39:03 +00:00
036d6f604c added startup messages 2019-02-03 21:00:14 +01:00
1f23290854 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer
added mutable nickname
2019-02-03 20:08:51 +01:00
c78afcdc24 added mutable nickname 2019-02-03 20:08:48 +01:00
4d4311c30c Update 'README.md' 2019-02-03 18:58:33 +00:00
5527744c43 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer
ENV vars to uppercase
2019-02-03 19:23:07 +01:00
2089963e9e ENV vars to uppercase 2019-02-03 19:22:59 +01:00
7ebfd7be08 readme 2019-01-31 23:36:07 +01:00
0d575286fa Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer 2019-01-31 23:28:59 +01:00
d6e1047ba2 Fixes 2019-01-31 23:28:58 +01:00
eddc32a157 added env vars 2019-01-31 23:00:42 +01:00
8bd0e85e17 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer 2019-01-31 22:51:51 +01:00
298435a4cb Final Fix maybe 2019-01-31 22:51:48 +01:00
cfde0fd79b added flags 2019-01-31 22:07:07 +01:00
d1b9551889 Could work 2019-01-31 21:43:17 +01:00
a308206256 Argumente werden nun übergeben ! 2019-01-31 21:05:49 +01:00
96fd78fcd3 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer 2019-01-31 20:57:09 +01:00
5a0da7fced ARG Flags added 2019-01-31 20:57:07 +01:00
126eb99d0f hotfix 2019-01-31 20:35:31 +01:00
e4556869fe added config.json 2019-01-31 19:59:26 +01:00
50b56687eb Docker 2019-01-31 19:56:44 +01:00
e152962fe7 Docker copy 2019-01-31 19:39:04 +01:00
6343577301 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer
fixing web port
2019-01-31 19:37:53 +01:00
470370aebd fixed web port 2019-01-31 19:37:04 +01:00
5189cb63f6 Dockerfile 2019-01-31 19:36:27 +01:00
d7d3732fab Fixing Port issue 2019-01-31 19:17:59 +01:00
c0e8b1b8e1 Solved Conflict 2019-01-31 18:06:19 +01:00
66fc41cc7f Conf 2019-01-31 18:05:42 +01:00
2250abb484 docker.. 2019-01-31 18:04:45 +01:00
5012872895 dockerfile update #2 2019-01-31 16:28:38 +00:00
1441b942a9 dockerfile update 2019-01-31 16:26:30 +00:00
5bb3e7781a only main go 2019-01-31 16:53:41 +01:00
8ed735698b add go get 2019-01-31 16:51:36 +01:00
b2480324c3 „Dockerfile“ ändern 2019-01-31 15:42:25 +00:00
e64c0c78a6 Docker Changes 2019-01-31 16:37:53 +01:00
1e03bba52b test uncommit 2019-01-22 00:00:22 +01:00
036219f445 test commit 2019-01-21 23:58:48 +01:00
9e2d7e9acc I tried so hard 2019-01-21 18:53:33 +01:00
872dd200e1 Merge branch 'master' of https://git.cliffbreak.de/Cliffbreak/tsviewer
added templating
2019-01-17 22:10:06 +01:00
58761d011e added templating 2019-01-17 22:09:41 +01:00
49129c845f Fix README.md Markdown syntax 2019-01-17 09:25:04 +01:00
119f9c4915 clean up 2019-01-17 01:44:08 +01:00
edbf70eede updated README 2019-01-17 01:37:54 +01:00
c534e4bf49 bug fixes 2019-01-17 01:35:51 +01:00
615f1969d7 Dockerfile change 2019-01-16 23:25:48 +01:00
8f613ed089 Docker 2019-01-16 23:22:46 +01:00
42a606b308 Merge branch 'master' of haveachin/go-tsviewer into master 2019-01-16 22:12:42 +00:00
31 changed files with 479 additions and 169 deletions

0
.dockerignore Normal file
View file

3
.gitignore vendored
View file

@ -13,4 +13,5 @@
*.out *.out
# Custom Blacklist # Custom Blacklist
config.json
#CM

22
Dockerfile Normal file
View file

@ -0,0 +1,22 @@
FROM golang:latest
ENV TS3_IP=127.0.0.1
ENV TS3_NAME=serveradmin
ENV TS3_PW=<changeMe>
ENV TS3_PORT=9987
ENV TS3_QUERY=10011
ENV WEB_PORT=8080
ENV BLACKLIST_USER=USER
RUN mkdir /app
WORKDIR /app
RUN mkdir /app/go
RUN export GOPATH=/app/go
COPY main.go .
RUN go get; exit 0
WORKDIR $GOPATH/src/git.cliffbreak.de/Cliffbreak/tsviewer
COPY config.json .
RUN go build
CMD ["./tsviewer"]

View file

@ -1,30 +1,59 @@
# go-tsviewer **[WIP]** # tsviewer **[ALPHA 1.0.1]**
## **WARNING** This API is not usable ATM
A REST API made for TS3 Viewer. A TS3 Viewer with REST API.
## DOCKER-Config
```bash
docker build -t name/cont:ver .
docker run -e TS3_IP="<SERVER-IP>" -e TS3_PW="<SERVER-ADMIN_PW>" -e WEB_PORT="8080" -d -p 8080:8080 name/cont:ver
```
ENV:
```Docker
ENV TS3_IP=127.0.0.1
ENV TS3_NAME=serveradmin
ENV TS3_PW=<changeMe>
ENV TS3_PORT=9987
ENV TS3_QUERY=10011
ENV WEB_PORT=8080
ENV BLACKLIST_USER=USER
```
# Features # Features
## Config ## Config
The config file is generated automatically on first startup. The config file is generated automatically on first startup.
```json ```json
{ {
"ip": "127.0.0.1", // Server IP
"port": 10011, // Dataquery Port
"user": { "user": {
"name": "serveradmin", // Username "nickname": "serveradmin",
"password": "" // Password "name": "serveradmin",
"password": ""
}, },
"server": { "serverTS": {
"port": 9987 // Port of the target server "ip": "127.0.0.1",
"portServer": 9987,
"portQuery": 10011
},
"serverWeb": {
"port": 80
} }
} }
``` ```
## URL-Parameter ## URL-Parameter
| Name | Type | Description | | Name | Type | Description |
| ---------- | -------- | ------------------------ | | ---------- | ------ | ------------------------ |
| `pretty` | `bool` | pretty-prints JSON | | `pretty` | `bool` | pretty-prints JSON |
| `envelope` | `bool` | wraps JSON in data array | | `envelope` | `bool` | wraps JSON in data array |
## Channels ## Channels
- **`GET`** `/v1/channels/:id` - **`GET`** `/v1/channels/:id`
- **`GET`** `/v1/channels` - **`GET`** `/v1/channels`
```json ```json
@ -37,7 +66,9 @@ The config file is generated automatically on first startup.
"awayMessage": "" "awayMessage": ""
} }
``` ```
## Clients ## Clients
- **`GET`** `/v1/clients/:id` - **`GET`** `/v1/clients/:id`
- **`GET`** `/v1/clients/` - **`GET`** `/v1/clients/`
```json ```json
@ -51,7 +82,9 @@ The config file is generated automatically on first startup.
"neededSubscribePower": 0 "neededSubscribePower": 0
} }
``` ```
## Server ## Server
- **`GET`** `/v1/server/info` - **`GET`** `/v1/server/info`
```json ```json
{ {

15
config.json Normal file
View file

@ -0,0 +1,15 @@
{
"user": {
"nickname": "serveradmin",
"name": "serveradmin",
"password": "HNxkefVx"
},
"serverTS": {
"ip": "127.0.0.1",
"portServer": 9987,
"portQuery": 10011
},
"serverWeb": {
"port": 80
}
}

View file

@ -1,69 +1,84 @@
package config package config
import ( import (
"encoding/json"
"io/ioutil"
"log"
"os" "os"
"strconv"
"github.com/spf13/viper"
) )
type Config struct { type Config struct {
IP string `json:"ip"` User struct {
Port uint16 `json:"port"` Nickname string
User User `json:"user"` Name string
Server Server `json:"server"` Password string
} }
ServerTS struct {
type User struct { IP string
Name string `json:"name"` PortServer uint16
Password string `json:"password"` PortQuery uint16
}
ServerWeb struct {
Port uint16
} }
type Server struct {
Port uint16 `json:"port"`
} }
const FileName = "config.json" const FileName = "config.json"
func New() (*Config, error) { func New() (*Config, error) {
config := defaults() viper.SetConfigName("config")
viper.SetConfigFile("yaml")
configFile, err := os.Open(FileName) viper.AddConfigPath(".")
if err != nil { if err := viper.ReadInConfig(); err != nil {
if err := config.createFile(); err != nil {
return nil, err return nil, err
} }
log.Println(config) setDefaults()
return &config, nil
}
defer configFile.Close()
jsonParser := json.NewDecoder(configFile) var config Config
jsonParser.Decode(&config) err := viper.Unmarshal(&config)
return &config, nil config.overrideWithEnvironmentVars()
return &config, err
} }
func (config Config) createFile() error { func setDefaults() {
configJSON, err := json.MarshalIndent(config, "", " ") viper.SetDefault("User.Nickname", "serveradmin")
if err != nil { viper.SetDefault("User.Name", "serveradmin")
return err viper.SetDefault("User.Password", "<changeMe>")
viper.SetDefault("ServerTS.IP", "127.0.0.1")
viper.SetDefault("ServerTS.PortServer", "9987")
viper.SetDefault("ServerTS.PortQuery", "10011")
viper.SetDefault("ServerWeb.Port", "80")
} }
return ioutil.WriteFile(FileName, configJSON, 0644) func (config *Config) overrideWithEnvironmentVars() {
if nickname := os.Getenv("TS3_NICKNAME"); nickname != "" {
config.User.Nickname = nickname
} }
func defaults() Config { if username := os.Getenv("TS3_NAME"); username != "" {
return Config{ config.User.Name = username
IP: "127.0.0.1", }
Port: 10011,
User: User{ if password := os.Getenv("TS3_PW"); password != "" {
Name: "serveradmin", config.User.Password = password
Password: "", }
},
Server: Server{ if tsIP := os.Getenv("TS3_IP"); tsIP != "" {
Port: 9987, config.ServerTS.IP = tsIP
}, }
if tsPort, err := strconv.Atoi(os.Getenv("TS3_PORT")); tsPort <= 65535 && tsPort >= 0 && err == nil {
config.ServerTS.PortServer = uint16(tsPort)
}
if tsQuery, err := strconv.Atoi(os.Getenv("TS3_QUERY")); tsQuery <= 65535 && tsQuery >= 0 && err == nil {
config.ServerTS.PortQuery = uint16(tsQuery)
}
if webPort, err := strconv.Atoi(os.Getenv("WEB_PORT")); webPort <= 65535 && webPort >= 0 && err == nil {
config.ServerWeb.Port = uint16(webPort)
} }
} }

View file

@ -7,8 +7,8 @@ type Service interface {
type Channel struct { type Channel struct {
ID int `json:"id"` ID int `json:"id"`
Subchannels []Channel `json:"subchannels,omitempty"`
Name string `json:"name"` Name string `json:"name"`
Subchannels []Channel `json:"subchannels,omitempty"`
TotalClients int `json:"totalClients"` TotalClients int `json:"totalClients"`
NeededSubscribePower int `json:"neededSubscribePower"` NeededSubscribePower int `json:"neededSubscribePower"`
} }

View file

@ -4,13 +4,13 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"git.cliffbreak.de/haveachin/go-tsviewer/response" "git.cliffbreak.de/Cliffbreak/tsviewer/response"
"github.com/go-chi/chi" "github.com/go-chi/chi"
) )
func ChannelAPIHandler(s Service) http.HandlerFunc { func ChannelAPIHandler(s Service) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response.Handler(w, response.HandlerFunc(func() (int, error) { response.Handler(w, r, response.HandlerFunc(func() (int, error) {
id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64) id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64)
if err != nil { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err
@ -28,7 +28,7 @@ func ChannelAPIHandler(s Service) http.HandlerFunc {
func ChannelsAPIHandler(s Service) http.HandlerFunc { func ChannelsAPIHandler(s Service) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response.Handler(w, response.HandlerFunc(func() (int, error) { response.Handler(w, r, response.HandlerFunc(func() (int, error) {
cc, err := s.Channels() cc, err := s.Channels()
if err != nil { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err

View file

@ -4,13 +4,13 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"git.cliffbreak.de/haveachin/go-tsviewer/response" "git.cliffbreak.de/Cliffbreak/tsviewer/response"
"github.com/go-chi/chi" "github.com/go-chi/chi"
) )
func ClientAPIHandler(s Service) http.HandlerFunc { func ClientAPIHandler(s Service) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response.Handler(w, response.HandlerFunc(func() (int, error) { response.Handler(w, r, response.HandlerFunc(func() (int, error) {
id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64) id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 64)
if err != nil { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err
@ -28,7 +28,7 @@ func ClientAPIHandler(s Service) http.HandlerFunc {
func ClientsAPIHandler(s Service) http.HandlerFunc { func ClientsAPIHandler(s Service) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response.Handler(w, response.HandlerFunc(func() (int, error) { response.Handler(w, r, response.HandlerFunc(func() (int, error) {
cc, err := s.Clients() cc, err := s.Clients()
if err != nil { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err

View file

@ -3,13 +3,13 @@ package server
import ( import (
"net/http" "net/http"
"git.cliffbreak.de/haveachin/go-tsviewer/response" "git.cliffbreak.de/Cliffbreak/tsviewer/response"
) )
func InfoAPIHandler(s Service) http.HandlerFunc { func InfoAPIHandler(s Service) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response.Handler(w, response.HandlerFunc(func() (int, error) { response.Handler(w, r, response.HandlerFunc(func() (int, error) {
s, err := s.Info() s, err := s.ServerInfo()
if err != nil { if err != nil {
return http.StatusNotFound, err return http.StatusNotFound, err
} }

View file

@ -3,7 +3,7 @@ package server
import "time" import "time"
type Service interface { type Service interface {
Info() (*Server, error) ServerInfo() (*Server, error)
} }
type Server struct { type Server struct {

View file

@ -0,0 +1,32 @@
package index
import (
"html/template"
"net/http"
"git.cliffbreak.de/Cliffbreak/tsviewer/features/web/weberror"
)
func IndexGUIHandler(s Service, t template.Template) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server, err := s.ServerInfo()
if err != nil {
weberror.NewPage(err, http.StatusNotFound).Send(w, t)
return
}
channels, err := s.ChannelsRaw()
if err != nil {
weberror.NewPage(err, http.StatusNotFound).Send(w, t)
return
}
clients, err := s.Clients()
if err != nil {
weberror.NewPage(err, http.StatusNotFound).Send(w, t)
return
}
NewPage(*server, channels, clients).Send(w, t)
})
}

View file

@ -0,0 +1,35 @@
package index
import (
"html/template"
"io"
"git.cliffbreak.de/Cliffbreak/tsviewer/features/api/channel"
"git.cliffbreak.de/Cliffbreak/tsviewer/features/api/client"
"git.cliffbreak.de/Cliffbreak/tsviewer/features/api/server"
)
type Service interface {
ServerInfo() (*server.Server, error)
Clients() ([]*client.Client, error)
Channels() ([]*channel.Channel, error)
ChannelsRaw() ([]*channel.Channel, error)
}
type IndexPage struct {
Server server.Server
Channels []*channel.Channel
Clients []*client.Client
}
func NewPage(server server.Server, channels []*channel.Channel, clients []*client.Client) *IndexPage {
return &IndexPage{
Server: server,
Channels: channels,
Clients: clients,
}
}
func (page IndexPage) Send(w io.Writer, t template.Template) {
t.Lookup("index.html").Execute(w, page)
}

View file

@ -0,0 +1,15 @@
package index
import (
"html/template"
"github.com/go-chi/chi"
)
func GUIRoutes(s Service, t template.Template) *chi.Mux {
router := chi.NewRouter()
router.Get("/", IndexGUIHandler(s, t))
return router
}

View file

@ -0,0 +1,22 @@
package weberror
import (
"html/template"
"io"
)
type ErrorPage struct {
Error error
StatusCode int
}
func NewPage(err error, statusCode int) *ErrorPage {
return &ErrorPage{
Error: err,
StatusCode: statusCode,
}
}
func (page ErrorPage) Send(w io.Writer, t template.Template) {
t.Lookup("error.html").Execute(w, page)
}

35
gui/template.go Normal file
View file

@ -0,0 +1,35 @@
package gui
import (
"fmt"
"html/template"
"io/ioutil"
"strings"
)
func LoadTemplates() (*template.Template, error) {
return loadTemplates("templates/")
}
func loadTemplates(path string) (*template.Template, error) {
dir, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
var ff []string
for _, file := range dir {
filename := file.Name()
if strings.HasSuffix(filename, ".html") {
ff = append(ff, fmt.Sprintf("%s%s", path, filename))
}
}
templates, err := template.ParseFiles(ff...)
if err != nil {
return nil, err
}
return templates, nil
}

48
main.go
View file

@ -1,27 +1,38 @@
package main package main
import ( import (
"fmt"
"html/template"
"log" "log"
"net/http" "net/http"
"time" "time"
"git.cliffbreak.de/haveachin/go-tsviewer/config" "git.cliffbreak.de/Cliffbreak/tsviewer/config"
"git.cliffbreak.de/haveachin/go-tsviewer/features/channel" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/channel"
"git.cliffbreak.de/haveachin/go-tsviewer/features/client" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/client"
"git.cliffbreak.de/haveachin/go-tsviewer/features/server" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/server"
"git.cliffbreak.de/haveachin/go-tsviewer/service" "git.cliffbreak.de/Cliffbreak/tsviewer/features/web/index"
"git.cliffbreak.de/Cliffbreak/tsviewer/gui"
"git.cliffbreak.de/Cliffbreak/tsviewer/service"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/go-chi/chi/middleware" "github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
) )
func Routes(s service.Service) *chi.Mux { func Routes(s service.Service, t template.Template) *chi.Mux {
router := chi.NewRouter() router := chi.NewRouter()
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"Get"},
})
router.Use( router.Use(
middleware.Logger, middleware.Logger,
middleware.Timeout(5*time.Second), middleware.Timeout(5*time.Second),
middleware.DefaultCompress, middleware.DefaultCompress,
middleware.RedirectSlashes, middleware.RedirectSlashes,
middleware.Recoverer, middleware.Recoverer,
cors.Handler,
) )
router.Route("/v1", func(r chi.Router) { router.Route("/v1", func(r chi.Router) {
@ -30,22 +41,41 @@ func Routes(s service.Service) *chi.Mux {
r.Mount("/server", server.APIRoutes(s)) r.Mount("/server", server.APIRoutes(s))
}) })
router.Route("/", func(r chi.Router) {
r.Mount("/", index.GUIRoutes(s, t))
})
return router return router
} }
func main() { func main() {
log.Println("Loading configurations...")
config, err := config.New() config, err := config.New()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Println("Configurations loaded!")
log.Println("Starting query service...")
service, err := service.New(*config) service, err := service.New(*config)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer service.TSClient.Close() defer service.TSClient.Close()
log.Println("Query service connected to", fmt.Sprintf("%s:%d!", config.ServerTS.IP, config.ServerTS.PortQuery))
router := Routes(*service) log.Println("Loading templates...")
templates, err := gui.LoadTemplates()
log.Fatal("Handler: ", http.ListenAndServe(":8080", router)) if err != nil {
log.Fatal(err)
}
log.Println("All templates loaded!")
router := Routes(*service, *templates)
router.Get("/static/*", func(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/static", http.FileServer(http.Dir("./static"))).ServeHTTP(w, r)
})
log.Println("Starting the web server locally on port", config.ServerWeb.Port)
log.Fatal("Handler: ", http.ListenAndServe(fmt.Sprintf(":%d", config.ServerWeb.Port), router))
} }

View file

@ -10,12 +10,10 @@ type Error struct {
} }
// NewError creates a new Response fill with an error // NewError creates a new Response fill with an error
func NewError(status int, err error) *Response { func NewError(status int, err error) *Error {
return &Response{ return &Error{
Content: Error{
Status: status, Status: status,
Error: err.Error(), Error: err.Error(),
Timestamp: time.Now().Format("2006-01-02T15:04:05Z"), //.Format("02 Jan 2006, 15:04:05 MST"), Timestamp: time.Now().Format("2006-01-02T15:04:05Z"), //.Format("02 Jan 2006, 15:04:05 MST"),
},
} }
} }

View file

@ -5,7 +5,7 @@ import (
"log" "log"
"net/http" "net/http"
"git.cliffbreak.de/haveachin/go-tsviewer/request/meta" "git.cliffbreak.de/Cliffbreak/tsviewer/request/meta"
) )
type HandlerFunc func() (int, error) type HandlerFunc func() (int, error)
@ -74,14 +74,14 @@ func (resp Response) Send(w http.ResponseWriter, status int) (int, error) {
return status, nil return status, nil
} }
func Handler(w http.ResponseWriter, hf HandlerFunc) { func Handler(w http.ResponseWriter, r *http.Request, hf HandlerFunc) {
status, err := hf() status, err := hf()
if err == nil { if err == nil {
return return
} }
log.Printf("HTTP %d: %q", status, err) log.Printf("HTTP %d: %q", status, err)
if status, err = NewError(status, err).Send(w, status); err != nil { if status, err = New(NewError(status, err), r).Send(w, status); err != nil {
http.Error(w, http.StatusText(status), status) http.Error(w, http.StatusText(status), status)
} }
} }

View file

@ -3,7 +3,7 @@ package service
import ( import (
"errors" "errors"
"git.cliffbreak.de/haveachin/go-tsviewer/features/channel" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/channel"
"github.com/multiplay/go-ts3" "github.com/multiplay/go-ts3"
) )
@ -37,17 +37,38 @@ func (s Service) Channels() ([]*channel.Channel, error) {
var cc []*channel.Channel var cc []*channel.Channel
for _, channel := range channels { for i, channel := range channels {
if channel.ParentID == 0 { if channel == nil {
cc = append(cc, convertChannel(channel))
continue continue
} }
if channel.ParentID == 0 {
channels[i] = nil
cc = append(cc, convertChannel(channel))
}
}
for _, c := range cc { for _, c := range cc {
if c.ID == channel.ParentID { addSubChannels(c, channels)
c.Subchannels = append(c.Subchannels, *convertChannel(channel))
} }
return cc, nil
} }
func (s Service) ChannelsRaw() ([]*channel.Channel, error) {
channels, err := s.TSClient.Server.ChannelList()
if err != nil {
return nil, err
}
var cc []*channel.Channel
for _, channel := range channels {
if channel == nil {
continue
}
cc = append(cc, convertChannel(channel))
} }
return cc, nil return cc, nil
@ -56,9 +77,24 @@ func (s Service) Channels() ([]*channel.Channel, error) {
func convertChannel(c *ts3.Channel) *channel.Channel { func convertChannel(c *ts3.Channel) *channel.Channel {
return &channel.Channel{ return &channel.Channel{
ID: c.ID, ID: c.ID,
Subchannels: []channel.Channel{},
Name: c.ChannelName, Name: c.ChannelName,
Subchannels: []channel.Channel{},
TotalClients: c.TotalClients, TotalClients: c.TotalClients,
NeededSubscribePower: c.NeededSubscribePower, NeededSubscribePower: c.NeededSubscribePower,
} }
} }
func addSubChannels(c *channel.Channel, channels []*ts3.Channel) {
for i, channel := range channels {
if channel == nil {
continue
}
if c.ID == channel.ParentID {
channels[i] = nil
subChannel := convertChannel(channel)
addSubChannels(subChannel, channels)
c.Subchannels = append(c.Subchannels, *subChannel)
}
}
}

View file

@ -3,7 +3,7 @@ package service
import ( import (
"errors" "errors"
"git.cliffbreak.de/haveachin/go-tsviewer/features/client" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/client"
"github.com/multiplay/go-ts3" "github.com/multiplay/go-ts3"
) )
@ -26,10 +26,6 @@ func (s Service) Client(id int) (*client.Client, error) {
return nil, errors.New("client does not exist") return nil, errors.New("client does not exist")
} }
/* if _, err := s.TSClient.Server.ExecCmd(ts3.NewCmd(fmt.Sprintf("clientinfo clid=%d", c.ID)).WithResponse(&c)); err != nil {
return nil, err
} */
return c, nil return c, nil
} }

View file

@ -3,10 +3,10 @@ package service
import ( import (
"time" "time"
"git.cliffbreak.de/haveachin/go-tsviewer/features/server" "git.cliffbreak.de/Cliffbreak/tsviewer/features/api/server"
) )
func (s Service) Info() (*server.Server, error) { func (s Service) ServerInfo() (*server.Server, error) {
serverInfo, err := s.TSClient.Server.Info() serverInfo, err := s.TSClient.Server.Info()
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -1,10 +1,11 @@
package service package service
import ( import (
"strconv" "fmt"
"log"
"time"
"git.cliffbreak.de/haveachin/go-tsviewer/config" "git.cliffbreak.de/Cliffbreak/tsviewer/config"
"git.cliffbreak.de/haveachin/go-tsviewer/stringer"
ts3 "github.com/multiplay/go-ts3" ts3 "github.com/multiplay/go-ts3"
) )
@ -13,12 +14,7 @@ type Service struct {
} }
func New(config config.Config) (*Service, error) { func New(config config.Config) (*Service, error) {
addr, err := stringer.Build(config.IP, ":", strconv.Itoa(int(config.Port))) client, err := ts3.NewClient(fmt.Sprintf("%s:%d", config.ServerTS.IP, config.ServerTS.PortQuery))
if err != nil {
return nil, err
}
client, err := ts3.NewClient(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -27,10 +23,22 @@ func New(config config.Config) (*Service, error) {
return nil, err return nil, err
} }
if err := client.UsePort(int(config.Server.Port)); err != nil { if err := client.UsePort(int(config.ServerTS.PortServer)); err != nil {
return nil, err return nil, err
} }
if err := client.SetNick(config.User.Nickname); err != nil {
return nil, err
}
go func() {
for true {
time.Sleep(time.Second * 150)
client.Server.Version()
log.Println("keep alive")
}
}()
return &Service{ return &Service{
TSClient: client, TSClient: client,
}, nil }, nil

22
static/styles.css Normal file
View file

@ -0,0 +1,22 @@
*{
padding: 0;
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.content {
max-width: 500px;
margin: auto;
}
h2{
padding-top: 20px;
}
small{
margin-left: 10px;
}
p.client{
margin-left: 10px;
}

View file

@ -1,49 +0,0 @@
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
}

11
templates/error.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>Oops, an error occurred!</h1>
<p1>Error {{.StatusCode}}</p1>
<p1>{{.Error}}</p1>
</body>
</html>

33
templates/index.html Normal file
View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<title>TS3 Viewer</title>
<link rel="stylesheet" href="static/styles.css">
</head>
<body>
<div class="content">
<h2>Online Members</h2>
<ul>
{{range .Clients}}
<li>{{.Nickname}}</li>
{{end}}
</ul>
<h2>{{.Server.Name}}</h2>
<!-- <small>{{.Server.Version}}</small> -->
{{$clients := .Clients}}
{{range .Channels}}
<p class="channel">{{.Name}}</p>
{{$channelId := .ID}}
{{range $clients}}
{{if eq $channelId .ChannelID}}
<p class="client">{{.Nickname}}</p>
{{end}}
{{end}}
{{end}}
</div>
</body>
</html>