Files
ALR/internal/config/config.go
2025-09-11 23:29:24 +03:00

266 lines
9.7 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.

// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/goccy/go-yaml"
"github.com/knadh/koanf/providers/confmap"
"github.com/knadh/koanf/v2"
ktoml "github.com/knadh/koanf/parsers/toml/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
)
type ALRConfig struct {
cfg *types.Config
paths *Paths
System *SystemConfig
env *EnvConfig
}
func New() *ALRConfig {
return &ALRConfig{
System: NewSystemConfig(),
env: NewEnvConfig(),
}
}
func defaultConfigKoanf() *koanf.Koanf {
k := koanf.New(".")
defaults := map[string]interface{}{
"rootCmd": "sudo",
"useRootCmd": true,
"pagerStyle": "native",
"ignorePkgUpdates": []string{},
"logLevel": "info",
"autoPull": true,
"updateSystemOnUpgrade": false,
"repos": []types.Repo{
{
Name: "alr-default",
URL: "https://gitea.plemya-x.ru/Plemya-x/alr-default.git",
},
},
}
if err := k.Load(confmap.Provider(defaults, "."), nil); err != nil {
panic(k)
}
return k
}
func (c *ALRConfig) Load() error {
config := types.Config{}
merged := koanf.New(".")
if err := c.System.Load(); err != nil {
return fmt.Errorf("failed to load system config: %w", err)
}
if err := c.env.Load(); err != nil {
return fmt.Errorf("failed to load env config: %w", err)
}
systemK := c.System.koanf()
envK := c.env.koanf()
if err := merged.Merge(defaultConfigKoanf()); err != nil {
return fmt.Errorf("failed to merge default config: %w", err)
}
if err := merged.Merge(systemK); err != nil {
return fmt.Errorf("failed to merge system config: %w", err)
}
if err := merged.Merge(envK); err != nil {
return fmt.Errorf("failed to merge env config: %w", err)
}
if err := merged.Unmarshal("", &config); err != nil {
return fmt.Errorf("failed to unmarshal merged config: %w", err)
}
c.cfg = &config
c.paths = &Paths{}
c.paths.UserConfigPath = constants.SystemConfigPath
c.paths.CacheDir = constants.SystemCachePath
c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
c.paths.PkgsDir = filepath.Join(constants.TempDir, "pkgs") // Перемещаем в /tmp/alr/pkgs
c.paths.DBPath = filepath.Join(c.paths.CacheDir, "alr.db")
// Проверяем существование кэш-директории, но не пытаемся создать
if _, err := os.Stat(c.paths.CacheDir); err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("failed to check cache directory: %w", err)
}
}
// Выполняем миграцию конфигурации при необходимости
if err := c.migrateConfig(); err != nil {
return fmt.Errorf("failed to migrate config: %w", err)
}
return nil
}
func (c *ALRConfig) ToYAML() (string, error) {
data, err := yaml.Marshal(c.cfg)
if err != nil {
return "", err
}
return string(data), nil
}
func (c *ALRConfig) migrateConfig() error {
// Проверяем, существует ли конфигурационный файл
if _, err := os.Stat(constants.SystemConfigPath); os.IsNotExist(err) {
// Если файла нет, создаем полный конфигурационный файл с дефолтными значениями
if err := c.createDefaultConfig(); err != nil {
// Если не удается создать конфиг, это не критично - продолжаем работу
// но выводим предупреждение
fmt.Fprintf(os.Stderr, "Предупреждение: не удалось создать конфигурационный файл %s: %v\n", constants.SystemConfigPath, err)
return nil
}
} else {
// Если файл существует, проверяем, есть ли в нем новая опция
if !c.System.k.Exists("updateSystemOnUpgrade") {
// Если опции нет, добавляем ее со значением по умолчанию
c.System.SetUpdateSystemOnUpgrade(false)
// Сохраняем обновленную конфигурацию
if err := c.System.Save(); err != nil {
// Если не удается сохранить - это не критично, продолжаем работу
return nil
}
}
}
return nil
}
func (c *ALRConfig) createDefaultConfig() error {
// Проверяем, запущен ли процесс от root
if os.Getuid() != 0 {
// Если не root, пытаемся запустить создание конфига с повышением привилегий
return c.createDefaultConfigWithPrivileges()
}
// Если уже root, создаем конфиг напрямую
return c.doCreateDefaultConfig()
}
func (c *ALRConfig) createDefaultConfigWithPrivileges() error {
// Если useRootCmd отключен, просто пытаемся создать без повышения привилегий
if !c.cfg.UseRootCmd {
return c.doCreateDefaultConfig()
}
// Определяем команду для повышения привилегий
rootCmd := c.cfg.RootCmd
if rootCmd == "" {
rootCmd = "sudo" // fallback
}
// Создаем временный файл с дефолтной конфигурацией
tmpFile, err := os.CreateTemp("", "alr-config-*.toml")
if err != nil {
return fmt.Errorf("не удалось создать временный файл: %w", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
// Генерируем дефолтную конфигурацию во временный файл
defaults := defaultConfigKoanf()
tempSystemConfig := &SystemConfig{k: defaults}
bytes, err := tempSystemConfig.k.Marshal(ktoml.Parser())
if err != nil {
return fmt.Errorf("не удалось сериализовать конфигурацию: %w", err)
}
if _, err := tmpFile.Write(bytes); err != nil {
return fmt.Errorf("не удалось записать во временный файл: %w", err)
}
tmpFile.Close()
// Используем команду повышения привилегий для создания директории и копирования файла
// Создаем директорию с правами
configDir := filepath.Dir(constants.SystemConfigPath)
mkdirCmd := exec.Command(rootCmd, "mkdir", "-p", configDir)
if err := mkdirCmd.Run(); err != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", configDir, err)
}
// Копируем файл в нужное место
cpCmd := exec.Command(rootCmd, "cp", tmpFile.Name(), constants.SystemConfigPath)
if err := cpCmd.Run(); err != nil {
return fmt.Errorf("не удалось скопировать конфигурацию в %s: %w", constants.SystemConfigPath, err)
}
// Устанавливаем правильные права доступа
chmodCmd := exec.Command(rootCmd, "chmod", "644", constants.SystemConfigPath)
if err := chmodCmd.Run(); err != nil {
// Не критично, продолжаем
fmt.Fprintf(os.Stderr, "Предупреждение: не удалось установить права доступа для %s: %v\n", constants.SystemConfigPath, err)
}
return nil
}
func (c *ALRConfig) doCreateDefaultConfig() error {
// Проверяем, существует ли директория для конфига
configDir := filepath.Dir(constants.SystemConfigPath)
if _, err := os.Stat(configDir); os.IsNotExist(err) {
// Пытаемся создать директорию
if err := os.MkdirAll(configDir, 0755); err != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", configDir, err)
}
}
// Загружаем дефолтную конфигурацию
defaults := defaultConfigKoanf()
// Копируем все дефолтные значения в системную конфигурацию
c.System.k = defaults
// Сохраняем конфигурацию в файл
if err := c.System.Save(); err != nil {
return fmt.Errorf("не удалось сохранить конфигурацию в %s: %w", constants.SystemConfigPath, err)
}
return nil
}
func (c *ALRConfig) RootCmd() string { return c.cfg.RootCmd }
func (c *ALRConfig) PagerStyle() string { return c.cfg.PagerStyle }
func (c *ALRConfig) AutoPull() bool { return c.cfg.AutoPull }
func (c *ALRConfig) Repos() []types.Repo { return c.cfg.Repos }
func (c *ALRConfig) SetRepos(repos []types.Repo) { c.System.SetRepos(repos) }
func (c *ALRConfig) IgnorePkgUpdates() []string { return c.cfg.IgnorePkgUpdates }
func (c *ALRConfig) LogLevel() string { return c.cfg.LogLevel }
func (c *ALRConfig) UseRootCmd() bool { return c.cfg.UseRootCmd }
func (c *ALRConfig) UpdateSystemOnUpgrade() bool { return c.cfg.UpdateSystemOnUpgrade }
func (c *ALRConfig) GetPaths() *Paths { return c.paths }