ci / ts / ts (packages + web) (push) Canceled after 1m0s
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 56s
ci / db / db (push) Successful in 1m32s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m31s
Search hits omit album covers and artist images. The worker now reads the full song and artist, stores the description, and leaves Genius image URLs unchanged on mobile. Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
5.6 KiB
Go
95 lines
5.6 KiB
Go
// Package config читает конфигурацию сервиса из переменных окружения.
|
||
package config
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/caarlos0/env/v11"
|
||
)
|
||
|
||
// API — конфигурация публичного сервиса api.
|
||
type API struct {
|
||
HTTPAddr string `env:"HTTP_ADDR" envDefault:":8080"`
|
||
DatabaseURL string `env:"DATABASE_URL,required"`
|
||
RedisURL string `env:"REDIS_URL,required"`
|
||
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
|
||
MigrateOnStart bool `env:"MIGRATE_ON_START" envDefault:"true"`
|
||
ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"15s"`
|
||
|
||
SupportedLocales []string `env:"SUPPORTED_LOCALES" envDefault:"ru,en" envSeparator:","`
|
||
MinAndroidVersionCode int `env:"MIN_ANDROID_VERSION_CODE" envDefault:"0"`
|
||
|
||
// JWTPrivateKey — Ed25519 (base64: 32-байтовый seed или 64-байтовый ключ). Пусто — случайный ключ
|
||
// на время жизни процесса: после перезапуска всем придётся обновить access-токен (refresh остаётся).
|
||
JWTPrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||
// JWTPreviousKeys — прежние ключи через запятую: токены, подписанные ими, ещё принимаются.
|
||
JWTPreviousKeys []string `env:"JWT_PREVIOUS_KEYS" envSeparator:","`
|
||
AccessTokenTTL time.Duration `env:"ACCESS_TOKEN_TTL" envDefault:"15m"`
|
||
// CookieSecure — флаг Secure у refresh-cookie. Отключать только для разработки по http не на localhost.
|
||
CookieSecure bool `env:"COOKIE_SECURE" envDefault:"true"`
|
||
|
||
BootstrapAdminUsername string `env:"BOOTSTRAP_ADMIN_USERNAME" envDefault:"admin"`
|
||
BootstrapAdminPassword string `env:"BOOTSTRAP_ADMIN_PASSWORD"`
|
||
|
||
// LibraryRoot — точки монтирования NFS-хранилищ (общий каталог с storage-agent).
|
||
LibraryRoot string `env:"LIBRARY_ROOT" envDefault:"/library"`
|
||
// LibraryHostRoot — LIBRARY_HOST внутри контейнера: local-хранилища — его подпапки.
|
||
LibraryHostRoot string `env:"LIBRARY_HOST_ROOT" envDefault:"/library-host"`
|
||
StorageAgentURL string `env:"STORAGE_AGENT_URL" envDefault:"http://storage-agent:8090"`
|
||
InternalToken string `env:"INTERNAL_TOKEN"`
|
||
FFprobePath string `env:"FFPROBE_PATH" envDefault:"ffprobe"`
|
||
// FPCalcPath — fpcalc (Chromaprint) для акустических отпечатков; нет бинарника — считается только SHA-256.
|
||
FPCalcPath string `env:"FPCALC_PATH" envDefault:"fpcalc"`
|
||
|
||
// SigningKey — ключ подписи ссылок на обложки и стрим (base64, ≥ 16 байт). Пусто — случайный до перезапуска.
|
||
SigningKey string `env:"STREAM_SIGNING_KEY"`
|
||
CacheDir string `env:"CACHE_DIR" envDefault:"/cache"`
|
||
FFmpegPath string `env:"FFMPEG_PATH" envDefault:"ffmpeg"`
|
||
// XAccel — файлы отдаёт nginx по X-Accel-Redirect; false — для запуска api без nginx.
|
||
XAccel bool `env:"X_ACCEL" envDefault:"true"`
|
||
// TranscodeConcurrency — параллельных ffmpeg; 0 — половина ядер.
|
||
TranscodeConcurrency int `env:"TRANSCODE_CONCURRENCY" envDefault:"0"`
|
||
TranscodeCacheLimitGB int `env:"TRANSCODE_CACHE_LIMIT_GB" envDefault:"50"`
|
||
|
||
RecommenderURL string `env:"RECOMMENDER_URL" envDefault:"http://recommender:8000"`
|
||
GeniusAccessToken string `env:"GENIUS_ACCESS_TOKEN"`
|
||
GeniusRequestInterval time.Duration `env:"GENIUS_REQUEST_INTERVAL" envDefault:"2s"`
|
||
|
||
MeiliURL string `env:"MEILI_URL" envDefault:"http://meilisearch:7700"`
|
||
MeiliMasterKey string `env:"MEILI_MASTER_KEY"`
|
||
|
||
// Самообновление Android: сервер читает релизы Gitea своим токеном и кэширует APK в UpdatesDir.
|
||
GiteaURL string `env:"GITEA_URL"`
|
||
GiteaRepo string `env:"GITEA_REPO"`
|
||
GiteaToken string `env:"GITEA_TOKEN"`
|
||
UpdatesDir string `env:"UPDATES_DIR" envDefault:"/updates"`
|
||
// UploadsDir — staging очереди на добавление (локальный диск, общий со storage-agent на чтение).
|
||
UploadsDir string `env:"UPLOADS_DIR" envDefault:"/uploads"`
|
||
}
|
||
|
||
// StorageAgent — конфигурация внутреннего сервиса storage-agent.
|
||
type StorageAgent struct {
|
||
HTTPAddr string `env:"HTTP_ADDR" envDefault:":8090"`
|
||
InternalToken string `env:"INTERNAL_TOKEN,required,notEmpty"`
|
||
LibraryRoot string `env:"LIBRARY_ROOT" envDefault:"/library"`
|
||
// Запись импортированных загрузок: rw-точки NFS (только внутри агента), LIBRARY_HOST на запись, staging.
|
||
LibraryRWRoot string `env:"LIBRARY_RW_ROOT" envDefault:"/library-rw"`
|
||
HostRWRoot string `env:"LIBRARY_HOST_RW_ROOT" envDefault:"/library-host-rw"`
|
||
UploadsRoot string `env:"UPLOADS_ROOT" envDefault:"/uploads"`
|
||
// Владелец новых файлов на файловом сервере; -1 — оставить как есть.
|
||
FileUID int `env:"PIPU_FILE_UID" envDefault:"-1"`
|
||
FileGID int `env:"PIPU_FILE_GID" envDefault:"-1"`
|
||
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
|
||
ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"15s"`
|
||
}
|
||
|
||
// Load заполняет структуру конфигурации T из окружения.
|
||
func Load[T any]() (T, error) {
|
||
var cfg T
|
||
if err := env.Parse(&cfg); err != nil {
|
||
return cfg, fmt.Errorf("config: %w", err)
|
||
}
|
||
return cfg, nil
|
||
}
|