Replace env vars with toml config

This commit is contained in:
2025-08-14 12:01:29 +03:00
parent 121585f807
commit 137da5a893
6 changed files with 151 additions and 29 deletions

6
.gitignore vendored
View File

@@ -48,10 +48,8 @@ Thumbs.db
# Log files
*.log
# Environment files
.env
.env.local
.env.*.local
# Config files
config.toml
# Sample and test audio files
*.m4a

View File

@@ -1,25 +1,40 @@
# Server configuration
[server]
port = 8080
# Database configuration
[database]
path = "data/transcriber.db"
# AWS S3 Configuration
[aws]
# Регион AWS (например: us-east-1, eu-west-1)
AWS_REGION=us-east-1
region = "us-east-1"
# AWS Access Key ID (получить в AWS Console)
AWS_ACCESS_KEY_ID=your_access_key_id
access_key_id = "your_access_key_id"
# AWS Secret Access Key (получить в AWS Console)
AWS_SECRET_ACCESS_KEY=your_secret_access_key
secret_access_key = "your_secret_access_key"
# Имя S3 bucket для загрузки файлов
S3_BUCKET_NAME=your_bucket_name
bucket_name = "your_bucket_name"
# Кастомный endpoint для S3 (оставить пустым для AWS S3, заполнить для MinIO или других S3-совместимых сервисов)
S3_ENDPOINT=
endpoint = ""
# Yandex Cloud Speech-to-Text Configuration
[yandex]
# API ключ для доступа к Yandex Cloud (получить в консоли Yandex Cloud)
YANDEX_CLOUD_API_KEY=your_api_key_here
api_key = "your_api_key_here"
# ID папки в Yandex Cloud (получить в консоли Yandex Cloud)
YANDEX_CLOUD_FOLDER_ID=your_folder_id_here
folder_id = "your_folder_id_here"
# Telegram Bot Configuration
[telegram]
# Токен Telegram бота (получить у @BotFather в Telegram)
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
bot_token = "your_telegram_bot_token_here"
# Таймаут обновлений Telegram бота (в секундах)
update_timeout = 10

1
go.mod
View File

@@ -23,6 +23,7 @@ require (
)
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 // indirect

2
go.sum
View File

@@ -1,3 +1,5 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/aws/aws-sdk-go-v2 v1.37.2 h1:xkW1iMYawzcmYFYEV0UCMxc8gSsjCGEhBXQkdQywVbo=

90
internal/config/config.go Normal file
View File

@@ -0,0 +1,90 @@
package config
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
)
type Config struct {
Server ServerConfig `toml:"server"`
Database DatabaseConfig `toml:"database"`
AWS AWSConfig `toml:"aws"`
Yandex YandexConfig `toml:"yandex"`
Telegram TelegramConfig `toml:"telegram"`
}
type ServerConfig struct {
Port int `toml:"port"`
ShutdownTimeout int `toml:"shutdown_timeout"`
ForceShutdownTimeout int `toml:"force_shutdown_timeout"`
}
type DatabaseConfig struct {
Path string `toml:"path"`
}
type AWSConfig struct {
Region string `toml:"region"`
AccessKey string `toml:"access_key_id"`
SecretKey string `toml:"secret_access_key"`
BucketName string `toml:"bucket_name"`
Endpoint string `toml:"endpoint"`
}
type YandexConfig struct {
APIKey string `toml:"api_key"`
FolderID string `toml:"folder_id"`
}
type TelegramConfig struct {
BotToken string `toml:"bot_token"`
UpdateTimeout int `toml:"update_timeout"`
}
// DefaultConfig returns a Config with default values
func DefaultConfig() *Config {
return &Config{
Server: ServerConfig{
Port: 8080,
ShutdownTimeout: 5,
ForceShutdownTimeout: 20,
},
Database: DatabaseConfig{
Path: "data/transcriber.db",
},
AWS: AWSConfig{
Region: "ru-central1",
AccessKey: "",
SecretKey: "",
BucketName: "",
Endpoint: "",
},
Yandex: YandexConfig{
APIKey: "",
FolderID: "",
},
Telegram: TelegramConfig{
BotToken: "",
UpdateTimeout: 10,
},
}
}
// LoadConfig loads configuration from a TOML file
func LoadConfig(path string) (*Config, error) {
// Check if file exists
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, fmt.Errorf("config file not found: %s", path)
}
config := DefaultConfig()
// Load configuration from file
if _, err := toml.DecodeFile(path, &config); err != nil {
return nil, fmt.Errorf("failed to decode config file: %w", err)
}
return config, nil
}

48
main.go
View File

@@ -3,6 +3,7 @@ package main
import (
"context"
"database/sql"
"flag"
"fmt"
"log/slog"
"net/http"
@@ -16,6 +17,7 @@ import (
ffmpegmv "git.vakhrushev.me/av/transcriber/internal/adapter/metaviewer/ffmpeg"
"git.vakhrushev.me/av/transcriber/internal/adapter/recognizer/yandex"
"git.vakhrushev.me/av/transcriber/internal/adapter/repo/sqlite"
"git.vakhrushev.me/av/transcriber/internal/config"
httpcontroller "git.vakhrushev.me/av/transcriber/internal/controller/http"
tgcontroller "git.vakhrushev.me/av/transcriber/internal/controller/tg"
"git.vakhrushev.me/av/transcriber/internal/controller/worker"
@@ -31,9 +33,7 @@ import (
)
const (
TelegramUpdateTimeout = 10
ServerShutdownTimeout = 5
ForceShutdownTimeout = 20
)
@@ -44,6 +44,22 @@ func main() {
}))
slog.SetDefault(logger)
// Parse command line flags
configPath := flag.String("c", "config.toml", "Path to config file")
flag.StringVar(configPath, "config", "config.toml", "Path to config file (alias for -c)")
flag.Parse()
// Load configuration
cfg, err := config.LoadConfig(*configPath)
if err != nil {
// If config file doesn't exist, use defaults
cfg = config.DefaultConfig()
// Log that we're using default config
logger.Info("Using default configuration", "config_path", *configPath, "error", err)
} else {
logger.Info("Configuration loaded successfully", "config_path", *configPath)
}
// Загружаем переменные окружения из .env файла
if err := godotenv.Load(); err != nil {
logger.Warn("Warning: .env file not found, using system environment variables")
@@ -55,7 +71,7 @@ func main() {
os.Exit(1)
}
db, err := sql.Open("sqlite3", "data/transcriber.db")
db, err := sql.Open("sqlite3", cfg.Database.Path)
if err != nil {
logger.Error("failed to open database", "error", err)
os.Exit(1)
@@ -84,13 +100,13 @@ func main() {
converter := ffmpegconv.NewFfmpegConverter()
recognizer, err := yandex.NewYandexAudioRecognizerService(yandex.YandexAudioRecognizerConfig{
Region: os.Getenv("AWS_REGION"),
AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
BucketName: os.Getenv("S3_BUCKET_NAME"),
Endpoint: os.Getenv("S3_ENDPOINT"),
ApiKey: os.Getenv("YANDEX_CLOUD_API_KEY"),
FolderID: os.Getenv("YANDEX_CLOUD_FOLDER_ID"),
Region: cfg.AWS.Region,
AccessKey: cfg.AWS.AccessKey,
SecretKey: cfg.AWS.SecretKey,
BucketName: cfg.AWS.BucketName,
Endpoint: cfg.AWS.Endpoint,
ApiKey: cfg.Yandex.APIKey,
FolderID: cfg.Yandex.FolderID,
})
if err != nil {
logger.Error("failed to create audio recognizer", "error", err)
@@ -109,8 +125,8 @@ func main() {
var wg sync.WaitGroup
tgConfig := tgcontroller.TelegramConfig{
BotToken: os.Getenv("TELEGRAM_BOT_TOKEN"),
UpdateTimeout: TelegramUpdateTimeout,
BotToken: cfg.Telegram.BotToken,
UpdateTimeout: cfg.Telegram.UpdateTimeout,
}
// Создаем Telegram бот
@@ -182,7 +198,7 @@ func main() {
// Создаем HTTP сервер
srv := &http.Server{
Addr: ":8080",
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
Handler: router,
}
@@ -190,7 +206,7 @@ func main() {
wg.Add(1)
go func() {
defer wg.Done()
logger.Info("Starting HTTP server", "port", 8080)
logger.Info("Starting HTTP server", "port", cfg.Server.Port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error("HTTP server error", "error", err)
}
@@ -214,7 +230,7 @@ func main() {
}
// Создаем контекст с таймаутом для graceful shutdown HTTP сервера
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), ServerShutdownTimeout*time.Second)
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Duration(cfg.Server.ShutdownTimeout)*time.Second)
defer shutdownCancel()
// Останавливаем HTTP сервер
@@ -239,7 +255,7 @@ func main() {
select {
case <-done:
logger.Info("All workers stopped gracefully")
case <-time.After(ForceShutdownTimeout * time.Second):
case <-time.After(time.Duration(cfg.Server.ForceShutdownTimeout) * time.Second):
logger.Warn("Timeout reached, forcing shutdown")
}