diff --git a/server/db.go b/server/db.go deleted file mode 100644 index 304c1f0..0000000 --- a/server/db.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "fmt" - "os" - "time" - - "github.com/google/uuid" - "github.com/labstack/gommon/log" - "gorm.io/driver/postgres" - "gorm.io/gorm" -) - -var db *gorm.DB - -func ConnectDbFromEnv() { - dbHost := os.Getenv("DB_HOST") - if dbHost == "" { - log.Fatal("No DB_HOST") - } - dbPort := os.Getenv("DB_PORT") - if dbPort == "" { - log.Fatal("No DB_PORT") - - } - dbName := os.Getenv("DB_NAME") - if dbName == "" { - log.Fatal("No DB_NAME") - } - dbUser := os.Getenv("DB_USER") - if dbUser == "" { - log.Fatal("No DB_USER") - - } - dbPassword := os.Getenv("DB_PASSWORD") - if dbPassword == "" { - log.Fatal("No DB_PASSWORD") - } - dbSslMode := os.Getenv("DB_SSLMODE") - if dbSslMode == "" { - log.Fatal("No DB_SSLMODE") - } - - dsn := fmt.Sprintf( - "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=Europe/Bratislava", - dbHost, - dbPort, - dbUser, - dbPassword, - dbName, - dbSslMode, - ) - - var err error - db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) - if err == nil { - err2 := db.AutoMigrate(&Dummy{}) - if err2 != nil { - fmt.Printf("db migration error: %v\n", err2) - } - } - - if err != nil { - log.Fatal(err) - } -} - -type Dummy struct { - ID uuid.UUID `gorm:"primaryKey;type:uuid;unique" json:"id"` - Name string `json:"name"` - Connected bool `json:"connected"` - ConnectedAt time.Time `json:"connectedAt"` - DisconnectedAt time.Time `json:"disconnectedAt"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} diff --git a/server/main.go b/server/main.go index 1c2f911..2555b48 100644 --- a/server/main.go +++ b/server/main.go @@ -22,9 +22,15 @@ import ( const TimeFormat = "2006-01-02 15:04:05" type Client struct { - Obj *Dummy `json:"obj"` - conn *websocket.Conn `json:"-"` - ExitWanted bool `json:"exitWanted"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` + Connected bool `json:"connected"` + ConnectedAt time.Time `json:"connectedAt"` + DisconnectedAt time.Time `json:"disconnectedAt"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + ExitWanted bool `json:"exitWanted"` + conn *websocket.Conn `json:"-"` } var clients = []*Client{} @@ -60,33 +66,34 @@ func idparam(next func(echo.Context, uuid.UUID) error) echo.HandlerFunc { return next(c, id) } +} +func filename(dataDir string, id string) string { + return fmt.Sprintf("%s/%s_%d.txt", dataDir, time.Now().Format("2006-01-02"), id) } func main() { _ = godotenv.Load() dataDir = os.Getenv("DATA_DIR") if dataDir == "" { - log.Fatal("KEY_DIR is not defined") + log.Fatal("DATA_DIR is not defined") } token = os.Getenv("TOKEN") if token == "" { log.Fatal("TOKEN is not defined") } - ConnectDbFromEnv() - f, err := os.OpenFile(path.Join(dataDir, "startup"), syscall.O_CREAT|syscall.O_APPEND|syscall.O_WRONLY, 0644) if err != nil { - log.Fatalf("KEY_DIR %s is not writable (%v)\n", dataDir, err) + log.Fatalf("DATA_DIR %s is not writable (%v)\n", dataDir, err) } _, err = f.Write([]byte(fmt.Sprintf("%s\n", time.Now().Format(TimeFormat)))) if err != nil { - log.Fatalf("KEY_DIR test file couldn't be written to (%v)\n", err) + log.Fatalf("DATA_DIR test file couldn't be written to (%v)\n", err) } err = f.Close() if err != nil { - log.Fatalf("KEY_DIR test file couldn't be closed (%v)\n", err) + log.Fatalf("DATA_DIR test file couldn't be closed (%v)\n", err) } app := echo.New() @@ -140,27 +147,40 @@ func main() { app.GET("/keys", keys) // administration - app.GET("/admin/clients", func(c echo.Context) error { - if c.Request().Header.Get("Authorization") != token { - return c.NoContent(http.StatusUnauthorized) + app.GET("/admin/clients", authed(func(c echo.Context) error { + return c.JSON(http.StatusOK, clients) + })) + + app.GET("/admin/logs", authed(func(c echo.Context) error { + files, err := os.ReadDir(dataDir) + if err != nil { + return c.NoContent(http.StatusInternalServerError) } - return c.JSON(http.StatusOK, clients) - }) + filenames := []string{} + for _, file := range files { + filenames = append(filenames, file.Name()) + } + + return c.JSON(http.StatusOK, filenames) + })) + app.GET("/admin/:id", authed(idparam(func(c echo.Context, id uuid.UUID) error { for _, client := range clients { - if client.Obj.ID == id { + if client.ID == id { return c.JSON(http.StatusOK, client) } } return c.NoContent(http.StatusNotFound) }))) + app.GET("/admin/:id/logs", authed(idparam(func(c echo.Context, id uuid.UUID) error { date := c.Param("date") if date == "" { date = time.Now().Format("2006-01-02") } + skipRaw := c.Param("skip") skip := 0 if skipRaw != "" { @@ -169,6 +189,7 @@ func main() { return c.JSON(http.StatusBadRequest, echo.Map{"message": "skip is not a number"}) } } + takeRaw := c.Param("take") take := 1024 if takeRaw != "" { @@ -178,14 +199,16 @@ func main() { } } - f, err := os.OpenFile(fmt.Sprintf("%s/%s_%d.txt", dataDir, time.Now().Format("2006-01-02"), id), syscall.O_CREAT|syscall.O_APPEND|syscall.O_WRONLY, 0644) + f, err := os.OpenFile(filename(dataDir, id.String()), syscall.O_CREAT|syscall.O_APPEND|syscall.O_WRONLY, 0644) if err != nil { return c.JSON(http.StatusNotFound, echo.Map{"message": "file not found"}) } - _, err = f.Seek(int64(skip), io.SeekStart) - if err != nil { - return c.NoContent(http.StatusInternalServerError) + if skip != 0 { + _, err = f.Seek(int64(skip), io.SeekStart) + if err != nil { + return c.NoContent(http.StatusInternalServerError) + } } bytes := make([]byte, take) @@ -196,24 +219,27 @@ func main() { return c.JSON(http.StatusOK, bytes) }))) + app.POST("/admin/:id/name", authed(idparam(func(c echo.Context, id uuid.UUID) error { + name := c.Param("name") + if name == "" { + return c.JSON(http.StatusBadRequest, "missing field 'name'") + } + for _, client := range clients { - if client.Obj.ID == id { - client.Obj.Name = c.Param("name") - client.Obj.UpdatedAt = time.Now() - tx := db.Save(client.Obj) - if tx.Error != nil { - return c.NoContent(http.StatusInternalServerError) - } + if client.ID == id { + client.Name = name + client.UpdatedAt = time.Now() return c.JSON(http.StatusOK, echo.Map{"message": "ok"}) } } return c.JSON(http.StatusNotFound, echo.Map{"message": "not found"}) }))) + app.POST("/admin/:id/exit", authed(idparam(func(c echo.Context, id uuid.UUID) error { for _, client := range clients { - if client.Obj.ID == id { + if client.ID == id { client.ExitWanted = true return c.JSON(http.StatusOK, echo.Map{"message": "ok"}) } @@ -234,51 +260,36 @@ func keys(c echo.Context) error { now := time.Now() client := Client{ - Obj: &Dummy{ - ID: uuid.New(), - Name: "Dummy", - Connected: true, - ConnectedAt: now, - DisconnectedAt: now, - CreatedAt: now, - UpdatedAt: now, - }, - conn: ws, - ExitWanted: false, + ID: uuid.New(), + Name: "Dummy", + Connected: true, + ConnectedAt: now, + DisconnectedAt: now, + CreatedAt: now, + UpdatedAt: now, + ExitWanted: false, + conn: ws, } - tx := db.Create(client.Obj) - if tx.Error != nil { - log.Error(err) - fmt.Printf("%s client %s crashed (couldn't create record)\n", time.Now().Format(TimeFormat), client.Obj.ID.String()) - client.ExitWanted = true - client.Obj.Connected = false - client.Obj.DisconnectedAt = time.Now() - return c.NoContent(http.StatusInternalServerError) - } clients = append(clients, &client) - fmt.Printf("%s client %s connected\n", time.Now().Format(TimeFormat), client.Obj.ID.String()) + fmt.Printf("%s client %s connected\n", time.Now().Format(TimeFormat), client.ID.String()) err = ws.WriteMessage(websocket.TextMessage, []byte("welcome")) if err != nil { log.Error(err) } - f, err := os.OpenFile(fmt.Sprintf("%s/%s_%d.txt", dataDir, time.Now().Format("2006-01-02"), client.Obj.ID.String()), syscall.O_CREAT|syscall.O_APPEND|syscall.O_WRONLY, 0644) + f, err := os.OpenFile(filename(dataDir, client.ID.String()), syscall.O_CREAT|syscall.O_APPEND|syscall.O_WRONLY, 0644) if err != nil { log.Error(err) - fmt.Printf("%s client %s crashed (couldn't open file)\n", time.Now().Format(TimeFormat), client.Obj.ID) + fmt.Printf("%s client %s crashed (couldn't open file)\n", time.Now().Format(TimeFormat), client.ID) now = time.Now() client.ExitWanted = true - client.Obj.Connected = false - client.Obj.DisconnectedAt = now - client.Obj.UpdatedAt = now + client.Connected = false + client.DisconnectedAt = now + client.UpdatedAt = now - tx = db.Save(client.Obj) - if tx.Error != nil { - log.Error(err) - } return c.NoContent(http.StatusInternalServerError) } defer f.Close() @@ -289,20 +300,15 @@ func keys(c echo.Context) error { now = time.Now() client.ExitWanted = true - client.Obj.Connected = false - client.Obj.DisconnectedAt = now - client.Obj.UpdatedAt = now - - tx = db.Save(client.Obj) - if tx.Error != nil { - log.Error(err) - } + client.Connected = false + client.DisconnectedAt = now + client.UpdatedAt = now } for { if client.ExitWanted { close() - fmt.Printf("%s client %s disconnected\n", time.Now().Format(TimeFormat), client.Obj.ID.String()) + fmt.Printf("%s client %s disconnected\n", time.Now().Format(TimeFormat), client.ID.String()) break } @@ -310,7 +316,7 @@ func keys(c echo.Context) error { if err != nil { log.Error(err) close() - fmt.Printf("%s client %s crashed (websocket error)\n", time.Now().Format(TimeFormat), client.Obj.ID.String()) + fmt.Printf("%s client %s crashed (websocket error)\n", time.Now().Format(TimeFormat), client.ID.String()) return c.NoContent(http.StatusInternalServerError) } @@ -318,10 +324,9 @@ func keys(c echo.Context) error { if err != nil { log.Error(err) close() - fmt.Printf("%s client %s crashed (file write error)\n", time.Now().Format(TimeFormat), client.Obj.ID.String()) + fmt.Printf("%s client %s crashed (file write error)\n", time.Now().Format(TimeFormat), client.ID.String()) return c.NoContent(http.StatusInternalServerError) } - } return c.NoContent(http.StatusOK)