Compare commits
No commits in common. "834092a10184d8152331409918474e76a450add9" and "60034b48153e0cc5b3424ac2a517f4ada9b741cf" have entirely different histories.
834092a101
...
60034b4815
43 changed files with 114 additions and 5061 deletions
|
@ -16,7 +16,7 @@ tmp_dir = "tmp"
|
||||||
kill_delay = "0s"
|
kill_delay = "0s"
|
||||||
log = "build-errors.log"
|
log = "build-errors.log"
|
||||||
send_interrupt = false
|
send_interrupt = false
|
||||||
stop_on_error = false
|
stop_on_error = true
|
||||||
|
|
||||||
[color]
|
[color]
|
||||||
app = ""
|
app = ""
|
||||||
|
|
|
@ -7,16 +7,16 @@ repos:
|
||||||
- id: go-test-repo-mod
|
- id: go-test-repo-mod
|
||||||
name: Backend tests
|
name: Backend tests
|
||||||
|
|
||||||
- repo: local
|
# - repo: local
|
||||||
hooks:
|
# hooks:
|
||||||
- id: tsc
|
# - id: tsc
|
||||||
name: tsc
|
# name: tsc
|
||||||
entry: tsc
|
# entry: tsc
|
||||||
language: node
|
# language: node
|
||||||
files: \.tsx?$
|
# files: \.tsx?$
|
||||||
args: ["-p", "./ui/tsconfig.json"]
|
# args: ["-p", "./ui/tsconfig.json"]
|
||||||
additional_dependencies: ["typescript@4.5.2"]
|
# additional_dependencies: ["typescript@4.5.2"]
|
||||||
pass_filenames: false
|
# pass_filenames: false
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: v2.4.1
|
rev: v2.4.1
|
||||||
|
|
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
|
@ -2,10 +2,9 @@
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "TSGRain WebUI server",
|
"name": "SEBRAUC server",
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/src"
|
"program": "${workspaceFolder}/src"
|
||||||
},
|
},
|
||||||
|
@ -17,6 +16,13 @@
|
||||||
"runtimeExecutable": "npm",
|
"runtimeExecutable": "npm",
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"type": "pwa-node"
|
"type": "pwa-node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Test program",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/tmp"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -35,10 +35,9 @@ protoc:
|
||||||
|
|
||||||
generate-apidoc:
|
generate-apidoc:
|
||||||
SWAGGER_GENERATE_EXTENSION=false swagger generate spec --scan-models -o ${APIDOC_FILE}
|
SWAGGER_GENERATE_EXTENSION=false swagger generate spec --scan-models -o ${APIDOC_FILE}
|
||||||
swagger validate ${APIDOC_FILE}
|
|
||||||
|
|
||||||
generate-apiclient:
|
generate-apiclient:
|
||||||
openapi-generator generate -i ${APIDOC_FILE} -g typescript-axios -o ${UI_DIR}/src/tsgrain-client -p "supportsES6=true"
|
openapi-generator generate -i ${APIDOC_FILE} -g typescript-axios -o ${UI_DIR}/src/sebrauc-client -p "supportsES6=true"
|
||||||
cd ${UI_DIR} && npm run format
|
cd ${UI_DIR} && npm run format
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -87,7 +87,6 @@ message Task {
|
||||||
message TaskList {
|
message TaskList {
|
||||||
repeated Task tasks = 1;
|
repeated Task tasks = 1;
|
||||||
Timestamp now = 2;
|
Timestamp now = 2;
|
||||||
bool auto_mode = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Job {
|
message Job {
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
// Job model
|
|
||||||
//
|
|
||||||
// Job stellt einen Bewässerungszeitplan dar.
|
|
||||||
//
|
|
||||||
//swagger:model Job
|
|
||||||
type Job struct {
|
|
||||||
// ID des Jobs
|
|
||||||
// required: true
|
|
||||||
// example: 1
|
|
||||||
Id int32 `json:"id"`
|
|
||||||
|
|
||||||
// Zritstempel des Bewässerungsjobs
|
|
||||||
// required: true
|
|
||||||
// example: 1643756107
|
|
||||||
Date int64 `json:"date"`
|
|
||||||
|
|
||||||
// Bewässerungsdauer in Sekunden
|
|
||||||
// required: true
|
|
||||||
// example: 300
|
|
||||||
Duration int32 `json:"duration"`
|
|
||||||
|
|
||||||
// Zu bewässernde Zonen
|
|
||||||
// required: true
|
|
||||||
// example: [2, 3]
|
|
||||||
Zones []int32 `json:"zones"`
|
|
||||||
|
|
||||||
// Job aktiviert?
|
|
||||||
// required: true
|
|
||||||
Enable bool `json:"enable"`
|
|
||||||
|
|
||||||
// Job täglich wiederholen
|
|
||||||
// required: true
|
|
||||||
Repeat bool `json:"repeat"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewJob model
|
|
||||||
//
|
|
||||||
// NewJob stellt einen zu erstellenden Bewässerungszeitplan dar.
|
|
||||||
//
|
|
||||||
//swagger:model NewJob
|
|
||||||
type NewJob struct {
|
|
||||||
// Zritstempel des Bewässerungsjobs
|
|
||||||
// required: true
|
|
||||||
// example: 1643756107
|
|
||||||
Date int64 `json:"date"`
|
|
||||||
|
|
||||||
// Bewässerungsdauer in Sekunden
|
|
||||||
// required: true
|
|
||||||
// example: 300
|
|
||||||
Duration int32 `json:"duration"`
|
|
||||||
|
|
||||||
// Zu bewässernde Zonen
|
|
||||||
// required: true
|
|
||||||
// example: [2, 3]
|
|
||||||
Zones []int32 `json:"zones"`
|
|
||||||
|
|
||||||
// Job aktiviert?
|
|
||||||
// required: true
|
|
||||||
Enable bool `json:"enable"`
|
|
||||||
|
|
||||||
// Job täglich wiederholen
|
|
||||||
// required: true
|
|
||||||
Repeat bool `json:"repeat"`
|
|
||||||
}
|
|
||||||
|
|
||||||
//swagger:model JobList
|
|
||||||
type JobList []Job
|
|
||||||
|
|
||||||
//swagger:model JobID
|
|
||||||
type JobID struct {
|
|
||||||
Id int32 `json:"id" binding:"required"`
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
// AutoMode model
|
|
||||||
//
|
|
||||||
// Zustand des Automatikmodus
|
|
||||||
//
|
|
||||||
//swagger:model AutoMode
|
|
||||||
type AutoMode struct {
|
|
||||||
// required: true
|
|
||||||
State bool `json:"state" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultIrrigationTime model
|
|
||||||
//
|
|
||||||
// Manuelle Bewässerungszeit in Sekunden
|
|
||||||
//
|
|
||||||
//swagger:model DefaultIrrigationTime
|
|
||||||
type DefaultIrrigationTime struct {
|
|
||||||
// required: true
|
|
||||||
Time int32 `json:"time" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigTime model
|
|
||||||
//
|
|
||||||
// Aktuelle Systemzeit/Zeitzone
|
|
||||||
//
|
|
||||||
//swagger:model ConfigTime
|
|
||||||
type ConfigTime struct {
|
|
||||||
// Aktuelle Systemzeit
|
|
||||||
//
|
|
||||||
// required: true
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
|
|
||||||
// Aktuelle Zeitzone
|
|
||||||
//
|
|
||||||
// required: true
|
|
||||||
Timezone string `json:"timezone"`
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
// Task model
|
|
||||||
//
|
|
||||||
// Task stellt eine Bewässerungsaufgabe dar.
|
|
||||||
//
|
|
||||||
//swagger:model Task
|
|
||||||
type Task struct {
|
|
||||||
// Quelle der Bewässerungsaufgabe (0: MANUAL, 1: SCHEDULE)
|
|
||||||
// required: true
|
|
||||||
// example: 0
|
|
||||||
Source int32 `json:"source"`
|
|
||||||
|
|
||||||
// Nummer der Bewässerungszone
|
|
||||||
// required: true
|
|
||||||
// example: 2
|
|
||||||
ZoneId int32 `json:"zone_id"`
|
|
||||||
|
|
||||||
// Bewässerungsdauer in Sekunden
|
|
||||||
// required: true
|
|
||||||
// example: 300
|
|
||||||
Duration int32 `json:"duration"`
|
|
||||||
|
|
||||||
// Zeitstempel, wann die Bewässerung gestartet wurde
|
|
||||||
// required: true
|
|
||||||
// nullable: true
|
|
||||||
// example: 1643756107
|
|
||||||
DatetimeStarted *int64 `json:"datetime_started"`
|
|
||||||
|
|
||||||
// Zeitstempel, wann die Bewässerung beendet sein wird
|
|
||||||
// required: true
|
|
||||||
// nullable: true
|
|
||||||
// example: 1643756407
|
|
||||||
DatetimeFinished *int64 `json:"datetime_finished"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TaskList model
|
|
||||||
//
|
|
||||||
// TaskList ist eine Liste der aktuell laufenden Bewässerungsaufgaben.
|
|
||||||
//
|
|
||||||
//swagger:model TaskList
|
|
||||||
type TaskList struct {
|
|
||||||
// Aktueller Zeitstempel
|
|
||||||
// required: true
|
|
||||||
// example: 1643756107
|
|
||||||
Now int64 `json:"now"`
|
|
||||||
|
|
||||||
// Liste der laufenden Tasks
|
|
||||||
// required: true
|
|
||||||
Tasks []Task `json:"tasks"`
|
|
||||||
|
|
||||||
// Automatikmodus aktiv
|
|
||||||
// required: true
|
|
||||||
AutoMode bool `json:"auto_mode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TaskRequestResult model
|
|
||||||
//
|
|
||||||
// TaskRequestResult wird beim Starten eines Tasks zurückgegeben
|
|
||||||
//
|
|
||||||
//swagger:model TaskRequestResult
|
|
||||||
type TaskRequestResult struct {
|
|
||||||
Started bool
|
|
||||||
Stopped bool
|
|
||||||
}
|
|
|
@ -24,17 +24,14 @@ import (
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/tsgrain_rpc"
|
"code.thetadev.de/TSGRain/WebUI/src/tsgrain_rpc"
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/util"
|
"code.thetadev.de/TSGRain/WebUI/src/util"
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/util/mode"
|
"code.thetadev.de/TSGRain/WebUI/src/util/mode"
|
||||||
"code.thetadev.de/TSGRain/WebUI/ui"
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebUIServer struct {
|
type WebUIServer struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
streamer *stream.API
|
streamer *stream.API
|
||||||
rpc *tsgrain_rpc.RPCClient
|
rpc *tsgrain_rpc.RPCClient
|
||||||
nZones int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(config *config.Config) *WebUIServer {
|
func NewServer(config *config.Config) *WebUIServer {
|
||||||
|
@ -84,26 +81,12 @@ func (srv *WebUIServer) getRouter() *gin.Engine {
|
||||||
api.GET("/panic", srv.controllerPanic)
|
api.GET("/panic", srv.controllerPanic)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.POST("/task/manual", srv.controllerStartTask)
|
|
||||||
|
|
||||||
api.GET("/jobs", srv.controllerGetJobs)
|
|
||||||
api.POST("/job", srv.controllerCreateJob)
|
|
||||||
api.PUT("/job", srv.controllerUpdateJob)
|
|
||||||
api.DELETE("/job", srv.controllerDeleteJob)
|
|
||||||
|
|
||||||
api.GET("config/auto", srv.controllerGetAutoMode)
|
|
||||||
api.POST("config/auto", srv.controllerSetAutoMode)
|
|
||||||
api.GET("config/defaultIrrigationTime", srv.controllerGetDefaultIrrigationTime)
|
|
||||||
api.POST("config/defaultIrrigationTime", srv.controllerSetDefaultIrrigationTime)
|
|
||||||
api.GET("config/time", srv.controllerGetConfigTime)
|
|
||||||
api.POST("config/time", srv.controllerSetConfigTime)
|
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
uiGroup := router.Group("/", middleware.Compression(
|
uiGroup := router.Group("/", middleware.Compression(
|
||||||
srv.config.Server.Compression.Gzip,
|
srv.config.Server.Compression.Gzip,
|
||||||
srv.config.Server.Compression.Brotli),
|
srv.config.Server.Compression.Brotli),
|
||||||
)
|
)
|
||||||
ui.Register(uiGroup, srv.nZones)
|
// ui.Register(uiGroup)
|
||||||
swagger.Register(uiGroup)
|
swagger.Register(uiGroup)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
@ -115,496 +98,12 @@ func (srv *WebUIServer) Run() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get TSGRain info
|
|
||||||
nZones, err := srv.rpc.GetNZones()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
srv.nZones = nZones
|
|
||||||
|
|
||||||
go srv.rpc.StreamTasks(srv.streamer)
|
go srv.rpc.StreamTasks(srv.streamer)
|
||||||
|
|
||||||
router := srv.getRouter()
|
router := srv.getRouter()
|
||||||
|
|
||||||
err = router.Run(fmt.Sprintf("%s:%d",
|
return router.Run(fmt.Sprintf("%s:%d",
|
||||||
srv.config.Server.Address, srv.config.Server.Port))
|
srv.config.Server.Address, srv.config.Server.Port))
|
||||||
|
|
||||||
_ = srv.rpc.Stop()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation POST /task/manual startManualTask
|
|
||||||
//
|
|
||||||
// Starte/stoppe manuell eine neue Bewässerungsaufgabe
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - in: body
|
|
||||||
// name: taskRequest
|
|
||||||
// schema:
|
|
||||||
// type: object
|
|
||||||
// properties:
|
|
||||||
// zone_id:
|
|
||||||
// description: Nummer der Bewässerungszone
|
|
||||||
// type: integer
|
|
||||||
// duration:
|
|
||||||
// description: "Bewässerungsdauer in Sekunden (0: Standarddauer)"
|
|
||||||
// type: integer
|
|
||||||
// queuing:
|
|
||||||
// description: |
|
|
||||||
// Aufgabe in die Warteschlange einreihen,
|
|
||||||
// wenn sie nicht sofort ausgeführt werden kann.
|
|
||||||
// type: boolean
|
|
||||||
// cancelling:
|
|
||||||
// description: |
|
|
||||||
// Aufgabe stoppen/aus der Warteschlange entfernen,
|
|
||||||
// wenn sie bereits läuft oder sich in der Warteschlange befindet.
|
|
||||||
// type: boolean
|
|
||||||
// required:
|
|
||||||
// - zone_id
|
|
||||||
// - duration
|
|
||||||
// - queuing
|
|
||||||
// - cancelling
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Bewässerungsaufgabe erfolgreich gestarted/gestoppt
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 400:
|
|
||||||
// description: Bewässerungsaufgabe läuft schon und kann nicht gestoppt werden.
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerStartTask(c *gin.Context) {
|
|
||||||
var params struct {
|
|
||||||
ZoneID int32 `json:"zone_id"`
|
|
||||||
Duration int32 `json:"duration"`
|
|
||||||
Queuing bool `json:"queuing"`
|
|
||||||
Cancelling bool `json:"cancelling"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(¶ms); err != nil {
|
|
||||||
log.Error().Err(err).Msg("StartTask input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !srv.validateZoneID(params.ZoneID) {
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid zone_id")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := srv.rpc.StartManualTask(
|
|
||||||
params.ZoneID, params.Duration, params.Queuing, params.Cancelling)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("StartManualTask")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error starting task")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
statusCode := 200
|
|
||||||
if !res.Started && !res.Stopped {
|
|
||||||
statusCode = 400
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(statusCode, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation GET /jobs getJobs
|
|
||||||
//
|
|
||||||
// Rufe alle gespeicherten Zeitpläne ab.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// produces: [application/json]
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Job-Liste
|
|
||||||
// schema:
|
|
||||||
// "$ref": "#/definitions/JobList"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// "$ref": "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerGetJobs(c *gin.Context) {
|
|
||||||
jobs, err := srv.rpc.GetJobs()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetJobs")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error getting jobs")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, jobs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation GET /job getJob
|
|
||||||
//
|
|
||||||
// Rufe einen gespeicherten Zeitplan ab.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - name: id
|
|
||||||
// in: query
|
|
||||||
// description: Job-ID
|
|
||||||
// type: integer
|
|
||||||
// required: true
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Job
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Job"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerGetJob(c *gin.Context) {
|
|
||||||
var params struct {
|
|
||||||
Id int32 `uri:"id" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.ShouldBindUri(¶ms); err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetJob input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
job, err := srv.rpc.GetJob(params.Id)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetJob")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error getting job")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, job)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation POST /job createJob
|
|
||||||
//
|
|
||||||
// Erstelle einen neuen Zeitplan.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - name: job
|
|
||||||
// in: body
|
|
||||||
// description: Neuer Job
|
|
||||||
// schema:
|
|
||||||
// $ref: '#/definitions/NewJob'
|
|
||||||
// required: true
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/JobID"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerCreateJob(c *gin.Context) {
|
|
||||||
var newJob model.NewJob
|
|
||||||
if err := c.ShouldBindJSON(&newJob); err != nil {
|
|
||||||
log.Error().Err(err).Msg("CreateJob input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
jobId, err := srv.rpc.CreateJob(newJob)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("CreateJob")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error creating job")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, model.JobID{Id: jobId})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation PUT /job updateJob
|
|
||||||
//
|
|
||||||
// Aktualisiere einen gespeicherten Zeitplan.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - name: job
|
|
||||||
// in: body
|
|
||||||
// description: Aktualisierter Job
|
|
||||||
// schema:
|
|
||||||
// $ref: '#/definitions/Job'
|
|
||||||
// required: true
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerUpdateJob(c *gin.Context) {
|
|
||||||
var job model.Job
|
|
||||||
if err := c.ShouldBindJSON(&job); err != nil {
|
|
||||||
log.Error().Err(err).Msg("UpdateJob input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.rpc.UpdateJob(job)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("UpdateJob")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error updating job")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStatus(c, http.StatusOK, "updated job")
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation DELETE /job deleteJob
|
|
||||||
//
|
|
||||||
// Lösche einen gespeicherten Zeitplan.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - name: id
|
|
||||||
// in: body
|
|
||||||
// schema:
|
|
||||||
// type: integer
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerDeleteJob(c *gin.Context) {
|
|
||||||
var jobId model.JobID
|
|
||||||
if err := c.ShouldBindJSON(&jobId); err != nil {
|
|
||||||
log.Error().Err(err).Msg("DeleteJob input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.rpc.DeleteJob(jobId.Id)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("DeleteJob")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error deleting job")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStatus(c, http.StatusOK, "deleted job")
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation GET /config/auto getAutoMode
|
|
||||||
//
|
|
||||||
// Rufe ab, ob der Automatikmodus aktiviert ist.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// produces: [application/json]
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Status des Automatikmodus
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/AutoMode"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerGetAutoMode(c *gin.Context) {
|
|
||||||
state, err := srv.rpc.GetAutoMode()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetAutoMode")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error getting autoMode")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, model.AutoMode{State: state})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation POST /config/auto setAutoMode
|
|
||||||
//
|
|
||||||
// Automatikmodus aktivieren/deaktivieren
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - in: body
|
|
||||||
// name: state
|
|
||||||
// description: Zustand des Automatikmodus
|
|
||||||
// schema:
|
|
||||||
// type: object
|
|
||||||
// properties:
|
|
||||||
// state:
|
|
||||||
// type: boolean
|
|
||||||
// required:
|
|
||||||
// - state
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerSetAutoMode(c *gin.Context) {
|
|
||||||
var autoMode model.AutoMode
|
|
||||||
if err := c.ShouldBindJSON(&autoMode); err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetAutoMode input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.rpc.SetAutoMode(autoMode.State)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetAutoMode")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error setting autoMode")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStatus(c, http.StatusOK, "set autoMode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation GET /config/time getConfigTime
|
|
||||||
//
|
|
||||||
// Rufe die aktuelle Systemzeit/Zeitzone ab
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// produces: [application/json]
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Aktuelle Systemzeit/Zeitzone
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/ConfigTime"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerGetConfigTime(c *gin.Context) {
|
|
||||||
configTime, err := srv.rpc.GetConfigTime()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetConfigTime")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error setting configTime")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, configTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation POST /config/time setConfigTime
|
|
||||||
//
|
|
||||||
// Automatikmodus aktivieren/deaktivieren
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - in: body
|
|
||||||
// name: configTime
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/ConfigTime"
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerSetConfigTime(c *gin.Context) {
|
|
||||||
var configTime model.ConfigTime
|
|
||||||
if err := c.ShouldBindJSON(&configTime); err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetConfigTime input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.rpc.SetConfigTime(configTime)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetConfigTime")
|
|
||||||
writeStatus(c, http.StatusInternalServerError, "error setting configTime")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStatus(c, http.StatusOK, "set configTime")
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation GET /config/defaultIrrigationTime getDefaultIrrigationTime
|
|
||||||
//
|
|
||||||
// Rufe die Standardzeit bei manueller Bewässerung ab.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// produces: [application/json]
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: Manuelle Bewässerungszeit in Sekunden
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/DefaultIrrigationTime"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerGetDefaultIrrigationTime(c *gin.Context) {
|
|
||||||
defaultIrrigationTime, err := srv.rpc.GetDefaultIrrigationTime()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("GetDefaultIrrigationTime")
|
|
||||||
writeStatus(c, http.StatusInternalServerError,
|
|
||||||
"error getting defaultIrrigationTime")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, defaultIrrigationTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:operation POST /config/defaultIrrigationTime setDefaultIrrigationTime
|
|
||||||
//
|
|
||||||
// Setze die die Standardzeit bei manueller Bewässerung.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// consumes: [application/json]
|
|
||||||
// produces: [application/json]
|
|
||||||
// parameters:
|
|
||||||
// - in: body
|
|
||||||
// name: defaultIrrigationTime
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/DefaultIrrigationTime"
|
|
||||||
// responses:
|
|
||||||
// 200:
|
|
||||||
// description: OK
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/StatusMessage"
|
|
||||||
// 500:
|
|
||||||
// description: Serverfehler
|
|
||||||
// schema:
|
|
||||||
// $ref: "#/definitions/Error"
|
|
||||||
func (srv *WebUIServer) controllerSetDefaultIrrigationTime(c *gin.Context) {
|
|
||||||
var defaultIrrigationTime model.DefaultIrrigationTime
|
|
||||||
if err := c.ShouldBindJSON(&defaultIrrigationTime); err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetDefaultIrrigationTime input error")
|
|
||||||
writeStatus(c, http.StatusBadRequest, "invalid input data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.rpc.SetDefaultIrrigationTime(defaultIrrigationTime.Time)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("SetDefaultIrrigationTime")
|
|
||||||
writeStatus(c, http.StatusInternalServerError,
|
|
||||||
"error setting defaultIrrigationTime")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStatus(c, http.StatusOK, "set defaultIrrigationTime")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// controllerError throws an error for testing
|
// controllerError throws an error for testing
|
||||||
|
@ -617,13 +116,9 @@ func (srv *WebUIServer) controllerPanic(c *gin.Context) {
|
||||||
panic(errors.New("panic message"))
|
panic(errors.New("panic message"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *WebUIServer) validateZoneID(zoneId int32) bool {
|
func writeStatus(c *gin.Context, success bool, msg string) {
|
||||||
return 0 < zoneId && zoneId <= srv.nZones
|
c.JSON(http.StatusOK, model.StatusMessage{
|
||||||
}
|
Success: success,
|
||||||
|
|
||||||
func writeStatus(c *gin.Context, code int, msg string) {
|
|
||||||
c.JSON(code, model.StatusMessage{
|
|
||||||
Success: code == http.StatusOK,
|
|
||||||
Msg: msg,
|
Msg: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>TSGRain WebUI API documentation</title>
|
<title>SEBRAUC API documentation</title>
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
|
@ -1,38 +1,4 @@
|
||||||
definitions:
|
definitions:
|
||||||
AutoMode:
|
|
||||||
description: Zustand des Automatikmodus
|
|
||||||
properties:
|
|
||||||
state:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- state
|
|
||||||
title: AutoMode model
|
|
||||||
type: object
|
|
||||||
ConfigTime:
|
|
||||||
description: Aktuelle Systemzeit/Zeitzone
|
|
||||||
properties:
|
|
||||||
time:
|
|
||||||
description: Aktuelle Systemzeit
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
timezone:
|
|
||||||
description: Aktuelle Zeitzone
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- time
|
|
||||||
- timezone
|
|
||||||
title: ConfigTime model
|
|
||||||
type: object
|
|
||||||
DefaultIrrigationTime:
|
|
||||||
description: Manuelle Bewässerungszeit in Sekunden
|
|
||||||
properties:
|
|
||||||
time:
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- time
|
|
||||||
title: DefaultIrrigationTime model
|
|
||||||
type: object
|
|
||||||
Error:
|
Error:
|
||||||
description: The Error contains error relevant information.
|
description: The Error contains error relevant information.
|
||||||
properties:
|
properties:
|
||||||
|
@ -55,94 +21,6 @@ definitions:
|
||||||
- msg
|
- msg
|
||||||
title: Error model
|
title: Error model
|
||||||
type: object
|
type: object
|
||||||
Job:
|
|
||||||
description: Job stellt einen Bewässerungszeitplan dar.
|
|
||||||
properties:
|
|
||||||
date:
|
|
||||||
description: Zritstempel des Bewässerungsjobs
|
|
||||||
example: 1643756107
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
duration:
|
|
||||||
description: Bewässerungsdauer in Sekunden
|
|
||||||
example: 300
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
enable:
|
|
||||||
description: Job aktiviert?
|
|
||||||
type: boolean
|
|
||||||
id:
|
|
||||||
description: ID des Jobs
|
|
||||||
example: 1
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
repeat:
|
|
||||||
description: Job täglich wiederholen
|
|
||||||
type: boolean
|
|
||||||
zones:
|
|
||||||
description: Zu bewässernde Zonen
|
|
||||||
example:
|
|
||||||
- 2
|
|
||||||
- 3
|
|
||||||
items:
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
- date
|
|
||||||
- duration
|
|
||||||
- zones
|
|
||||||
- enable
|
|
||||||
- repeat
|
|
||||||
title: Job model
|
|
||||||
type: object
|
|
||||||
JobID:
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
JobList:
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Job"
|
|
||||||
type: array
|
|
||||||
NewJob:
|
|
||||||
description: NewJob stellt einen zu erstellenden Bewässerungszeitplan dar.
|
|
||||||
properties:
|
|
||||||
date:
|
|
||||||
description: Zritstempel des Bewässerungsjobs
|
|
||||||
example: 1643756107
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
duration:
|
|
||||||
description: Bewässerungsdauer in Sekunden
|
|
||||||
example: 300
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
enable:
|
|
||||||
description: Job aktiviert?
|
|
||||||
type: boolean
|
|
||||||
repeat:
|
|
||||||
description: Job täglich wiederholen
|
|
||||||
type: boolean
|
|
||||||
zones:
|
|
||||||
description: Zu bewässernde Zonen
|
|
||||||
example:
|
|
||||||
- 2
|
|
||||||
- 3
|
|
||||||
items:
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- date
|
|
||||||
- duration
|
|
||||||
- zones
|
|
||||||
- enable
|
|
||||||
- repeat
|
|
||||||
title: NewJob model
|
|
||||||
type: object
|
|
||||||
StatusMessage:
|
StatusMessage:
|
||||||
description: StatusMessage contains the status of an operation.
|
description: StatusMessage contains the status of an operation.
|
||||||
properties:
|
properties:
|
||||||
|
@ -158,345 +36,13 @@ definitions:
|
||||||
- msg
|
- msg
|
||||||
title: StatusMessage model
|
title: StatusMessage model
|
||||||
type: object
|
type: object
|
||||||
Task:
|
|
||||||
description: Task stellt eine Bewässerungsaufgabe dar.
|
|
||||||
properties:
|
|
||||||
datetime_finished:
|
|
||||||
description: Zeitstempel, wann die Bewässerung beendet sein wird
|
|
||||||
example: 1643756407
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
datetime_started:
|
|
||||||
description: Zeitstempel, wann die Bewässerung gestartet wurde
|
|
||||||
example: 1643756107
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
duration:
|
|
||||||
description: Bewässerungsdauer in Sekunden
|
|
||||||
example: 300
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
source:
|
|
||||||
description: "Quelle der Bewässerungsaufgabe (0: MANUAL, 1: SCHEDULE)"
|
|
||||||
example: 0
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
zone_id:
|
|
||||||
description: Nummer der Bewässerungszone
|
|
||||||
example: 2
|
|
||||||
format: int32
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- source
|
|
||||||
- zone_id
|
|
||||||
- duration
|
|
||||||
- datetime_started
|
|
||||||
- datetime_finished
|
|
||||||
title: Task model
|
|
||||||
type: object
|
|
||||||
TaskList:
|
|
||||||
description: TaskList ist eine Liste der aktuell laufenden Bewässerungsaufgaben.
|
|
||||||
properties:
|
|
||||||
auto_mode:
|
|
||||||
description: Automatikmodus aktiv
|
|
||||||
type: boolean
|
|
||||||
now:
|
|
||||||
description: Aktueller Zeitstempel
|
|
||||||
example: 1643756107
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
tasks:
|
|
||||||
description: Liste der laufenden Tasks
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Task"
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- now
|
|
||||||
- tasks
|
|
||||||
- auto_mode
|
|
||||||
title: TaskList model
|
|
||||||
type: object
|
|
||||||
TaskRequestResult:
|
|
||||||
description: TaskRequestResult wird beim Starten eines Tasks zurückgegeben
|
|
||||||
properties:
|
|
||||||
Started:
|
|
||||||
type: boolean
|
|
||||||
Stopped:
|
|
||||||
type: boolean
|
|
||||||
title: TaskRequestResult model
|
|
||||||
type: object
|
|
||||||
info:
|
info:
|
||||||
description: REST API for the TSGRain WebUI
|
description: REST API for the TSGRain WebUI
|
||||||
license:
|
license:
|
||||||
name: MIT
|
name: MIT
|
||||||
title: TSGRain WebUI
|
title: TSGRain WebUI
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
paths:
|
paths: {}
|
||||||
/config/auto:
|
|
||||||
get:
|
|
||||||
operationId: getAutoMode
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Status des Automatikmodus
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/AutoMode"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Rufe ab, ob der Automatikmodus aktiviert ist.
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Automatikmodus aktivieren/deaktivieren
|
|
||||||
operationId: setAutoMode
|
|
||||||
parameters:
|
|
||||||
- description: Zustand des Automatikmodus
|
|
||||||
in: body
|
|
||||||
name: state
|
|
||||||
schema:
|
|
||||||
properties:
|
|
||||||
state:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- state
|
|
||||||
type: object
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
/config/defaultIrrigationTime:
|
|
||||||
get:
|
|
||||||
operationId: getDefaultIrrigationTime
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Manuelle Bewässerungszeit in Sekunden
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/DefaultIrrigationTime"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Rufe die Standardzeit bei manueller Bewässerung ab.
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
operationId: setDefaultIrrigationTime
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: defaultIrrigationTime
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/DefaultIrrigationTime"
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Setze die die Standardzeit bei manueller Bewässerung.
|
|
||||||
/config/time:
|
|
||||||
get:
|
|
||||||
description: Rufe die aktuelle Systemzeit/Zeitzone ab
|
|
||||||
operationId: getConfigTime
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Aktuelle Systemzeit/Zeitzone
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ConfigTime"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Automatikmodus aktivieren/deaktivieren
|
|
||||||
operationId: setConfigTime
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: configTime
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/ConfigTime"
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
/job:
|
|
||||||
delete:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
operationId: deleteJob
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: id
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Lösche einen gespeicherten Zeitplan.
|
|
||||||
get:
|
|
||||||
operationId: getJob
|
|
||||||
parameters:
|
|
||||||
- description: Job-ID
|
|
||||||
in: query
|
|
||||||
name: id
|
|
||||||
required: true
|
|
||||||
type: integer
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Job
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Job"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Rufe einen gespeicherten Zeitplan ab.
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
operationId: createJob
|
|
||||||
parameters:
|
|
||||||
- description: Neuer Job
|
|
||||||
in: body
|
|
||||||
name: job
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/NewJob"
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/JobID"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Erstelle einen neuen Zeitplan.
|
|
||||||
put:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
operationId: updateJob
|
|
||||||
parameters:
|
|
||||||
- description: Aktualisierter Job
|
|
||||||
in: body
|
|
||||||
name: job
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Job"
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Aktualisiere einen gespeicherten Zeitplan.
|
|
||||||
/jobs:
|
|
||||||
get:
|
|
||||||
operationId: getJobs
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Job-Liste
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/JobList"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
summary: Rufe alle gespeicherten Zeitpläne ab.
|
|
||||||
/task/manual:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Starte/stoppe manuell eine neue Bewässerungsaufgabe
|
|
||||||
operationId: startManualTask
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: taskRequest
|
|
||||||
schema:
|
|
||||||
properties:
|
|
||||||
cancelling:
|
|
||||||
description: |
|
|
||||||
Aufgabe stoppen/aus der Warteschlange entfernen,
|
|
||||||
wenn sie bereits läuft oder sich in der Warteschlange befindet.
|
|
||||||
type: boolean
|
|
||||||
duration:
|
|
||||||
description: "Bewässerungsdauer in Sekunden (0: Standarddauer)"
|
|
||||||
type: integer
|
|
||||||
queuing:
|
|
||||||
description: |
|
|
||||||
Aufgabe in die Warteschlange einreihen,
|
|
||||||
wenn sie nicht sofort ausgeführt werden kann.
|
|
||||||
type: boolean
|
|
||||||
zone_id:
|
|
||||||
description: Nummer der Bewässerungszone
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- zone_id
|
|
||||||
- duration
|
|
||||||
- queuing
|
|
||||||
- cancelling
|
|
||||||
type: object
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Bewässerungsaufgabe erfolgreich gestarted/gestoppt
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/StatusMessage"
|
|
||||||
"400":
|
|
||||||
description: Bewässerungsaufgabe läuft schon und kann nicht gestoppt werden.
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
"500":
|
|
||||||
description: Serverfehler
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Error"
|
|
||||||
schemes:
|
schemes:
|
||||||
- http
|
- http
|
||||||
- https
|
- https
|
||||||
|
|
|
@ -335,9 +335,8 @@ type TaskList struct {
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Tasks []*Task `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"`
|
Tasks []*Task `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"`
|
||||||
Now *Timestamp `protobuf:"bytes,2,opt,name=now,proto3" json:"now,omitempty"`
|
Now *Timestamp `protobuf:"bytes,2,opt,name=now,proto3" json:"now,omitempty"`
|
||||||
AutoMode bool `protobuf:"varint,3,opt,name=auto_mode,json=autoMode,proto3" json:"auto_mode,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TaskList) Reset() {
|
func (x *TaskList) Reset() {
|
||||||
|
@ -386,13 +385,6 @@ func (x *TaskList) GetNow() *Timestamp {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TaskList) GetAutoMode() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.AutoMode
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type Job struct {
|
type Job struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -667,91 +659,89 @@ var file_proto_tsgrain_proto_rawDesc = []byte{
|
||||||
0x12, 0x37, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x6e,
|
0x12, 0x37, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x6e,
|
||||||
0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69,
|
0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69,
|
||||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d,
|
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d,
|
||||||
0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x22, 0x62, 0x0a, 0x08, 0x54, 0x61, 0x73,
|
0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x22, 0x45, 0x0a, 0x08, 0x54, 0x61, 0x73,
|
||||||
0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01,
|
0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01,
|
||||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73,
|
||||||
0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x6e, 0x6f, 0x77,
|
0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x6e, 0x6f, 0x77,
|
||||||
0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20,
|
0x22, 0x97, 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x97, 0x01,
|
0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65,
|
||||||
0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||||
0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20,
|
0x6d, 0x70, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61,
|
||||||
0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x04, 0x20,
|
||||||
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
0x03, 0x28, 0x05, 0x52, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e,
|
||||||
0x6e, 0x12, 0x14, 0x0a, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05,
|
0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62,
|
||||||
0x52, 0x05, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01,
|
||||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12,
|
0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x22, 0x17, 0x0a, 0x05, 0x4a, 0x6f,
|
||||||
0x16, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x62, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||||
0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x22, 0x17, 0x0a, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44,
|
0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18,
|
||||||
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64,
|
0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4a,
|
||||||
0x22, 0x23, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x04, 0x6a,
|
0x6f, 0x62, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x52,
|
0x69, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69,
|
||||||
0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54,
|
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||||
0x69, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18,
|
0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x70, 0x52, 0x08, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74,
|
0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x2a, 0x26, 0x0a, 0x0a, 0x54, 0x61,
|
||||||
0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74,
|
0x73, 0x6b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55,
|
||||||
0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x2a, 0x26, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x53,
|
0x41, 0x4c, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45,
|
||||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10,
|
0x10, 0x01, 0x32, 0xc7, 0x06, 0x0a, 0x07, 0x54, 0x53, 0x47, 0x52, 0x61, 0x69, 0x6e, 0x12, 0x2f,
|
||||||
0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x01, 0x32,
|
0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0c, 0x2e, 0x54, 0x61,
|
||||||
0xc7, 0x06, 0x0a, 0x07, 0x54, 0x53, 0x47, 0x52, 0x61, 0x69, 0x6e, 0x12, 0x2f, 0x0a, 0x09, 0x53,
|
0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x54, 0x61, 0x73, 0x6b,
|
||||||
0x74, 0x61, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0c, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52,
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71,
|
0x2f, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x08,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00,
|
||||||
|
0x12, 0x34, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12,
|
||||||
|
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4c, 0x69,
|
||||||
|
0x73, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x1b, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||||
|
0x4a, 0x6f, 0x62, 0x12, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x1a, 0x06, 0x2e, 0x4a, 0x6f, 0x62, 0x49,
|
||||||
|
0x44, 0x22, 0x00, 0x12, 0x18, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x12, 0x06, 0x2e,
|
||||||
|
0x4a, 0x6f, 0x62, 0x49, 0x44, 0x1a, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x22, 0x00, 0x12, 0x2d, 0x0a,
|
||||||
|
0x07, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x1a, 0x09, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x34, 0x0a,
|
0x1a, 0x08, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x09,
|
||||||
0x0b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x16, 0x2e, 0x67,
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x1a,
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x22,
|
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x09, 0x44, 0x65, 0x6c,
|
||||||
0x00, 0x30, 0x01, 0x12, 0x1b, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62,
|
0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x06, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x1a, 0x16,
|
||||||
0x12, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x1a, 0x06, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0x00,
|
|
||||||
0x12, 0x18, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x12, 0x06, 0x2e, 0x4a, 0x6f, 0x62,
|
|
||||||
0x49, 0x44, 0x1a, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x07, 0x47, 0x65,
|
|
||||||
0x74, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x08, 0x2e,
|
|
||||||
0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x09, 0x55, 0x70, 0x64,
|
|
||||||
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x1a, 0x16, 0x2e, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
|
||||||
0x4a, 0x6f, 0x62, 0x12, 0x06, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
|
|
||||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
|
||||||
0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f,
|
|
||||||
0x4d, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42,
|
|
||||||
0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0b, 0x53, 0x65,
|
|
||||||
0x74, 0x41, 0x75, 0x74, 0x6f, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c,
|
|
||||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
|
|
||||||
0x36, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x69, 0x6d, 0x65,
|
|
||||||
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
|
||||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x43, 0x6f,
|
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0b, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,
|
|
||||||
0x51, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x49, 0x72, 0x72,
|
|
||||||
0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
|
||||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
|
||||||
0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
|
||||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
|
||||||
0x22, 0x00, 0x12, 0x51, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
|
|
||||||
0x49, 0x72, 0x72, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x41,
|
||||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
0x75, 0x74, 0x6f, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4e, 0x5a, 0x6f, 0x6e,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||||
0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f,
|
0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74,
|
0x0b, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x2e, 0x67,
|
||||||
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x63, 0x6f, 0x64,
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42,
|
||||||
0x65, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x61, 0x64, 0x65, 0x76, 0x2e, 0x64, 0x65, 0x2f, 0x54, 0x53,
|
0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x47, 0x52, 0x61, 0x69, 0x6e, 0x2f, 0x57, 0x65, 0x62, 0x55, 0x49, 0x2f, 0x73, 0x72, 0x63, 0x2f,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x74, 0x73, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
0x22, 0x00, 0x12, 0x36, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54,
|
||||||
0x74, 0x6f, 0x33,
|
0x69, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x43, 0x6f,
|
||||||
|
0x6e, 0x66, 0x69, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0d, 0x53, 0x65,
|
||||||
|
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0b, 0x2e, 0x43, 0x6f,
|
||||||
|
0x6e, 0x66, 0x69, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x22, 0x00, 0x12, 0x51, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
|
||||||
|
0x49, 0x72, 0x72, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16,
|
||||||
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61,
|
||||||
|
0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61,
|
||||||
|
0x75, 0x6c, 0x74, 0x49, 0x72, 0x72, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d,
|
||||||
|
0x65, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16,
|
||||||
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4e,
|
||||||
|
0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
|
0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e,
|
||||||
|
0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x61, 0x64, 0x65, 0x76, 0x2e, 0x64, 0x65,
|
||||||
|
0x2f, 0x54, 0x53, 0x47, 0x52, 0x61, 0x69, 0x6e, 0x2f, 0x57, 0x65, 0x62, 0x55, 0x49, 0x2f, 0x73,
|
||||||
|
0x72, 0x63, 0x2f, 0x74, 0x73, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x70, 0x63, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -7,14 +7,12 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/model"
|
|
||||||
tsgrain_grpc "code.thetadev.de/TSGRain/WebUI/src/tsgrain_rpc/proto"
|
tsgrain_grpc "code.thetadev.de/TSGRain/WebUI/src/tsgrain_rpc/proto"
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/util"
|
"code.thetadev.de/TSGRain/WebUI/src/util"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RPCClient struct {
|
type RPCClient struct {
|
||||||
|
@ -52,78 +50,6 @@ func (c *RPCClient) Stop() error {
|
||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapTask(pbTask *tsgrain_grpc.Task) model.Task {
|
|
||||||
task := model.Task{
|
|
||||||
Source: int32(pbTask.Source),
|
|
||||||
ZoneId: pbTask.ZoneId,
|
|
||||||
Duration: pbTask.Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
if pbTask.DatetimeStarted != nil {
|
|
||||||
task.DatetimeStarted = &pbTask.DatetimeStarted.Seconds
|
|
||||||
}
|
|
||||||
if pbTask.DatetimeFinished != nil {
|
|
||||||
task.DatetimeFinished = &pbTask.DatetimeFinished.Seconds
|
|
||||||
}
|
|
||||||
return task
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapTaskList(pbTaskList *tsgrain_grpc.TaskList) model.TaskList {
|
|
||||||
taskList := model.TaskList{
|
|
||||||
Now: pbTaskList.Now.Seconds,
|
|
||||||
Tasks: []model.Task{},
|
|
||||||
AutoMode: pbTaskList.AutoMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pbTask := range pbTaskList.Tasks {
|
|
||||||
if pbTask != nil {
|
|
||||||
taskList.Tasks = append(taskList.Tasks, mapTask(pbTask))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return taskList
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) StartManualTask(
|
|
||||||
zone_id int32, duration int32, queuing bool, cancelling bool) (
|
|
||||||
model.TaskRequestResult, error) {
|
|
||||||
res, err := c.tsgrain.StartTask(c.ctx, &tsgrain_grpc.TaskRequest{
|
|
||||||
Source: tsgrain_grpc.TaskSource_MANUAL,
|
|
||||||
ZoneId: zone_id,
|
|
||||||
Duration: duration,
|
|
||||||
Queuing: queuing,
|
|
||||||
Cancelling: cancelling,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return model.TaskRequestResult{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.TaskRequestResult{
|
|
||||||
Started: res.Started,
|
|
||||||
Stopped: res.Stopped,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetTasks() (model.TaskList, error) {
|
|
||||||
pbTaskList, err := c.tsgrain.GetTasks(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return model.TaskList{}, err
|
|
||||||
}
|
|
||||||
return mapTaskList(pbTaskList), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func broadcastTaskList(bc util.Broadcaster, pbTaskList *tsgrain_grpc.TaskList) error {
|
|
||||||
taskList := mapTaskList(pbTaskList)
|
|
||||||
|
|
||||||
taskListJson, err := json.Marshal(taskList)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bc.Broadcast(taskListJson)
|
|
||||||
log.Debug().RawJSON("task_list", taskListJson).Msg("TaskList received")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) streamTasks(bc util.Broadcaster) error {
|
func (c *RPCClient) streamTasks(bc util.Broadcaster) error {
|
||||||
stream, err := c.tsgrain.StreamTasks(c.ctx, &emptypb.Empty{})
|
stream, err := c.tsgrain.StreamTasks(c.ctx, &emptypb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,25 +57,25 @@ func (c *RPCClient) streamTasks(bc util.Broadcaster) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pbTaskList, err := stream.Recv()
|
taskList, err := stream.Recv()
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
_ = broadcastTaskList(bc, pbTaskList)
|
taskListJson, err := json.Marshal(taskList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bc.Broadcast(taskListJson)
|
||||||
|
log.Debug().RawJSON("task_list", taskListJson).Msg("TaskList received")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RPCClient) StreamTasks(bc util.Broadcaster) {
|
func (c *RPCClient) StreamTasks(bc util.Broadcaster) {
|
||||||
// Get initial state
|
|
||||||
pbTaskList, err := c.tsgrain.GetTasks(c.ctx, &emptypb.Empty{})
|
|
||||||
if err == nil {
|
|
||||||
_ = broadcastTaskList(bc, pbTaskList)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep stream running if it errors
|
// Keep stream running if it errors
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -164,142 +90,3 @@ func (c *RPCClient) StreamTasks(bc util.Broadcaster) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RPCClient) CreateJob(job model.NewJob) (int32, error) {
|
|
||||||
pbJob := &tsgrain_grpc.Job{
|
|
||||||
Date: &tsgrain_grpc.Timestamp{Seconds: job.Date},
|
|
||||||
Duration: job.Duration,
|
|
||||||
Zones: job.Zones,
|
|
||||||
Enable: job.Enable,
|
|
||||||
Repeat: job.Repeat,
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.tsgrain.CreateJob(c.ctx, pbJob)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetJobs() (model.JobList, error) {
|
|
||||||
jobs := []model.Job{}
|
|
||||||
|
|
||||||
res, err := c.tsgrain.GetJobs(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return jobs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pbJob := range res.Jobs {
|
|
||||||
job := model.Job{
|
|
||||||
Id: pbJob.Id,
|
|
||||||
Date: pbJob.Date.Seconds,
|
|
||||||
Duration: pbJob.Duration,
|
|
||||||
Zones: pbJob.Zones,
|
|
||||||
Enable: pbJob.Enable,
|
|
||||||
Repeat: pbJob.Repeat,
|
|
||||||
}
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
}
|
|
||||||
|
|
||||||
return jobs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetJob(id int32) (model.Job, error) {
|
|
||||||
res, err := c.tsgrain.GetJob(c.ctx, &tsgrain_grpc.JobID{Id: id})
|
|
||||||
if err != nil {
|
|
||||||
return model.Job{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
job := model.Job{
|
|
||||||
Id: res.Id,
|
|
||||||
Date: res.Date.Seconds,
|
|
||||||
Duration: res.Duration,
|
|
||||||
Zones: res.Zones,
|
|
||||||
Enable: res.Enable,
|
|
||||||
Repeat: res.Repeat,
|
|
||||||
}
|
|
||||||
return job, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) UpdateJob(job model.Job) error {
|
|
||||||
pbJob := tsgrain_grpc.Job{
|
|
||||||
Id: job.Id,
|
|
||||||
Date: &tsgrain_grpc.Timestamp{Seconds: job.Date},
|
|
||||||
Duration: job.Duration,
|
|
||||||
Zones: job.Zones,
|
|
||||||
Enable: job.Enable,
|
|
||||||
Repeat: job.Repeat,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := c.tsgrain.UpdateJob(c.ctx, &pbJob)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) DeleteJob(job_id int32) error {
|
|
||||||
_, err := c.tsgrain.DeleteJob(c.ctx, &tsgrain_grpc.JobID{Id: job_id})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetAutoMode() (bool, error) {
|
|
||||||
res, err := c.tsgrain.GetAutoMode(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) SetAutoMode(state bool) error {
|
|
||||||
_, err := c.tsgrain.SetAutoMode(c.ctx, wrapperspb.Bool(state))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetConfigTime() (model.ConfigTime, error) {
|
|
||||||
configTime, err := c.tsgrain.GetConfigTime(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return model.ConfigTime{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.ConfigTime{
|
|
||||||
Time: configTime.Datetime.Seconds,
|
|
||||||
Timezone: configTime.Timezone,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) SetConfigTime(configTime model.ConfigTime) error {
|
|
||||||
_, err := c.tsgrain.SetConfigTime(c.ctx, &tsgrain_grpc.ConfigTime{
|
|
||||||
Datetime: &tsgrain_grpc.Timestamp{Seconds: configTime.Time},
|
|
||||||
Timezone: configTime.Timezone,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) SetSystemTimezone(timezone string) error {
|
|
||||||
_, err := c.tsgrain.SetConfigTime(c.ctx, &tsgrain_grpc.ConfigTime{
|
|
||||||
Timezone: timezone,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetDefaultIrrigationTime() (int32, error) {
|
|
||||||
res, err := c.tsgrain.GetDefaultIrrigationTime(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) SetDefaultIrrigationTime(defaultTime int32) error {
|
|
||||||
_, err := c.tsgrain.SetDefaultIrrigationTime(c.ctx, wrapperspb.Int32(defaultTime))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RPCClient) GetNZones() (int32, error) {
|
|
||||||
val, err := c.tsgrain.GetNZones(c.ctx, &emptypb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return val.Value, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
VITE_API_HOST=127.0.0.1:8001
|
|
5
ui/.gitignore
vendored
5
ui/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
|
@ -1,4 +0,0 @@
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
tmp
|
|
||||||
.tmp
|
|
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>TSGRain</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You have to enable JavaScript to use TSGRain.</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script>
|
|
||||||
window.config = "%CONFIG%"
|
|
||||||
</script>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"name": "ui",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"serve": "vite preview",
|
|
||||||
"lint": "tsc",
|
|
||||||
"format": "prettier --write ../"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@mdi/js": "^6.5.95",
|
|
||||||
"axios": "^0.24.0",
|
|
||||||
"preact": "^10.5.15"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@preact/preset-vite": "^2.1.5",
|
|
||||||
"prettier": "^2.4.1",
|
|
||||||
"sass": "^1.43.4",
|
|
||||||
"typescript": "^4.5.2",
|
|
||||||
"vite": "^2.6.14",
|
|
||||||
"@babel/core": ">=7.12.10 <8.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
1242
ui/pnpm-lock.yaml
1242
ui/pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,10 +0,0 @@
|
||||||
import {Component} from "preact"
|
|
||||||
// import UpdaterView from "./Updater/UpdaterView"
|
|
||||||
// import logo from "../assets/logo.svg"
|
|
||||||
import {getConfig} from "../util/config"
|
|
||||||
|
|
||||||
export default class App extends Component {
|
|
||||||
render() {
|
|
||||||
return <div>{getConfig().version}</div>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
import {render} from "preact"
|
|
||||||
import App from "./components/app"
|
|
||||||
import "./style/index.scss"
|
|
||||||
|
|
||||||
render(<App />, document.getElementById("app")!)
|
|
1
ui/src/preact.d.ts
vendored
1
ui/src/preact.d.ts
vendored
|
@ -1 +0,0 @@
|
||||||
import JSX = preact.JSX
|
|
4
ui/src/tsgrain-client/.gitignore
vendored
4
ui/src/tsgrain-client/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
||||||
wwwroot/*.js
|
|
||||||
node_modules
|
|
||||||
typings
|
|
||||||
dist
|
|
|
@ -1 +0,0 @@
|
||||||
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
|
|
@ -1,23 +0,0 @@
|
||||||
# OpenAPI Generator Ignore
|
|
||||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
|
||||||
|
|
||||||
# Use this file to prevent files from being overwritten by the generator.
|
|
||||||
# The patterns follow closely to .gitignore or .dockerignore.
|
|
||||||
|
|
||||||
# As an example, the C# client generator defines ApiClient.cs.
|
|
||||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
|
||||||
#ApiClient.cs
|
|
||||||
|
|
||||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
|
||||||
#foo/*/qux
|
|
||||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
|
||||||
|
|
||||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
|
||||||
#foo/**/qux
|
|
||||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
|
||||||
|
|
||||||
# You can also negate patterns with an exclamation (!).
|
|
||||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
|
||||||
#docs/*.md
|
|
||||||
# Then explicitly reverse the ignore rule for a single file:
|
|
||||||
#!docs/README.md
|
|
|
@ -1,9 +0,0 @@
|
||||||
.gitignore
|
|
||||||
.npmignore
|
|
||||||
.openapi-generator-ignore
|
|
||||||
api.ts
|
|
||||||
base.ts
|
|
||||||
common.ts
|
|
||||||
configuration.ts
|
|
||||||
git_push.sh
|
|
||||||
index.ts
|
|
|
@ -1 +0,0 @@
|
||||||
5.3.1
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,74 +0,0 @@
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* TSGRain WebUI
|
|
||||||
* REST API for the TSGRain WebUI
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Configuration} from "./configuration"
|
|
||||||
// Some imports not used depending on template conditions
|
|
||||||
// @ts-ignore
|
|
||||||
import globalAxios, {AxiosPromise, AxiosInstance, AxiosRequestConfig} from "axios"
|
|
||||||
|
|
||||||
export const BASE_PATH = "http://localhost".replace(/\/+$/, "")
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const COLLECTION_FORMATS = {
|
|
||||||
csv: ",",
|
|
||||||
ssv: " ",
|
|
||||||
tsv: "\t",
|
|
||||||
pipes: "|",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface RequestArgs
|
|
||||||
*/
|
|
||||||
export interface RequestArgs {
|
|
||||||
url: string
|
|
||||||
options: AxiosRequestConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class BaseAPI
|
|
||||||
*/
|
|
||||||
export class BaseAPI {
|
|
||||||
protected configuration: Configuration | undefined
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
configuration?: Configuration,
|
|
||||||
protected basePath: string = BASE_PATH,
|
|
||||||
protected axios: AxiosInstance = globalAxios
|
|
||||||
) {
|
|
||||||
if (configuration) {
|
|
||||||
this.configuration = configuration
|
|
||||||
this.basePath = configuration.basePath || this.basePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class RequiredError
|
|
||||||
* @extends {Error}
|
|
||||||
*/
|
|
||||||
export class RequiredError extends Error {
|
|
||||||
name: "RequiredError" = "RequiredError"
|
|
||||||
constructor(public field: string, msg?: string) {
|
|
||||||
super(msg)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* TSGRain WebUI
|
|
||||||
* REST API for the TSGRain WebUI
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Configuration} from "./configuration"
|
|
||||||
import {RequiredError, RequestArgs} from "./base"
|
|
||||||
import {AxiosInstance, AxiosResponse} from "axios"
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DUMMY_BASE_URL = "https://example.com"
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const assertParamExists = function (
|
|
||||||
functionName: string,
|
|
||||||
paramName: string,
|
|
||||||
paramValue: unknown
|
|
||||||
) {
|
|
||||||
if (paramValue === null || paramValue === undefined) {
|
|
||||||
throw new RequiredError(
|
|
||||||
paramName,
|
|
||||||
`Required parameter ${paramName} was null or undefined when calling ${functionName}.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setApiKeyToObject = async function (
|
|
||||||
object: any,
|
|
||||||
keyParamName: string,
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
if (configuration && configuration.apiKey) {
|
|
||||||
const localVarApiKeyValue =
|
|
||||||
typeof configuration.apiKey === "function"
|
|
||||||
? await configuration.apiKey(keyParamName)
|
|
||||||
: await configuration.apiKey
|
|
||||||
object[keyParamName] = localVarApiKeyValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setBasicAuthToObject = function (
|
|
||||||
object: any,
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
if (configuration && (configuration.username || configuration.password)) {
|
|
||||||
object["auth"] = {
|
|
||||||
username: configuration.username,
|
|
||||||
password: configuration.password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setBearerAuthToObject = async function (
|
|
||||||
object: any,
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
if (configuration && configuration.accessToken) {
|
|
||||||
const accessToken =
|
|
||||||
typeof configuration.accessToken === "function"
|
|
||||||
? await configuration.accessToken()
|
|
||||||
: await configuration.accessToken
|
|
||||||
object["Authorization"] = "Bearer " + accessToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setOAuthToObject = async function (
|
|
||||||
object: any,
|
|
||||||
name: string,
|
|
||||||
scopes: string[],
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
if (configuration && configuration.accessToken) {
|
|
||||||
const localVarAccessTokenValue =
|
|
||||||
typeof configuration.accessToken === "function"
|
|
||||||
? await configuration.accessToken(name, scopes)
|
|
||||||
: await configuration.accessToken
|
|
||||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
|
||||||
const searchParams = new URLSearchParams(url.search)
|
|
||||||
for (const object of objects) {
|
|
||||||
for (const key in object) {
|
|
||||||
if (Array.isArray(object[key])) {
|
|
||||||
searchParams.delete(key)
|
|
||||||
for (const item of object[key]) {
|
|
||||||
searchParams.append(key, item)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
searchParams.set(key, object[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
url.search = searchParams.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const serializeDataIfNeeded = function (
|
|
||||||
value: any,
|
|
||||||
requestOptions: any,
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
const nonString = typeof value !== "string"
|
|
||||||
const needsSerialization =
|
|
||||||
nonString && configuration && configuration.isJsonMime
|
|
||||||
? configuration.isJsonMime(requestOptions.headers["Content-Type"])
|
|
||||||
: nonString
|
|
||||||
return needsSerialization
|
|
||||||
? JSON.stringify(value !== undefined ? value : {})
|
|
||||||
: value || ""
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const toPathString = function (url: URL) {
|
|
||||||
return url.pathname + url.search + url.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const createRequestFunction = function (
|
|
||||||
axiosArgs: RequestArgs,
|
|
||||||
globalAxios: AxiosInstance,
|
|
||||||
BASE_PATH: string,
|
|
||||||
configuration?: Configuration
|
|
||||||
) {
|
|
||||||
return <T = unknown, R = AxiosResponse<T>>(
|
|
||||||
axios: AxiosInstance = globalAxios,
|
|
||||||
basePath: string = BASE_PATH
|
|
||||||
) => {
|
|
||||||
const axiosRequestArgs = {
|
|
||||||
...axiosArgs.options,
|
|
||||||
url: (configuration?.basePath || basePath) + axiosArgs.url,
|
|
||||||
}
|
|
||||||
return axios.request<T, R>(axiosRequestArgs)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* TSGRain WebUI
|
|
||||||
* REST API for the TSGRain WebUI
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface ConfigurationParameters {
|
|
||||||
apiKey?:
|
|
||||||
| string
|
|
||||||
| Promise<string>
|
|
||||||
| ((name: string) => string)
|
|
||||||
| ((name: string) => Promise<string>)
|
|
||||||
username?: string
|
|
||||||
password?: string
|
|
||||||
accessToken?:
|
|
||||||
| string
|
|
||||||
| Promise<string>
|
|
||||||
| ((name?: string, scopes?: string[]) => string)
|
|
||||||
| ((name?: string, scopes?: string[]) => Promise<string>)
|
|
||||||
basePath?: string
|
|
||||||
baseOptions?: any
|
|
||||||
formDataCtor?: new () => any
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Configuration {
|
|
||||||
/**
|
|
||||||
* parameter for apiKey security
|
|
||||||
* @param name security name
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
apiKey?:
|
|
||||||
| string
|
|
||||||
| Promise<string>
|
|
||||||
| ((name: string) => string)
|
|
||||||
| ((name: string) => Promise<string>)
|
|
||||||
/**
|
|
||||||
* parameter for basic security
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
username?: string
|
|
||||||
/**
|
|
||||||
* parameter for basic security
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
password?: string
|
|
||||||
/**
|
|
||||||
* parameter for oauth2 security
|
|
||||||
* @param name security name
|
|
||||||
* @param scopes oauth2 scope
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
accessToken?:
|
|
||||||
| string
|
|
||||||
| Promise<string>
|
|
||||||
| ((name?: string, scopes?: string[]) => string)
|
|
||||||
| ((name?: string, scopes?: string[]) => Promise<string>)
|
|
||||||
/**
|
|
||||||
* override base path
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
basePath?: string
|
|
||||||
/**
|
|
||||||
* base options for axios calls
|
|
||||||
*
|
|
||||||
* @type {any}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
baseOptions?: any
|
|
||||||
/**
|
|
||||||
* The FormData constructor that will be used to create multipart form data
|
|
||||||
* requests. You can inject this here so that execution environments that
|
|
||||||
* do not support the FormData class can still run the generated client.
|
|
||||||
*
|
|
||||||
* @type {new () => FormData}
|
|
||||||
*/
|
|
||||||
formDataCtor?: new () => any
|
|
||||||
|
|
||||||
constructor(param: ConfigurationParameters = {}) {
|
|
||||||
this.apiKey = param.apiKey
|
|
||||||
this.username = param.username
|
|
||||||
this.password = param.password
|
|
||||||
this.accessToken = param.accessToken
|
|
||||||
this.basePath = param.basePath
|
|
||||||
this.baseOptions = param.baseOptions
|
|
||||||
this.formDataCtor = param.formDataCtor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the given MIME is a JSON MIME.
|
|
||||||
* JSON MIME examples:
|
|
||||||
* application/json
|
|
||||||
* application/json; charset=UTF8
|
|
||||||
* APPLICATION/JSON
|
|
||||||
* application/vnd.company+json
|
|
||||||
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
|
||||||
* @return True if the given MIME is JSON, false otherwise.
|
|
||||||
*/
|
|
||||||
public isJsonMime(mime: string): boolean {
|
|
||||||
const jsonMime: RegExp = new RegExp(
|
|
||||||
"^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$",
|
|
||||||
"i"
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
mime !== null &&
|
|
||||||
(jsonMime.test(mime) ||
|
|
||||||
mime.toLowerCase() === "application/json-patch+json")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
|
||||||
#
|
|
||||||
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
|
||||||
|
|
||||||
git_user_id=$1
|
|
||||||
git_repo_id=$2
|
|
||||||
release_note=$3
|
|
||||||
git_host=$4
|
|
||||||
|
|
||||||
if [ "$git_host" = "" ]; then
|
|
||||||
git_host="github.com"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$git_user_id" = "" ]; then
|
|
||||||
git_user_id="GIT_USER_ID"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$git_repo_id" = "" ]; then
|
|
||||||
git_repo_id="GIT_REPO_ID"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$release_note" = "" ]; then
|
|
||||||
release_note="Minor update"
|
|
||||||
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Initialize the local directory as a Git repository
|
|
||||||
git init
|
|
||||||
|
|
||||||
# Adds the files in the local repository and stages them for commit.
|
|
||||||
git add .
|
|
||||||
|
|
||||||
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
|
||||||
git commit -m "$release_note"
|
|
||||||
|
|
||||||
# Sets the new remote
|
|
||||||
git_remote=$(git remote)
|
|
||||||
if [ "$git_remote" = "" ]; then # git remote not defined
|
|
||||||
|
|
||||||
if [ "$GIT_TOKEN" = "" ]; then
|
|
||||||
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
|
||||||
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
|
||||||
else
|
|
||||||
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
git pull origin master
|
|
||||||
|
|
||||||
# Pushes (Forces) the changes in the local repository up to the remote repository
|
|
||||||
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
|
||||||
git push origin master 2>&1 | grep -v 'To https'
|
|
|
@ -1,16 +0,0 @@
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* TSGRain WebUI
|
|
||||||
* REST API for the TSGRain WebUI
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./api"
|
|
||||||
export * from "./configuration"
|
|
|
@ -1,20 +0,0 @@
|
||||||
import {Configuration, DefaultApi} from "../tsgrain-client"
|
|
||||||
|
|
||||||
let apiHost = document.location.host
|
|
||||||
const httpProto = document.location.protocol
|
|
||||||
const wsProto = httpProto === "https:" ? "wss:" : "ws:"
|
|
||||||
|
|
||||||
if (import.meta.env.VITE_API_HOST !== undefined) {
|
|
||||||
apiHost = import.meta.env.VITE_API_HOST as string
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiUrl = `${httpProto}//${apiHost}/api`
|
|
||||||
const wsUrl = `${wsProto}//${apiHost}/api/ws`
|
|
||||||
|
|
||||||
let apicfg = new Configuration({
|
|
||||||
basePath: apiUrl,
|
|
||||||
})
|
|
||||||
|
|
||||||
const sebraucApi = new DefaultApi(apicfg)
|
|
||||||
|
|
||||||
export {apiUrl, wsUrl, sebraucApi}
|
|
|
@ -1,27 +0,0 @@
|
||||||
export interface Config {
|
|
||||||
version: string
|
|
||||||
n_zones: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
config?: any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isConfig(object: any): object is Config {
|
|
||||||
return typeof object === "object" && "version" in object
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getConfig(): Config {
|
|
||||||
if (isConfig(window.config)) {
|
|
||||||
return window.config
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error("App config not found")
|
|
||||||
return {
|
|
||||||
version: "dev",
|
|
||||||
n_zones: 0,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
function secondsToString(seconds: number): string {
|
|
||||||
const numyears = Math.floor(seconds / 31536000)
|
|
||||||
const numdays = Math.floor((seconds % 31536000) / 86400)
|
|
||||||
const numhours = Math.floor(((seconds % 31536000) % 86400) / 3600)
|
|
||||||
const numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60)
|
|
||||||
const numseconds = (((seconds % 31536000) % 86400) % 3600) % 60
|
|
||||||
|
|
||||||
let res = []
|
|
||||||
if (numyears > 0) res.push(numyears + "yr")
|
|
||||||
if (numdays > 0) res.push(numdays + "d")
|
|
||||||
if (numhours > 0) res.push(numhours + "h")
|
|
||||||
if (numminutes > 0) res.push(numminutes + "m")
|
|
||||||
if (seconds < 60) res.push(numseconds + "s")
|
|
||||||
|
|
||||||
return res.join(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
export {secondsToString}
|
|
|
@ -1,92 +0,0 @@
|
||||||
import {wsUrl} from "./apiUrls"
|
|
||||||
|
|
||||||
class WebsocketAPI {
|
|
||||||
private static ws: WebsocketAPI | undefined
|
|
||||||
|
|
||||||
private conn: WebSocket | undefined
|
|
||||||
private wsConnected: boolean
|
|
||||||
|
|
||||||
private clients: Set<WebsocketClient>
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
this.clients = new Set()
|
|
||||||
this.wsConnected = false
|
|
||||||
|
|
||||||
if (window.WebSocket) {
|
|
||||||
this.connect()
|
|
||||||
} else {
|
|
||||||
console.log("Your browser does not support WebSockets")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setStatus(wsConnected: boolean) {
|
|
||||||
if (wsConnected !== this.wsConnected) {
|
|
||||||
this.wsConnected = wsConnected
|
|
||||||
this.clients.forEach((client) => {
|
|
||||||
client.statusCallback(this.wsConnected)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private connect() {
|
|
||||||
this.conn = new WebSocket(wsUrl)
|
|
||||||
this.conn.onopen = () => {
|
|
||||||
this.setStatus(true)
|
|
||||||
console.log("WS connected")
|
|
||||||
}
|
|
||||||
this.conn.onclose = () => {
|
|
||||||
this.setStatus(false)
|
|
||||||
console.log("WS connection closed")
|
|
||||||
window.setTimeout(() => this.connect(), 3000)
|
|
||||||
}
|
|
||||||
this.conn.onmessage = (evt) => {
|
|
||||||
this.clients.forEach((client) => {
|
|
||||||
client.msgCallback(evt)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Get(): WebsocketAPI {
|
|
||||||
if (this.ws === undefined) {
|
|
||||||
this.ws = new WebsocketAPI()
|
|
||||||
}
|
|
||||||
return this.ws
|
|
||||||
}
|
|
||||||
|
|
||||||
isConnected(): boolean {
|
|
||||||
return this.wsConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
addClient(client: WebsocketClient) {
|
|
||||||
console.log("added client", client)
|
|
||||||
this.clients.add(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
removeClient(client: WebsocketClient) {
|
|
||||||
console.log("removed client", client)
|
|
||||||
this.clients.delete(client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WebsocketClient {
|
|
||||||
statusCallback: (wsConnected: boolean) => void
|
|
||||||
msgCallback: (evt: MessageEvent) => void
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
statusCallback: (wsConnected: boolean) => void,
|
|
||||||
msgCallback: (evt: MessageEvent) => void
|
|
||||||
) {
|
|
||||||
this.statusCallback = statusCallback
|
|
||||||
this.msgCallback = msgCallback
|
|
||||||
|
|
||||||
this.api().addClient(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
api(): WebsocketAPI {
|
|
||||||
return WebsocketAPI.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.api().removeClient(this)
|
|
||||||
}
|
|
||||||
}
|
|
1
ui/src/vite-env.d.ts
vendored
1
ui/src/vite-env.d.ts
vendored
|
@ -1 +0,0 @@
|
||||||
/// <reference types="vite/client" />
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": false,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"jsxFactory": "h",
|
|
||||||
"jsxFragmentFactory": "Fragment"
|
|
||||||
}
|
|
||||||
}
|
|
66
ui/ui.go
66
ui/ui.go
|
@ -1,66 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"embed"
|
|
||||||
"encoding/json"
|
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/server/middleware"
|
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/util"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
const distDir = "dist"
|
|
||||||
|
|
||||||
//go:embed dist/**
|
|
||||||
var assets embed.FS
|
|
||||||
|
|
||||||
type uiConfig struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
NZones int32 `json:"n_zones"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func subFS(fsys fs.FS, dir string) fs.FS {
|
|
||||||
sub, err := fs.Sub(fsys, dir)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
|
|
||||||
func distFS() fs.FS {
|
|
||||||
return subFS(assets, distDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Register(r gin.IRouter, nZones int32) {
|
|
||||||
indexHandler := getIndexHandler(nZones)
|
|
||||||
|
|
||||||
uiAssets := r.Group("/assets", middleware.Cache)
|
|
||||||
|
|
||||||
r.GET("/", indexHandler)
|
|
||||||
r.GET("/index.html", indexHandler)
|
|
||||||
|
|
||||||
uiAssets.StaticFS("/", http.FS(subFS(distFS(), "assets")))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIndexHandler(nZones int32) gin.HandlerFunc {
|
|
||||||
content, err := fs.ReadFile(distFS(), "index.html")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
uiConfigBytes, err := json.Marshal(uiConfig{
|
|
||||||
Version: util.Version(),
|
|
||||||
NZones: nZones,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
content = bytes.ReplaceAll(content, []byte("\"%CONFIG%\""), uiConfigBytes)
|
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
c.Data(200, "text/html", content)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.thetadev.de/TSGRain/WebUI/src/fixtures"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUI(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
path string
|
|
||||||
contains string
|
|
||||||
cached bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "index_html",
|
|
||||||
path: "/",
|
|
||||||
contains: "TSGRain",
|
|
||||||
cached: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "index_html2",
|
|
||||||
path: "/index.html",
|
|
||||||
contains: "TSGRain",
|
|
||||||
cached: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "index_js",
|
|
||||||
path: path.Join("/assets", getIndexJS()),
|
|
||||||
contains: "app",
|
|
||||||
cached: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
router := gin.New()
|
|
||||||
Register(router, 3)
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req, _ := http.NewRequest("GET", tt.path, nil)
|
|
||||||
router.ServeHTTP(w, req)
|
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
|
||||||
assert.Contains(t, w.Body.String(), tt.contains)
|
|
||||||
|
|
||||||
ccHeader := w.Header().Get("Cache-Control")
|
|
||||||
|
|
||||||
if tt.cached {
|
|
||||||
assert.Equal(t, "public, max-age=604800, immutable", ccHeader)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, "", ccHeader)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIndexJS() string {
|
|
||||||
baseDir := "ui/dist/assets"
|
|
||||||
indexExp := regexp.MustCompile(`index\.[0-9a-f]{8}\.js`)
|
|
||||||
|
|
||||||
fixtures.CdProjectRoot()
|
|
||||||
distDir, err := os.Open(baseDir)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
list, err := distDir.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range list {
|
|
||||||
if indexExp.MatchString(f.Name()) {
|
|
||||||
return f.Name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("no index.js found")
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import {defineConfig} from "vite"
|
|
||||||
import preact from "@preact/preset-vite"
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [preact()],
|
|
||||||
})
|
|
Loading…
Reference in a new issue