skeleton added
This commit is contained in:
42
gates/storage/admin.go
Normal file
42
gates/storage/admin.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"tgVideoCall/domain/admin/models"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
// Пытается получить админа по userID
|
||||
func (p *DB) GetAdmin(ctx context.Context, userID int) (models.Admin, error) {
|
||||
const op = "gates.postgres.GetAdmin"
|
||||
p.log.Debug(op, "Starting retrieving admin, for user: ", userID)
|
||||
|
||||
query := p.sq.Select("user_id", "role").
|
||||
From("admins").
|
||||
Where(sq.Eq{"user_id": userID})
|
||||
|
||||
qry, args, err := query.ToSql()
|
||||
p.log.Debug(op, "Building query: ", qry, "args: ", args)
|
||||
if err != nil {
|
||||
p.log.Error(op, "Error building query: ", err)
|
||||
return models.Admin{}, err
|
||||
}
|
||||
|
||||
var result models.Admin
|
||||
err = p.db.QueryRowxContext(ctx, qry, args...).StructScan(&result)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
p.log.Debug(op, "Admin does not exist, for user: ", userID)
|
||||
return models.Admin{}, models.ErrNotAdmin
|
||||
}
|
||||
if err != nil {
|
||||
p.log.Error(op, "Error retrieving admin: ", err)
|
||||
return models.Admin{}, err
|
||||
}
|
||||
|
||||
p.log.Debug(op, "Successfully retrieved admin for user: ", userID)
|
||||
return result, nil
|
||||
}
|
||||
20
gates/storage/migrations/1_admins.sql
Executable file
20
gates/storage/migrations/1_admins.sql
Executable file
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS admins(
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL UNIQUE,
|
||||
role VARCHAR(50) NOT NULL
|
||||
);
|
||||
|
||||
-- Добавляем первого админа
|
||||
INSERT INTO admins (user_id, role)
|
||||
SELECT '104186268', 'creator'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM admins WHERE user_id = '104186268'
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS admins;
|
||||
-- +goose StatementEnd
|
||||
20
gates/storage/migrations/1_init.sql
Executable file
20
gates/storage/migrations/1_init.sql
Executable file
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS admins(
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL UNIQUE,
|
||||
role VARCHAR(50) NOT NULL
|
||||
);
|
||||
|
||||
-- Добавляем первого админа
|
||||
INSERT INTO admins (user_id, role)
|
||||
SELECT '104186268', 'creator'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM admins WHERE user_id = '104186268'
|
||||
);
|
||||
-- +goose StatementEnd1_admins
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS admins;
|
||||
-- +goose StatementEnd
|
||||
30
gates/storage/storage.go
Normal file
30
gates/storage/storage.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/bool64/sqluct"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
db *sqlx.DB
|
||||
sq sq.StatementBuilderType
|
||||
sm sqluct.Mapper
|
||||
log slog.Logger
|
||||
}
|
||||
|
||||
func NewPostgresDB(db *sqlx.DB, log slog.Logger) *DB {
|
||||
dab := DB{
|
||||
db: db,
|
||||
sm: sqluct.Mapper{Dialect: sqluct.DialectPostgres},
|
||||
sq: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
|
||||
log: log,
|
||||
}
|
||||
_, err := dab.db.Exec("SET timezone TO 'UTC'")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &dab
|
||||
}
|
||||
1
gates/telegram/handlers.go
Normal file
1
gates/telegram/handlers.go
Normal file
@@ -0,0 +1 @@
|
||||
package telegram
|
||||
12
gates/telegram/interface.go
Normal file
12
gates/telegram/interface.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tgVideoCall/domain/admin/models"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
// Получает администратора из базы данных, возвращает ошибку если не является администратором
|
||||
GetAdmin(ctx context.Context, id int)(admin models.Admin, err error)
|
||||
}
|
||||
105
gates/telegram/telegram.go
Normal file
105
gates/telegram/telegram.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"tgVideoCall/domain/admin"
|
||||
"tgVideoCall/domain/admin/models"
|
||||
"tgVideoCall/pkg/config"
|
||||
|
||||
tele "gopkg.in/telebot.v3"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Service Service
|
||||
bot *tele.Bot
|
||||
cfg config.Config
|
||||
log slog.Logger
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewServer создает новый экземпляр сервера Telegram-бота без регистрации хендлеров
|
||||
func NewServer(ctx context.Context, log slog.Logger, cfg config.Config, Service admin.Service) *Server {
|
||||
return &Server{
|
||||
log: log,
|
||||
cfg: cfg,
|
||||
Service: Service,
|
||||
bot: initTelebot(cfg, log),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func initTelebot(cfg config.Config, log slog.Logger) *tele.Bot {
|
||||
const op = "gates.telegram.server.initTelebot"
|
||||
//регестрируем бота
|
||||
bot, err := tele.NewBot(tele.Settings{
|
||||
Token: cfg.APIKeys.Telegram,
|
||||
Synchronous: true,
|
||||
Verbose: false,
|
||||
OnError: func(err error, msg tele.Context) {
|
||||
if err != nil {
|
||||
log.Error(op, "failed to send message", err)
|
||||
}
|
||||
},
|
||||
Poller: &tele.LongPoller{Timeout: 10 * time.Second},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bot
|
||||
}
|
||||
|
||||
// RunServer регистрирует все публичные и приватные команды, а также middleware
|
||||
func (s *Server) RunServer() {
|
||||
// публичные команды
|
||||
//s.bot.Handle("/start", s.hello)
|
||||
//s.bot.Handle("/help", s.help)
|
||||
|
||||
// middleware
|
||||
adminGroup := s.bot.Group()
|
||||
adminGroup.Use(s.adminMiddleware)
|
||||
|
||||
// приватные команды
|
||||
|
||||
go func(ctx context.Context) {
|
||||
time.Sleep(1 * time.Second)
|
||||
<-ctx.Done()
|
||||
s.bot.Stop()
|
||||
}(s.ctx)
|
||||
s.bot.Start()
|
||||
}
|
||||
|
||||
func (s *Server) adminMiddleware(next tele.HandlerFunc) tele.HandlerFunc {
|
||||
return func(teleCtx tele.Context) error {
|
||||
const op = "gates.telegram.admin.adminMiddleware"
|
||||
s.log.Info(op, "trying to check admin status for user: ", teleCtx.Sender().ID)
|
||||
|
||||
userID := teleCtx.Sender().ID
|
||||
admin, err := s.Service.GetAdmin(s.ctx, int(userID))
|
||||
|
||||
if err == models.ErrNotAdmin {
|
||||
s.log.Debug(op, "user not found in db or not admin, user_id: ", userID)
|
||||
err = teleCtx.Reply("У вас нет прав администратора")
|
||||
if err != nil {
|
||||
s.log.Error(op, "error sending message", err, "to user: ", teleCtx.Sender().ID)
|
||||
}
|
||||
return nil // Возвращаем nil чтобы не показывать стектрейс
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.log.Error(op, "error getting admin", err)
|
||||
return teleCtx.Reply("Произошла внутренняя ошибка")
|
||||
}
|
||||
s.log.Debug(op, "admin found: ", admin, "role: ", admin.Role)
|
||||
if admin.Role != "admin" && admin.Role != "creator" {
|
||||
err = teleCtx.Reply("У вас нет прав администратора")
|
||||
if err != nil {
|
||||
s.log.Error(op, "error sending message", err, "to user: ", teleCtx.Sender().ID)
|
||||
}
|
||||
return nil // как при ErrNotAdmin — не показываем стектрейс при отказе в доступе
|
||||
}
|
||||
return next(teleCtx)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user