add admin service (#263)

* add admin server + service

* string fixes

* ban and unban user

* add return

* ban using admin key

* ban reason

* remove redundant func call

* add http status

* add log

* anon_id_sha256 (#262)

* anon_id_sha256

* refactor sha256 hash to strings package

* oops

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>

* add admin server + service

* ban and unban user

* string fixes

* add return

* ban using admin key

* ban reason

* remove redundant func call

* add http status

* add log

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
Co-authored-by: LightningTipBot <88730856+LightningTipBot@users.noreply.github.com>
This commit is contained in:
gohumble 2022-01-05 01:40:37 +01:00 committed by GitHub
parent e275b8760a
commit 8f60cb82f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 23 deletions

View file

@ -0,0 +1,16 @@
package admin
import (
"github.com/LightningTipBot/LightningTipBot/internal/telegram"
"gorm.io/gorm"
)
type Service struct {
db *gorm.DB
}
func New(b *telegram.TipBot) Service {
return Service{
db: b.Database,
}
}

65
internal/api/admin/ban.go Normal file
View file

@ -0,0 +1,65 @@
package admin
import (
"fmt"
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"net/http"
"strings"
)
func (s Service) UnbanUser(w http.ResponseWriter, r *http.Request) {
user, err := s.getUserByTelegramId(r)
if err != nil {
log.Errorf("[ADMIN] could not ban user: %v", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if !user.Banned {
log.Infof("[ADMIN] user already banned")
w.WriteHeader(http.StatusBadRequest)
return
}
user.Banned = false
adminSlice := strings.Split(user.Wallet.Adminkey, "_")
user.Wallet.Adminkey = adminSlice[len(adminSlice)-1]
s.db.Save(user)
log.Infof("[ADMIN] Unbanned user (%s)", user.ID)
w.WriteHeader(http.StatusOK)
}
func (s Service) BanUser(w http.ResponseWriter, r *http.Request) {
user, err := s.getUserByTelegramId(r)
if err != nil {
log.Errorf("[ADMIN] could not ban user: %v", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if user.Banned {
w.WriteHeader(http.StatusBadRequest)
log.Infof("[ADMIN] user already banned")
return
}
user.Banned = true
if reason := r.URL.Query().Get("reason"); reason != "" {
user.Wallet.Adminkey = fmt.Sprintf("%s_%s", reason, user.Wallet.Adminkey)
}
user.Wallet.Adminkey = fmt.Sprintf("%s_%s", "banned", user.Wallet.Adminkey)
s.db.Save(user)
log.Infof("[ADMIN] Banned user (%s)", user.ID)
w.WriteHeader(http.StatusOK)
}
func (s Service) getUserByTelegramId(r *http.Request) (*lnbits.User, error) {
user := &lnbits.User{}
v := mux.Vars(r)
if v["id"] == "" {
return nil, fmt.Errorf("invalid id")
}
tx := s.db.Where("telegram_id = ? COLLATE NOCASE", v["id"]).First(user)
if tx.Error != nil {
return nil, tx.Error
}
return user, nil
}

View file

@ -20,9 +20,9 @@ const (
StatusOk = "OK"
)
func NewServer() *Server {
func NewServer(address string) *Server {
srv := &http.Server{
Addr: internal.Configuration.Bot.LNURLServerUrl.Host,
Addr: address,
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
@ -40,6 +40,9 @@ func NewServer() *Server {
func (w *Server) ListenAndServe() {
go w.httpServer.ListenAndServe()
}
func (w *Server) PathPrefix(path string, handler http.Handler) {
w.router.PathPrefix(path).Handler(handler)
}
func (w *Server) AppendRoute(path string, handler func(http.ResponseWriter, *http.Request), methods ...string) {
r := w.router.HandleFunc(path, LoggingMiddleware("API", handler))
if len(methods) > 0 {

View file

@ -15,17 +15,18 @@ type Client struct {
}
type User struct {
ID string `json:"id"`
Name string `json:"name" gorm:"primaryKey"`
Initialized bool `json:"initialized"`
Telegram *tb.User `gorm:"embedded;embeddedPrefix:telegram_"`
Wallet *Wallet `gorm:"embedded;embeddedPrefix:wallet_"`
StateKey UserStateKey `json:"stateKey"`
StateData string `json:"stateData"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
AnonID string `json:"anon_id"`
ID string `json:"id"`
Name string `json:"name" gorm:"primaryKey"`
Initialized bool `json:"initialized"`
Telegram *tb.User `gorm:"embedded;embeddedPrefix:telegram_"`
Wallet *Wallet `gorm:"embedded;embeddedPrefix:wallet_"`
StateKey UserStateKey `json:"stateKey"`
StateData string `json:"stateData"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
AnonID string `json:"anon_id"`
AnonIDSha256 string `json:"anon_id_sha256"`
Banned bool `json:"banned"`
}
const (

View file

@ -3,10 +3,11 @@ package mutex
import (
"context"
"fmt"
"github.com/gorilla/mux"
"net/http"
"sync"
"github.com/gorilla/mux"
cmap "github.com/orcaman/concurrent-map"
log "github.com/sirupsen/logrus"
)
@ -18,14 +19,14 @@ func init() {
}
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf("Current number of locks: %d\nLocks: %+v\nUse /mutex/unlock endpoint to unlock all users", len(mutexMap.Keys()), mutexMap.Keys())))
w.Write([]byte(fmt.Sprintf("Current number of locks: %d\nLocks: %+v\nUse /mutex/unlock/{id} endpoint to mutex", len(mutexMap.Keys()), mutexMap.Keys())))
}
func UnlockHTTP(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if m, ok := mutexMap.Get(vars["id"]); ok {
m.(*sync.Mutex).Unlock()
w.Write([]byte(fmt.Sprintf("Unlocked %s mutexe.\nCurrent number of locks: %d\nLocks: %+v",
w.Write([]byte(fmt.Sprintf("Unlocked mutex %s.\nCurrent number of locks: %d\nLocks: %+v",
vars["id"], len(mutexMap.Keys()), mutexMap.Keys())))
return
}
@ -108,6 +109,6 @@ func Unlock(s string) {
log.Tracef("[Mutex] Unlocked %s", s)
} else {
// this should never happen. Mutex should have been in the mutexMap.
log.Errorf("[Mutex] ⚠⚠️ Unlock %s not in mutexMap. Skip.", s)
log.Errorf("[Mutex] ⚠️⚠️⚠️ Unlock %s not in mutexMap. Skip.", s)
}
}

18
main.go
View file

@ -1,11 +1,12 @@
package main
import (
"github.com/LightningTipBot/LightningTipBot/internal"
"github.com/LightningTipBot/LightningTipBot/internal/api"
"github.com/LightningTipBot/LightningTipBot/internal/api/admin"
"github.com/LightningTipBot/LightningTipBot/internal/lndhub"
"github.com/LightningTipBot/LightningTipBot/internal/lnurl"
"github.com/LightningTipBot/LightningTipBot/internal/runtime/mutex"
"github.com/gorilla/mux"
"net/http"
"runtime/debug"
@ -40,7 +41,7 @@ func startApiServer(bot *telegram.TipBot) {
// start internal webhook server
webhook.NewServer(bot)
// start external api server
s := api.NewServer()
s := api.NewServer(internal.Configuration.Bot.LNURLServerUrl.Host)
// append lnurl handler functions
lnUrl := lnurl.New(bot)
@ -53,11 +54,14 @@ func startApiServer(bot *telegram.TipBot) {
s.AppendRoute(`/lndhub/ext`, hub.Handle)
// start internal admin server
router := mux.NewRouter()
router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
router.Handle("/mutex", http.HandlerFunc(mutex.ServeHTTP))
router.Handle("/mutex/unlock/{id}", http.HandlerFunc(mutex.UnlockHTTP))
go http.ListenAndServe("0.0.0.0:6060", router)
adminService := admin.New(bot)
internalAdminServer := api.NewServer("0.0.0.0:6060")
internalAdminServer.AppendRoute("/mutex", mutex.ServeHTTP)
internalAdminServer.AppendRoute("/mutex/unlock/{id}", mutex.UnlockHTTP)
internalAdminServer.AppendRoute("/admin/ban/{id}", adminService.BanUser)
internalAdminServer.AppendRoute("/admin/unban/{id}", adminService.UnbanUser)
internalAdminServer.PathPrefix("/debug/pprof/", http.DefaultServeMux)
}
func withRecovery() {