trashlog/app/validate.go
Pasha 9ff5fdb551
All checks were successful
Lint / Lint (push) Successful in 2m16s
upd after review
2024-12-09 12:46:14 +03:00

201 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package app
import (
"context"
"database/sql"
"errors"
"fmt"
"gitea.pena/PenaSide/trashlog/dal/bbolt"
_ "github.com/ClickHouse/clickhouse-go"
"github.com/caarlos0/env/v8"
bolt "go.etcd.io/bbolt"
tb "gopkg.in/tucnak/telebot.v2"
"log"
"os"
"path/filepath"
"strings"
"time"
)
func NewOptions() *Options {
opts := &Options{}
if err := env.Parse(opts); err != nil {
log.Fatalf("failed to parse environment variables: %v", err)
}
return opts
}
func (o *Options) Validate() error {
var err error
if o.ClickhouseTableMainName == "" {
err = errors.New("invalid value for DBClickHouseName, some value is empty")
return err
}
if err = o.validateAddr(); err != nil {
return err
}
if err = o.validateClickhouseCred(); err != nil {
return err
}
if err = o.validateDBBolt(); err != nil {
return err
}
if err = o.validateTgToken(); err != nil {
return err
}
return nil
}
func (o *Options) validateAddr() error {
if !strings.HasPrefix(o.GrpcURL, ":") {
return errors.New("GRPC_URL must start with ':'")
}
if o.AdminHttpURL == "" {
return errors.New("ADMIN_HTTP_URL not be empty")
}
return nil
}
func (o *Options) validateClickhouseCred() error {
var err error
if o.ClickhouseURL == "" {
err = errors.New("CLICKHOUSE_URL is empty")
return err
} else {
connect, openErr := sql.Open(
"clickhouse",
o.ClickhouseURL,
)
if openErr != nil {
err = fmt.Errorf("clickhouse connection error: %s", openErr.Error())
return err
}
if pingErr := connect.PingContext(context.TODO()); pingErr != nil {
err = fmt.Errorf("clickhouse ping error: %s", pingErr.Error())
return err
}
}
return nil
}
func (o *Options) validateDBBolt() error {
dir := filepath.Dir(o.DBPath)
tmpFile, err := os.CreateTemp(dir, ".tmp-validate")
if err != nil {
return fmt.Errorf("unable to create temporary file in directory '%s': %w", dir, err)
}
err = func(name string) error {
err := tmpFile.Close()
if err != nil {
return fmt.Errorf("unable to close temporary file in directory '%s': %v", dir, err)
}
err = os.Remove(name)
if err != nil {
return fmt.Errorf("error removing temporary file in directory '%s': %v", name, err)
}
return nil
}(tmpFile.Name())
exs, dbFileErr := exists(o.DBPath)
if dbFileErr != nil {
err = fmt.Errorf("error checking DBPath: %v", dbFileErr)
return err
} else if !exs {
// если файла базы данных нет то создаем
db, createFileErr := bolt.Open(o.DBPath, 0664, &bolt.Options{
Timeout: 5 * time.Second, // устанавливает максимальное время ожидания для доступа к базе данных
NoGrowSync: false, // не будет увеличивать размер файла синхронно при записи
FreelistType: bolt.FreelistArrayType, // для повышения производительности бд
})
if createFileErr != nil {
err = fmt.Errorf("err create file at path '%s': %v", o.DBPath, createFileErr)
return err
}
// проверяем что файл действительно появился
exs, dbFileErr = exists(o.DBPath)
if dbFileErr != nil {
err = fmt.Errorf("error checking DBPath after crreate bbolt db: %v", dbFileErr)
return err
}
// если нет, то возвращаем ошибку
if !exs {
err = errors.New("error checking DBPath after create bbolt db")
return err
}
// создаем бакет как элемент проверки того что писать и изменять мы можем файл
createBucketErr := db.Update(func(tx *bolt.Tx) error {
_, createBucketErr := tx.CreateBucketIfNotExists([]byte(bbolt.BucketName))
if createBucketErr != nil {
return createBucketErr
}
return nil
})
if createBucketErr != nil {
err = fmt.Errorf("error create bucket in bbolt db: %v", createBucketErr.Error())
return err
}
closeErr := db.Close()
if closeErr != nil {
err = fmt.Errorf("error closing DB connection: %v", closeErr)
return err
}
// удаляем после, чтобы главный процесс сам его создавал
removeFileErr := os.Remove(o.DBPath)
if removeFileErr != nil {
err = fmt.Errorf("err remove file at path '%s': %v", o.DBPath, removeFileErr)
return err
}
}
return nil
}
func (o *Options) validateTgToken() error {
var err error
if strings.TrimSpace(o.TelegramToken) == "" || o.TelegramChannelID == 0 {
err = errors.New("TELEGRAM_TOKEN or TELEGRAM_CHANNEL_ID empty")
return err
}
bot, newBotErr := tb.NewBot(tb.Settings{
Token: o.TelegramToken,
})
if newBotErr != nil {
return fmt.Errorf("failed create telegram bot: %w", newBotErr)
}
if _, sendErr := bot.Send(&tb.Chat{ID: int64(o.TelegramChannelID) * -1}, "PING", tb.ModeMarkdown); sendErr != nil {
err = errors.New(fmt.Sprintf("failed send telegram message: %v", sendErr.Error()))
return err
}
return nil
}
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}