Skip to content

Commit

Permalink
Add API endpoint for log querying (admin)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucaBernstein committed Apr 11, 2023
1 parent f51d319 commit ff3761f
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 12 deletions.
72 changes: 72 additions & 0 deletions api/admin/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package admin

import (
"database/sql"
"net/http"
"strconv"
"time"

"github.com/LucaBernstein/beancount-bot-tg/db"
"github.com/gin-gonic/gin"
)

type Log struct {
Created string `json:"createdOn"`
Chat string `json:"chat"`
Level int `json:"level"`
Message string `json:"message"`
}

func GetLogs(from, to string, minLevel int) ([]Log, error) {
rows, err := db.Connection().Query(`
SELECT "created", "chat", "level", "message"
FROM "app::log"
WHERE "created" >= $1 AND "created" < $2 AND "level" >= $3
ORDER BY "created" DESC
`, from, to, minLevel)
if err != nil {
return nil, err
}
logs := []Log{}
for rows.Next() {
logEntry := &Log{}
chatDb := sql.NullString{}
err := rows.Scan(&logEntry.Created, &chatDb, &logEntry.Level, &logEntry.Message)
if err != nil {
return nil, err
}
logEntry.Chat = chatDb.String
logs = append(logs, *logEntry)
}
return logs, nil
}

func (r *Router) Logs(c *gin.Context) {
from := c.Query("from")
if from == "" {
from = time.Now().Add(-24 * time.Hour).Format(time.DateTime)
}
to := c.Query("to")
if to == "" {
to = time.Now().Format(time.DateTime)
}
minLevelQ := c.Query("minLevel")
if minLevelQ == "" {
minLevelQ = "0"
}
minLevel, err := strconv.Atoi(minLevelQ)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
logs, err := GetLogs(from, to, minLevel)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, logs)
}
38 changes: 38 additions & 0 deletions api/admin/logs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package admin_test

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/LucaBernstein/beancount-bot-tg/api/admin"
"github.com/LucaBernstein/beancount-bot-tg/api/helpers/apiTest"
"github.com/LucaBernstein/beancount-bot-tg/bot/botTest"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)

func TestLogs(t *testing.T) {
token, mockBc, msg := apiTest.MockBcApiUser(t, 919)
r := gin.Default()
g := r.Group("")
admin.NewRouter(mockBc).Hook(g)

// Should be forbidden without admin priviledges
err := apiTest.PromoteAdmin(msg.Chat.ID, false)
botTest.HandleErr(t, err)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/logs", nil)
req.Header.Add("Authorization", "Bearer "+token)
r.ServeHTTP(w, req)
assert.Equal(t, 403, w.Result().StatusCode)

err = apiTest.PromoteAdmin(msg.Chat.ID, true)
botTest.HandleErr(t, err)

w = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/logs", nil)
req.Header.Add("Authorization", "Bearer "+token)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Result().StatusCode)
}
24 changes: 24 additions & 0 deletions api/admin/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package admin

import (
"github.com/LucaBernstein/beancount-bot-tg/api/helpers"
"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/gin-gonic/gin"
)

type Router struct {
bc *bot.BotController
}

func NewRouter(bc *bot.BotController) *Router {
return &Router{
bc: bc,
}
}

func (r *Router) Hook(g *gin.RouterGroup) {
g.Use(helpers.AttachChatId(r.bc))
g.Use(helpers.EnsureAdmin(r.bc))

g.GET("/logs", r.Logs)
}
12 changes: 1 addition & 11 deletions api/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,10 @@ import (
"github.com/LucaBernstein/beancount-bot-tg/api/helpers/apiTest"
"github.com/LucaBernstein/beancount-bot-tg/bot/botTest"
"github.com/LucaBernstein/beancount-bot-tg/db"
"github.com/LucaBernstein/beancount-bot-tg/helpers"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)

func PromoteAdmin(chatId int64) error {
_, err := db.Connection().Exec(`DELETE FROM "bot::userSetting" WHERE "tgChatId" = $1 AND "setting" = $2`, chatId, helpers.USERSET_ADM)
if err != nil {
return err
}
_, err = db.Connection().Exec(`INSERT INTO "bot::userSetting" ("tgChatId", "setting", "value") VALUES ($1, $2, $3)`, chatId, helpers.USERSET_ADM, "true")
return err
}

func AllSettingsTypes() ([]string, error) {
rows, err := db.Connection().Query(`SELECT "setting" FROM "bot::userSettingTypes"`)
if err != nil {
Expand All @@ -43,7 +33,7 @@ func AllSettingsTypes() ([]string, error) {

func TestFullConfigMap(t *testing.T) {
token, mockBc, msg := apiTest.MockBcApiUser(t, 786)
err := PromoteAdmin(msg.Chat.ID)
err := apiTest.PromoteAdmin(msg.Chat.ID, true)
botTest.HandleErr(t, err)

settings, err := AllSettingsTypes()
Expand Down
11 changes: 11 additions & 0 deletions api/helpers/apiTest/mockBotApiUser.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ func MockBcApiUser(t *testing.T, id int64) (token string, mockBc *bot.BotControl

return token, mockBc, msg
}

func PromoteAdmin(chatId int64, becomes bool) error {
_, err := db.Connection().Exec(`DELETE FROM "bot::userSetting" WHERE "tgChatId" = $1 AND "setting" = $2`, chatId, helpers.USERSET_ADM)
if err != nil {
return err
}
if becomes {
_, err = db.Connection().Exec(`INSERT INTO "bot::userSetting" ("tgChatId", "setting", "value") VALUES ($1, $2, $3)`, chatId, helpers.USERSET_ADM, "true")
}
return err
}
17 changes: 17 additions & 0 deletions api/helpers/chatId.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/gin-gonic/gin"
"gopkg.in/telebot.v3"
)

const K_CHAT_ID = "tgChatId"
Expand Down Expand Up @@ -42,3 +43,19 @@ func AttachChatId(bc *bot.BotController) gin.HandlerFunc {
c.Next()
}
}

func EnsureAdmin(bc *bot.BotController) gin.HandlerFunc {
return func(c *gin.Context) {
tgChatId := c.GetInt64("tgChatId")
m := &telebot.Message{Chat: &telebot.Chat{ID: tgChatId}}
isAdmin := bc.Repo.UserIsAdmin(m)
if !isAdmin {
c.JSON(http.StatusForbidden, gin.H{
"error": "missing priviledges",
})
c.Abort()
return
}
c.Next()
}
}
6 changes: 5 additions & 1 deletion api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package api
import (
"log"

"github.com/LucaBernstein/beancount-bot-tg/api/token"
"github.com/LucaBernstein/beancount-bot-tg/api/admin"
"github.com/LucaBernstein/beancount-bot-tg/api/config"
"github.com/LucaBernstein/beancount-bot-tg/api/token"
"github.com/LucaBernstein/beancount-bot-tg/api/transactions"
"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/gin-gonic/gin"
Expand All @@ -26,6 +27,9 @@ func StartWebServer(bc *bot.BotController) {
configGroup := apiGroup.Group("/config")
config.NewRouter(bc).Hook(configGroup)

adminGroup := apiGroup.Group("/admin")
admin.NewRouter(bc).Hook(adminGroup)

port := ":8080"
log.Printf("Web server started on %s", port)
r.Run(port)
Expand Down

0 comments on commit ff3761f

Please sign in to comment.