pls pull #1
					 27 changed files with 791 additions and 627 deletions
				
			
		
							
								
								
									
										17
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1 +1,16 @@
 | 
			
		|||
node_modules/
 | 
			
		||||
# Binaries for programs and plugins
 | 
			
		||||
*.exe
 | 
			
		||||
*.exe~
 | 
			
		||||
*.dll
 | 
			
		||||
*.so
 | 
			
		||||
*.dylib
 | 
			
		||||
*.bat
 | 
			
		||||
 | 
			
		||||
# Test binary, build with `go test -c`
 | 
			
		||||
*.test
 | 
			
		||||
 | 
			
		||||
# Output of the go coverage tool, specifically when used with LiteIDE
 | 
			
		||||
*.out
 | 
			
		||||
 | 
			
		||||
# Custom Blacklist
 | 
			
		||||
config.json
 | 
			
		||||
							
								
								
									
										71
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										71
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,2 +1,71 @@
 | 
			
		|||
# tsviewer
 | 
			
		||||
# go-tsviewer **[WIP]**
 | 
			
		||||
## **WARNING** This API is not usable ATM
 | 
			
		||||
 | 
			
		||||
A REST API made for TS3 Viewer.
 | 
			
		||||
 | 
			
		||||
# Features
 | 
			
		||||
## Config
 | 
			
		||||
The config file is generated automatically on first startup.
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "ip": "127.0.0.1",              // Server IP
 | 
			
		||||
    "port": 10011,                  // Dataquery Port
 | 
			
		||||
    "user": {
 | 
			
		||||
        "name": "serveradmin",      // Username
 | 
			
		||||
        "password": ""              // Password
 | 
			
		||||
    },
 | 
			
		||||
    "server": {
 | 
			
		||||
        "port": 9987                // Port of the target server
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## URL-Parameter
 | 
			
		||||
| Name       | Type     | Description              |
 | 
			
		||||
| ---------- | -------- | ------------------------ |
 | 
			
		||||
| `pretty`   | `bool`   | pretty-prints JSON       |
 | 
			
		||||
| `envelope` | `bool`   | wraps JSON in data array |
 | 
			
		||||
## Channels
 | 
			
		||||
- **`GET`** `/v1/channels/:id`
 | 
			
		||||
- **`GET`** `/v1/channels`
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "databaseId": 1,
 | 
			
		||||
    "channelId": 1,
 | 
			
		||||
    "nickname": "serveradmin from 127.0.0.1:58359",
 | 
			
		||||
    "type": 1,
 | 
			
		||||
    "away": false,
 | 
			
		||||
    "awayMessage": ""
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Clients
 | 
			
		||||
- **`GET`** `/v1/clients/:id`
 | 
			
		||||
- **`GET`** `/v1/clients/`
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "id": 1,
 | 
			
		||||
    "subchannels": [
 | 
			
		||||
        ... (contains all subchannel)
 | 
			
		||||
    ],
 | 
			
		||||
    "name": "main1",
 | 
			
		||||
    "totalClients": 0,
 | 
			
		||||
    "neededSubscribePower": 0
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
## Server
 | 
			
		||||
- **`GET`** `/v1/server/info`
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "name": "TeamSpeak ]I[ Server",
 | 
			
		||||
    "status": "online",
 | 
			
		||||
    "version": "3.5.1 [Build: 1545076855]",
 | 
			
		||||
    "welcomeMessage": "Welcome to TeamSpeak, check [URL]www.teamspeak.com[/URL] for latest information",
 | 
			
		||||
    "maxClients": 32,
 | 
			
		||||
    "clientsOnline": 2,
 | 
			
		||||
    "reservedSlots": 0,
 | 
			
		||||
    "uptime": 5976,
 | 
			
		||||
    "totalPing": 0,
 | 
			
		||||
    "minAndroidVersion": 1502275280,
 | 
			
		||||
    "minClientVersion": 1513163251,
 | 
			
		||||
    "miniOSVersion": 1502275280
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										43
									
								
								app.js
									
										
									
									
									
								
							
							
						
						
									
										43
									
								
								app.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
var express = require('express');
 | 
			
		||||
var exphbs = require('express-handlebars');
 | 
			
		||||
 | 
			
		||||
var app = express();
 | 
			
		||||
 | 
			
		||||
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
 | 
			
		||||
app.set('view engine', 'handlebars');
 | 
			
		||||
 | 
			
		||||
app.get('/', function (req, res) {
 | 
			
		||||
    res.render('home', {foo: 'bar'});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(3000);
 | 
			
		||||
 | 
			
		||||
const TeamSpeak3 = require("ts3-nodejs-library")
 | 
			
		||||
 | 
			
		||||
var ts3conn = new TeamSpeak3({
 | 
			
		||||
    host: "localhost",
 | 
			
		||||
    queryport: 10011,
 | 
			
		||||
    serverport: 9987,
 | 
			
		||||
    username: "serveradmin",
 | 
			
		||||
    password: "R0cHL6tb",
 | 
			
		||||
    nickname: "NodeJS Query Framework",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
    ts3conn.on("ready", () => {
 | 
			
		||||
        ts3conn.clientList({client_type:0}).then(clients => {
 | 
			
		||||
            clients.forEach(client => {
 | 
			
		||||
                console.log("Online: ", client.getCache().client_nickname)
 | 
			
		||||
            })
 | 
			
		||||
        }).catch(e => console.log("CATCHED", e.message))
 | 
			
		||||
 | 
			
		||||
        ts3conn.channelList({}).then(channel => {
 | 
			
		||||
            channel.forEach(channel => {
 | 
			
		||||
                console.log("Channel: ", channel.getCache().channel_name)
 | 
			
		||||
            })
 | 
			
		||||
        }).catch(e => console.log("CATCHED", e.message))
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    ts3conn.on("error", e => console.log("Error", e.message))
 | 
			
		||||
    ts3conn.on("close", e => console.log("Connection has been closed!", e))
 | 
			
		||||
							
								
								
									
										69
									
								
								config/config.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								config/config.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	IP     string `json:"ip"`
 | 
			
		||||
	Port   uint16 `json:"port"`
 | 
			
		||||
	User   User   `json:"user"`
 | 
			
		||||
	Server Server `json:"server"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	Name     string `json:"name"`
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Server struct {
 | 
			
		||||
	Port uint16 `json:"port"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FileName = "config.json"
 | 
			
		||||
 | 
			
		||||
func New() (*Config, error) {
 | 
			
		||||
	config := defaults()
 | 
			
		||||
 | 
			
		||||
	configFile, err := os.Open(FileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err := config.createFile(); err != nil {
 | 
			
		||||
			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: "",
 | 
			
		||||
		},
 | 
			
		||||
		Server: Server{
 | 
			
		||||
			Port: 9987,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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:"subchannels,omitempty"`
 | 
			
		||||
	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 ChannelAPIHandler(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 ChannelsAPIHandler(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 APIRoutes(s Service) *chi.Mux {
 | 
			
		||||
	router := chi.NewRouter()
 | 
			
		||||
 | 
			
		||||
	router.Get("/{id}", ChannelAPIHandler(s))
 | 
			
		||||
	router.Get("/", ChannelsAPIHandler(s))
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								features/client/client.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								features/client/client.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
type Service interface {
 | 
			
		||||
	Client(id int) (*Client, error)
 | 
			
		||||
	Clients() ([]*Client, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Client struct {
 | 
			
		||||
	DatabaseID  int    `json:"databaseId"`
 | 
			
		||||
	ChannelID   int    `json:"channelId"`
 | 
			
		||||
	Nickname    string `json:"nickname"`
 | 
			
		||||
	Type        int    `json:"type"`
 | 
			
		||||
	Away        bool   `json:"away"`
 | 
			
		||||
	AwayMessage string `json:"awayMessage"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								features/client/handler.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								features/client/handler.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/response"
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ClientAPIHandler(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.Client(int(id))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusNotFound, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return response.New(c, r).Send(w, http.StatusOK)
 | 
			
		||||
		}))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ClientsAPIHandler(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.Clients()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusBadRequest, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return response.New(cc, r).Send(w, http.StatusOK)
 | 
			
		||||
		}))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								features/client/routes.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								features/client/routes.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func APIRoutes(s Service) *chi.Mux {
 | 
			
		||||
	router := chi.NewRouter()
 | 
			
		||||
 | 
			
		||||
	router.Get("/{id}", ClientAPIHandler(s))
 | 
			
		||||
	router.Get("/", ClientsAPIHandler(s))
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								features/server/handler.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								features/server/handler.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/response"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InfoAPIHandler(s Service) http.HandlerFunc {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		response.Handler(w, response.HandlerFunc(func() (int, error) {
 | 
			
		||||
			s, err := s.Info()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusNotFound, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return response.New(s, r).Send(w, http.StatusOK)
 | 
			
		||||
		}))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								features/server/routes.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								features/server/routes.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-chi/chi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func APIRoutes(s Service) *chi.Mux {
 | 
			
		||||
	router := chi.NewRouter()
 | 
			
		||||
 | 
			
		||||
	router.Get("/info", InfoAPIHandler(s))
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								features/server/server.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								features/server/server.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
package server
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
type Service interface {
 | 
			
		||||
	Info() (*Server, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Server struct {
 | 
			
		||||
	Name              string        `json:"name"`
 | 
			
		||||
	Status            string        `json:"status"`
 | 
			
		||||
	Version           string        `json:"version"`
 | 
			
		||||
	WelcomeMessage    string        `json:"welcomeMessage"`
 | 
			
		||||
	MaxClients        int           `json:"maxClients"`
 | 
			
		||||
	ClientsOnline     int           `json:"clientsOnline"`
 | 
			
		||||
	ReservedSlots     int           `json:"reservedSlots"`
 | 
			
		||||
	Uptime            time.Duration `json:"uptime"`
 | 
			
		||||
	TotalPing         float32       `json:"totalPing"`
 | 
			
		||||
	MinAndroidVersion int           `json:"minAndroidVersion"`
 | 
			
		||||
	MinClientVersion  int           `json:"minClientVersion"`
 | 
			
		||||
	MiniOSVersion     int           `json:"miniOSVersion"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
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/features/client"
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/server"
 | 
			
		||||
	"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.APIRoutes(s))
 | 
			
		||||
		r.Mount("/clients", client.APIRoutes(s))
 | 
			
		||||
		r.Mount("/server", server.APIRoutes(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.TSClient.Close()
 | 
			
		||||
 | 
			
		||||
	router := Routes(*service)
 | 
			
		||||
 | 
			
		||||
	log.Fatal("Handler: ", http.ListenAndServe(":8080", router))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										552
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										552
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1,552 +0,0 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "TSViewer",
 | 
			
		||||
  "version": "0.0.1",
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "accepts": {
 | 
			
		||||
      "version": "1.3.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
 | 
			
		||||
      "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "mime-types": "2.1.21",
 | 
			
		||||
        "negotiator": "0.6.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "array-flatten": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
 | 
			
		||||
      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
 | 
			
		||||
    },
 | 
			
		||||
    "asap": {
 | 
			
		||||
      "version": "2.0.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
 | 
			
		||||
      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
 | 
			
		||||
    },
 | 
			
		||||
    "async": {
 | 
			
		||||
      "version": "2.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
 | 
			
		||||
      "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "lodash": "4.17.11"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "balanced-match": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
 | 
			
		||||
    },
 | 
			
		||||
    "body-parser": {
 | 
			
		||||
      "version": "1.18.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
 | 
			
		||||
      "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "bytes": "3.0.0",
 | 
			
		||||
        "content-type": "1.0.4",
 | 
			
		||||
        "debug": "2.6.9",
 | 
			
		||||
        "depd": "1.1.2",
 | 
			
		||||
        "http-errors": "1.6.3",
 | 
			
		||||
        "iconv-lite": "0.4.23",
 | 
			
		||||
        "on-finished": "2.3.0",
 | 
			
		||||
        "qs": "6.5.2",
 | 
			
		||||
        "raw-body": "2.3.3",
 | 
			
		||||
        "type-is": "1.6.16"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "brace-expansion": {
 | 
			
		||||
      "version": "1.1.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
 | 
			
		||||
      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "balanced-match": "1.0.0",
 | 
			
		||||
        "concat-map": "0.0.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "bytes": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
 | 
			
		||||
    },
 | 
			
		||||
    "commander": {
 | 
			
		||||
      "version": "2.17.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
 | 
			
		||||
      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
 | 
			
		||||
      "optional": true
 | 
			
		||||
    },
 | 
			
		||||
    "concat-map": {
 | 
			
		||||
      "version": "0.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 | 
			
		||||
      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
 | 
			
		||||
    },
 | 
			
		||||
    "content-disposition": {
 | 
			
		||||
      "version": "0.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
 | 
			
		||||
      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
 | 
			
		||||
    },
 | 
			
		||||
    "content-type": {
 | 
			
		||||
      "version": "1.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
 | 
			
		||||
    },
 | 
			
		||||
    "cookie": {
 | 
			
		||||
      "version": "0.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
 | 
			
		||||
      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
 | 
			
		||||
    },
 | 
			
		||||
    "cookie-signature": {
 | 
			
		||||
      "version": "1.0.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
 | 
			
		||||
      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
 | 
			
		||||
    },
 | 
			
		||||
    "debug": {
 | 
			
		||||
      "version": "2.6.9",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
 | 
			
		||||
      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "ms": "2.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "define-properties": {
 | 
			
		||||
      "version": "1.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "object-keys": "1.0.12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "depd": {
 | 
			
		||||
      "version": "1.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
 | 
			
		||||
      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
 | 
			
		||||
    },
 | 
			
		||||
    "destroy": {
 | 
			
		||||
      "version": "1.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
 | 
			
		||||
      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
 | 
			
		||||
    },
 | 
			
		||||
    "ee-first": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
 | 
			
		||||
      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
 | 
			
		||||
    },
 | 
			
		||||
    "encodeurl": {
 | 
			
		||||
      "version": "1.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
 | 
			
		||||
      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
 | 
			
		||||
    },
 | 
			
		||||
    "escape-html": {
 | 
			
		||||
      "version": "1.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
 | 
			
		||||
      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
 | 
			
		||||
    },
 | 
			
		||||
    "etag": {
 | 
			
		||||
      "version": "1.8.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
 | 
			
		||||
      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
 | 
			
		||||
    },
 | 
			
		||||
    "express": {
 | 
			
		||||
      "version": "4.16.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
 | 
			
		||||
      "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "accepts": "1.3.5",
 | 
			
		||||
        "array-flatten": "1.1.1",
 | 
			
		||||
        "body-parser": "1.18.3",
 | 
			
		||||
        "content-disposition": "0.5.2",
 | 
			
		||||
        "content-type": "1.0.4",
 | 
			
		||||
        "cookie": "0.3.1",
 | 
			
		||||
        "cookie-signature": "1.0.6",
 | 
			
		||||
        "debug": "2.6.9",
 | 
			
		||||
        "depd": "1.1.2",
 | 
			
		||||
        "encodeurl": "1.0.2",
 | 
			
		||||
        "escape-html": "1.0.3",
 | 
			
		||||
        "etag": "1.8.1",
 | 
			
		||||
        "finalhandler": "1.1.1",
 | 
			
		||||
        "fresh": "0.5.2",
 | 
			
		||||
        "merge-descriptors": "1.0.1",
 | 
			
		||||
        "methods": "1.1.2",
 | 
			
		||||
        "on-finished": "2.3.0",
 | 
			
		||||
        "parseurl": "1.3.2",
 | 
			
		||||
        "path-to-regexp": "0.1.7",
 | 
			
		||||
        "proxy-addr": "2.0.4",
 | 
			
		||||
        "qs": "6.5.2",
 | 
			
		||||
        "range-parser": "1.2.0",
 | 
			
		||||
        "safe-buffer": "5.1.2",
 | 
			
		||||
        "send": "0.16.2",
 | 
			
		||||
        "serve-static": "1.13.2",
 | 
			
		||||
        "setprototypeof": "1.1.0",
 | 
			
		||||
        "statuses": "1.4.0",
 | 
			
		||||
        "type-is": "1.6.16",
 | 
			
		||||
        "utils-merge": "1.0.1",
 | 
			
		||||
        "vary": "1.1.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "express-handlebars": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-gKBwu4GbCeSvLKbQeA91zgXnXC8=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "glob": "6.0.4",
 | 
			
		||||
        "graceful-fs": "4.1.15",
 | 
			
		||||
        "handlebars": "4.0.12",
 | 
			
		||||
        "object.assign": "4.1.0",
 | 
			
		||||
        "promise": "7.3.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "finalhandler": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "debug": "2.6.9",
 | 
			
		||||
        "encodeurl": "1.0.2",
 | 
			
		||||
        "escape-html": "1.0.3",
 | 
			
		||||
        "on-finished": "2.3.0",
 | 
			
		||||
        "parseurl": "1.3.2",
 | 
			
		||||
        "statuses": "1.4.0",
 | 
			
		||||
        "unpipe": "1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "forwarded": {
 | 
			
		||||
      "version": "0.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
 | 
			
		||||
      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
 | 
			
		||||
    },
 | 
			
		||||
    "fresh": {
 | 
			
		||||
      "version": "0.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
 | 
			
		||||
      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
 | 
			
		||||
    },
 | 
			
		||||
    "function-bind": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
 | 
			
		||||
    },
 | 
			
		||||
    "glob": {
 | 
			
		||||
      "version": "6.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
 | 
			
		||||
      "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "inflight": "1.0.6",
 | 
			
		||||
        "inherits": "2.0.3",
 | 
			
		||||
        "minimatch": "3.0.4",
 | 
			
		||||
        "once": "1.4.0",
 | 
			
		||||
        "path-is-absolute": "1.0.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "graceful-fs": {
 | 
			
		||||
      "version": "4.1.15",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
 | 
			
		||||
      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
 | 
			
		||||
    },
 | 
			
		||||
    "handlebars": {
 | 
			
		||||
      "version": "4.0.12",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz",
 | 
			
		||||
      "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "async": "2.6.1",
 | 
			
		||||
        "optimist": "0.6.1",
 | 
			
		||||
        "source-map": "0.6.1",
 | 
			
		||||
        "uglify-js": "3.4.9"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "has-symbols": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
 | 
			
		||||
    },
 | 
			
		||||
    "http-errors": {
 | 
			
		||||
      "version": "1.6.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
 | 
			
		||||
      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "depd": "1.1.2",
 | 
			
		||||
        "inherits": "2.0.3",
 | 
			
		||||
        "setprototypeof": "1.1.0",
 | 
			
		||||
        "statuses": "1.4.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "iconv-lite": {
 | 
			
		||||
      "version": "0.4.23",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
 | 
			
		||||
      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "safer-buffer": "2.1.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "inflight": {
 | 
			
		||||
      "version": "1.0.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
 | 
			
		||||
      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "once": "1.4.0",
 | 
			
		||||
        "wrappy": "1.0.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "inherits": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
 | 
			
		||||
      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
 | 
			
		||||
    },
 | 
			
		||||
    "ipaddr.js": {
 | 
			
		||||
      "version": "1.8.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
 | 
			
		||||
      "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
 | 
			
		||||
    },
 | 
			
		||||
    "lodash": {
 | 
			
		||||
      "version": "4.17.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
 | 
			
		||||
      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
 | 
			
		||||
    },
 | 
			
		||||
    "media-typer": {
 | 
			
		||||
      "version": "0.3.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
 | 
			
		||||
      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
 | 
			
		||||
    },
 | 
			
		||||
    "merge-descriptors": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
 | 
			
		||||
    },
 | 
			
		||||
    "methods": {
 | 
			
		||||
      "version": "1.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
 | 
			
		||||
      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
 | 
			
		||||
    },
 | 
			
		||||
    "mime": {
 | 
			
		||||
      "version": "1.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
 | 
			
		||||
      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "mime-db": {
 | 
			
		||||
      "version": "1.37.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
 | 
			
		||||
      "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
 | 
			
		||||
    },
 | 
			
		||||
    "mime-types": {
 | 
			
		||||
      "version": "2.1.21",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
 | 
			
		||||
      "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "mime-db": "1.37.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "minimatch": {
 | 
			
		||||
      "version": "3.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "brace-expansion": "1.1.11"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "minimist": {
 | 
			
		||||
      "version": "0.0.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
 | 
			
		||||
      "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
 | 
			
		||||
    },
 | 
			
		||||
    "ms": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
 | 
			
		||||
    },
 | 
			
		||||
    "negotiator": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
 | 
			
		||||
      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
 | 
			
		||||
    },
 | 
			
		||||
    "object-keys": {
 | 
			
		||||
      "version": "1.0.12",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
 | 
			
		||||
      "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
 | 
			
		||||
    },
 | 
			
		||||
    "object.assign": {
 | 
			
		||||
      "version": "4.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "define-properties": "1.1.3",
 | 
			
		||||
        "function-bind": "1.1.1",
 | 
			
		||||
        "has-symbols": "1.0.0",
 | 
			
		||||
        "object-keys": "1.0.12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "on-finished": {
 | 
			
		||||
      "version": "2.3.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
 | 
			
		||||
      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "ee-first": "1.1.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "once": {
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "wrappy": "1.0.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "optimist": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
 | 
			
		||||
      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "minimist": "0.0.10",
 | 
			
		||||
        "wordwrap": "0.0.3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "parseurl": {
 | 
			
		||||
      "version": "1.3.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
 | 
			
		||||
      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
 | 
			
		||||
    },
 | 
			
		||||
    "path-is-absolute": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
 | 
			
		||||
    },
 | 
			
		||||
    "path-to-regexp": {
 | 
			
		||||
      "version": "0.1.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
 | 
			
		||||
      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
 | 
			
		||||
    },
 | 
			
		||||
    "promise": {
 | 
			
		||||
      "version": "7.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "asap": "2.0.6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "proxy-addr": {
 | 
			
		||||
      "version": "2.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "forwarded": "0.1.2",
 | 
			
		||||
        "ipaddr.js": "1.8.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "qs": {
 | 
			
		||||
      "version": "6.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
 | 
			
		||||
      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
 | 
			
		||||
    },
 | 
			
		||||
    "range-parser": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
 | 
			
		||||
      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
 | 
			
		||||
    },
 | 
			
		||||
    "raw-body": {
 | 
			
		||||
      "version": "2.3.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
 | 
			
		||||
      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "bytes": "3.0.0",
 | 
			
		||||
        "http-errors": "1.6.3",
 | 
			
		||||
        "iconv-lite": "0.4.23",
 | 
			
		||||
        "unpipe": "1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "safe-buffer": {
 | 
			
		||||
      "version": "5.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 | 
			
		||||
      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
 | 
			
		||||
    },
 | 
			
		||||
    "safer-buffer": {
 | 
			
		||||
      "version": "2.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
 | 
			
		||||
      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
 | 
			
		||||
    },
 | 
			
		||||
    "send": {
 | 
			
		||||
      "version": "0.16.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
 | 
			
		||||
      "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "debug": "2.6.9",
 | 
			
		||||
        "depd": "1.1.2",
 | 
			
		||||
        "destroy": "1.0.4",
 | 
			
		||||
        "encodeurl": "1.0.2",
 | 
			
		||||
        "escape-html": "1.0.3",
 | 
			
		||||
        "etag": "1.8.1",
 | 
			
		||||
        "fresh": "0.5.2",
 | 
			
		||||
        "http-errors": "1.6.3",
 | 
			
		||||
        "mime": "1.4.1",
 | 
			
		||||
        "ms": "2.0.0",
 | 
			
		||||
        "on-finished": "2.3.0",
 | 
			
		||||
        "range-parser": "1.2.0",
 | 
			
		||||
        "statuses": "1.4.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "serve-static": {
 | 
			
		||||
      "version": "1.13.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
 | 
			
		||||
      "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "encodeurl": "1.0.2",
 | 
			
		||||
        "escape-html": "1.0.3",
 | 
			
		||||
        "parseurl": "1.3.2",
 | 
			
		||||
        "send": "0.16.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "setprototypeof": {
 | 
			
		||||
      "version": "1.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "source-map": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
 | 
			
		||||
      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
 | 
			
		||||
    },
 | 
			
		||||
    "statuses": {
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
 | 
			
		||||
    },
 | 
			
		||||
    "type-is": {
 | 
			
		||||
      "version": "1.6.16",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
 | 
			
		||||
      "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "media-typer": "0.3.0",
 | 
			
		||||
        "mime-types": "2.1.21"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "uglify-js": {
 | 
			
		||||
      "version": "3.4.9",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
 | 
			
		||||
      "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
 | 
			
		||||
      "optional": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "commander": "2.17.1",
 | 
			
		||||
        "source-map": "0.6.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "unpipe": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
 | 
			
		||||
    },
 | 
			
		||||
    "utils-merge": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
 | 
			
		||||
    },
 | 
			
		||||
    "vary": {
 | 
			
		||||
      "version": "1.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
 | 
			
		||||
      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
 | 
			
		||||
    },
 | 
			
		||||
    "wordwrap": {
 | 
			
		||||
      "version": "0.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
 | 
			
		||||
      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
 | 
			
		||||
    },
 | 
			
		||||
    "wrappy": {
 | 
			
		||||
      "version": "1.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
 | 
			
		||||
      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								package.json
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "TSViewer",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "main": "app.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1",
 | 
			
		||||
    "start": "node app.js"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "Marc Kemper",
 | 
			
		||||
  "license": "",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.16.4",
 | 
			
		||||
    "express-handlebars": "^3.0.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								service/channel.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								service/channel.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/channel"
 | 
			
		||||
	"github.com/multiplay/go-ts3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s Service) Channel(id int) (*channel.Channel, error) {
 | 
			
		||||
	channels, err := s.TSClient.Server.ChannelList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c *channel.Channel
 | 
			
		||||
 | 
			
		||||
	for _, channel := range channels {
 | 
			
		||||
		if channel.ID == id {
 | 
			
		||||
			c = convertChannel(channel)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c == nil {
 | 
			
		||||
		return nil, errors.New("channel does not exist")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Service) Channels() ([]*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.ParentID == 0 {
 | 
			
		||||
			cc = append(cc, convertChannel(channel))
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, c := range cc {
 | 
			
		||||
			if c.ID == channel.ParentID {
 | 
			
		||||
				c.Subchannels = append(c.Subchannels, *convertChannel(channel))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertChannel(c *ts3.Channel) *channel.Channel {
 | 
			
		||||
	return &channel.Channel{
 | 
			
		||||
		ID:                   c.ID,
 | 
			
		||||
		Subchannels:          []channel.Channel{},
 | 
			
		||||
		Name:                 c.ChannelName,
 | 
			
		||||
		TotalClients:         c.TotalClients,
 | 
			
		||||
		NeededSubscribePower: c.NeededSubscribePower,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								service/client.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								service/client.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/client"
 | 
			
		||||
	"github.com/multiplay/go-ts3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s Service) Client(id int) (*client.Client, error) {
 | 
			
		||||
	clients, err := s.TSClient.Server.ClientList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c *client.Client
 | 
			
		||||
 | 
			
		||||
	for _, client := range clients {
 | 
			
		||||
		if client.DatabaseID == id {
 | 
			
		||||
			c = convertClient(client)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c == nil {
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Service) Clients() ([]*client.Client, error) {
 | 
			
		||||
	clients, err := s.TSClient.Server.ClientList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cc []*client.Client
 | 
			
		||||
 | 
			
		||||
	for _, client := range clients {
 | 
			
		||||
		cc = append(cc, convertClient(client))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertClient(c *ts3.OnlineClient) *client.Client {
 | 
			
		||||
	return &client.Client{
 | 
			
		||||
		DatabaseID:  c.DatabaseID,
 | 
			
		||||
		ChannelID:   c.ID,
 | 
			
		||||
		Nickname:    c.Nickname,
 | 
			
		||||
		Type:        c.Type,
 | 
			
		||||
		Away:        c.Away,
 | 
			
		||||
		AwayMessage: c.AwayMessage,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								service/server.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								service/server.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.cliffbreak.de/haveachin/go-tsviewer/features/server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s Service) Info() (*server.Server, error) {
 | 
			
		||||
	serverInfo, err := s.TSClient.Server.Info()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &server.Server{
 | 
			
		||||
		Name:              serverInfo.Name,
 | 
			
		||||
		Status:            serverInfo.Status,
 | 
			
		||||
		Version:           serverInfo.Version,
 | 
			
		||||
		WelcomeMessage:    serverInfo.WelcomeMessage,
 | 
			
		||||
		MaxClients:        serverInfo.MaxClients,
 | 
			
		||||
		ClientsOnline:     serverInfo.ClientsOnline,
 | 
			
		||||
		ReservedSlots:     serverInfo.ReservedSlots,
 | 
			
		||||
		Uptime:            time.Duration(serverInfo.Uptime) * time.Nanosecond,
 | 
			
		||||
		TotalPing:         serverInfo.TotalPing,
 | 
			
		||||
		MinAndroidVersion: serverInfo.MinAndroidVersion,
 | 
			
		||||
		MinClientVersion:  serverInfo.MinClientVersion,
 | 
			
		||||
		MiniOSVersion:     serverInfo.MiniOSVersion,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								service/service.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								service/service.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
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 {
 | 
			
		||||
	TSClient *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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := client.UsePort(int(config.Server.Port)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Service{
 | 
			
		||||
		TSClient: 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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
<h1>TSViewer: Home</h1>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <title>TSViewer</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
        Test 123
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue