Replace env vars with toml config
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -48,10 +48,8 @@ Thumbs.db
|
|||||||
# Log files
|
# Log files
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Environment files
|
# Config files
|
||||||
.env
|
config.toml
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# Sample and test audio files
|
# Sample and test audio files
|
||||||
*.m4a
|
*.m4a
|
||||||
|
@@ -1,25 +1,40 @@
|
|||||||
|
# Server configuration
|
||||||
|
[server]
|
||||||
|
port = 8080
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
[database]
|
||||||
|
path = "data/transcriber.db"
|
||||||
|
|
||||||
# AWS S3 Configuration
|
# AWS S3 Configuration
|
||||||
|
[aws]
|
||||||
# Регион AWS (например: us-east-1, eu-west-1)
|
# Регион 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 (получить в 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 (получить в AWS Console)
|
||||||
AWS_SECRET_ACCESS_KEY=your_secret_access_key
|
secret_access_key = "your_secret_access_key"
|
||||||
|
|
||||||
# Имя S3 bucket для загрузки файлов
|
# Имя S3 bucket для загрузки файлов
|
||||||
S3_BUCKET_NAME=your_bucket_name
|
bucket_name = "your_bucket_name"
|
||||||
|
|
||||||
# Кастомный endpoint для S3 (оставить пустым для AWS S3, заполнить для MinIO или других S3-совместимых сервисов)
|
# Кастомный endpoint для S3 (оставить пустым для AWS S3, заполнить для MinIO или других S3-совместимых сервисов)
|
||||||
S3_ENDPOINT=
|
endpoint = ""
|
||||||
|
|
||||||
# Yandex Cloud Speech-to-Text Configuration
|
# Yandex Cloud Speech-to-Text Configuration
|
||||||
|
[yandex]
|
||||||
# API ключ для доступа к Yandex Cloud (получить в консоли Yandex Cloud)
|
# API ключ для доступа к Yandex Cloud (получить в консоли Yandex Cloud)
|
||||||
YANDEX_CLOUD_API_KEY=your_api_key_here
|
api_key = "your_api_key_here"
|
||||||
|
|
||||||
# ID папки в Yandex Cloud (получить в консоли Yandex Cloud)
|
# ID папки в Yandex Cloud (получить в консоли Yandex Cloud)
|
||||||
YANDEX_CLOUD_FOLDER_ID=your_folder_id_here
|
folder_id = "your_folder_id_here"
|
||||||
|
|
||||||
# Telegram Bot Configuration
|
# Telegram Bot Configuration
|
||||||
|
[telegram]
|
||||||
# Токен Telegram бота (получить у @BotFather в 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
1
go.mod
@@ -23,6 +23,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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/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/feature/ec2/imds v1.18.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 // indirect
|
||||||
|
2
go.sum
2
go.sum
@@ -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 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
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=
|
github.com/aws/aws-sdk-go-v2 v1.37.2 h1:xkW1iMYawzcmYFYEV0UCMxc8gSsjCGEhBXQkdQywVbo=
|
||||||
|
90
internal/config/config.go
Normal file
90
internal/config/config.go
Normal 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
48
main.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -16,6 +17,7 @@ import (
|
|||||||
ffmpegmv "git.vakhrushev.me/av/transcriber/internal/adapter/metaviewer/ffmpeg"
|
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/recognizer/yandex"
|
||||||
"git.vakhrushev.me/av/transcriber/internal/adapter/repo/sqlite"
|
"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"
|
httpcontroller "git.vakhrushev.me/av/transcriber/internal/controller/http"
|
||||||
tgcontroller "git.vakhrushev.me/av/transcriber/internal/controller/tg"
|
tgcontroller "git.vakhrushev.me/av/transcriber/internal/controller/tg"
|
||||||
"git.vakhrushev.me/av/transcriber/internal/controller/worker"
|
"git.vakhrushev.me/av/transcriber/internal/controller/worker"
|
||||||
@@ -31,9 +33,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TelegramUpdateTimeout = 10
|
|
||||||
ServerShutdownTimeout = 5
|
ServerShutdownTimeout = 5
|
||||||
|
|
||||||
ForceShutdownTimeout = 20
|
ForceShutdownTimeout = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,6 +44,22 @@ func main() {
|
|||||||
}))
|
}))
|
||||||
slog.SetDefault(logger)
|
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 файла
|
// Загружаем переменные окружения из .env файла
|
||||||
if err := godotenv.Load(); err != nil {
|
if err := godotenv.Load(); err != nil {
|
||||||
logger.Warn("Warning: .env file not found, using system environment variables")
|
logger.Warn("Warning: .env file not found, using system environment variables")
|
||||||
@@ -55,7 +71,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "data/transcriber.db")
|
db, err := sql.Open("sqlite3", cfg.Database.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to open database", "error", err)
|
logger.Error("failed to open database", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -84,13 +100,13 @@ func main() {
|
|||||||
converter := ffmpegconv.NewFfmpegConverter()
|
converter := ffmpegconv.NewFfmpegConverter()
|
||||||
|
|
||||||
recognizer, err := yandex.NewYandexAudioRecognizerService(yandex.YandexAudioRecognizerConfig{
|
recognizer, err := yandex.NewYandexAudioRecognizerService(yandex.YandexAudioRecognizerConfig{
|
||||||
Region: os.Getenv("AWS_REGION"),
|
Region: cfg.AWS.Region,
|
||||||
AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
|
AccessKey: cfg.AWS.AccessKey,
|
||||||
SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
SecretKey: cfg.AWS.SecretKey,
|
||||||
BucketName: os.Getenv("S3_BUCKET_NAME"),
|
BucketName: cfg.AWS.BucketName,
|
||||||
Endpoint: os.Getenv("S3_ENDPOINT"),
|
Endpoint: cfg.AWS.Endpoint,
|
||||||
ApiKey: os.Getenv("YANDEX_CLOUD_API_KEY"),
|
ApiKey: cfg.Yandex.APIKey,
|
||||||
FolderID: os.Getenv("YANDEX_CLOUD_FOLDER_ID"),
|
FolderID: cfg.Yandex.FolderID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create audio recognizer", "error", err)
|
logger.Error("failed to create audio recognizer", "error", err)
|
||||||
@@ -109,8 +125,8 @@ func main() {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
tgConfig := tgcontroller.TelegramConfig{
|
tgConfig := tgcontroller.TelegramConfig{
|
||||||
BotToken: os.Getenv("TELEGRAM_BOT_TOKEN"),
|
BotToken: cfg.Telegram.BotToken,
|
||||||
UpdateTimeout: TelegramUpdateTimeout,
|
UpdateTimeout: cfg.Telegram.UpdateTimeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем Telegram бот
|
// Создаем Telegram бот
|
||||||
@@ -182,7 +198,7 @@ func main() {
|
|||||||
|
|
||||||
// Создаем HTTP сервер
|
// Создаем HTTP сервер
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: ":8080",
|
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
|
||||||
Handler: router,
|
Handler: router,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +206,7 @@ func main() {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
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 {
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
logger.Error("HTTP server error", "error", err)
|
logger.Error("HTTP server error", "error", err)
|
||||||
}
|
}
|
||||||
@@ -214,7 +230,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Создаем контекст с таймаутом для graceful shutdown HTTP сервера
|
// Создаем контекст с таймаутом для 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()
|
defer shutdownCancel()
|
||||||
|
|
||||||
// Останавливаем HTTP сервер
|
// Останавливаем HTTP сервер
|
||||||
@@ -239,7 +255,7 @@ func main() {
|
|||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
logger.Info("All workers stopped gracefully")
|
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")
|
logger.Warn("Timeout reached, forcing shutdown")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user